• Runtimes
  • Unity Joystick and Spineboy

Harald is noch bis zum 10.1 im wohlverdienten Relaxmodus. Ich hab mir euren Verlauf hier angeschaut und versucht zu verstehen, wo ded Fehler liegen könnte. Leider fällt mir dazu auch nichts ein. Die Update() Funktion ist jedenfalls korrekt, so Input.mousePosition korrekt ist.

Zum Zurücksetzen reicht es eigentlich, den aktuellen AnimationState zu updaten und aufs Skeleton anzuwenden. Beides sollte eigentlich in jedem frame von SkeletonAnimation ausgeführt werden. Ausser deine Tracks sind auf additive animation blending gestellt, was ich aber hier nicht sehe.

Related Discussions
...

Danke, werde noch etwas basteln ....
Input.mousepostion ist identisch mit dem Touch.
Wenn ich model.trymove () lahm lege funktioniert es, also hat die Animation konkret damit zu tun ??

Hm, die tryMove() Methode setzt nur den state soweit ich sehe:
https://github.com/EsotericSoftware/spine-runtimes/blob/4.0/spine-unity/Assets/Spine%20Examples/Scripts/Getting%20Started%20Scripts/SpineboyBeginnerModel.cs#L84

Irgend ein Code muss den state auswerten und entsprechend Animationen setzen, die dann die manuelle gesetzte Position des Knochens, der das IK Target ist, irgendwo überschreibt. Wo kann ich aber leider auf Basis des Codes den ich sehen kann nicht sagen.

Hier das Script mit State-Auswertung in "PlayerAction" (unter Verwendung SpineBeginnerView), mein Hauptscript des Players (Leben-Counter und Collisions habe ich hier weggelassen), SpineBeginnerInput und SpineBeginnerModel liegen auch auf dem Player.

void Start()
         {
            bulletPrefab = GetComponent<Rigidbody2D>();
            if (skeletonAnimation == null) return;
            model.ShootEvent += PlayShoot;
            model.StartAimEvent += StartPlayingAim;
            model.StopAimEvent += StopPlayingAim;
            skeletonAnimation.AnimationState.Event += HandleEvent;
            isJumping = false;
            inputESC = false;
            bone = skeletonAnimation.Skeleton.FindBone(boneName);
         }
         void HandleEvent(Spine.TrackEntry trackEntry, Spine.Event e)
         {
            if (e.Data == footstepEvent.EventData)
               PlayFootstepSound();
         }
   public void FixedUpdate()
   {
      rb.MovePosition(rb.position + move * speed * Time.fixedDeltaTime);
   }
   public void Update()
   {
         if (skeletonAnimation == null) return;
         if (model == null) return;

     move.x = joystick.Horizontal;
     move.y = joystick.Vertical * jumpspeed;

     if (Input.GetKeyDown(KeyCode.Escape))
        {
           #if UNITY_EDITOR
              UnityEditor.EditorApplication.isPlaying = false;
           #else
              Application.Quit();
           #endif
        }
        
         if ((skeletonAnimation.skeleton.ScaleX < 0) != model.facingLeft)
        {   // Detect changes in model.facingLeft
           Turn(model.facingLeft);
        }
  var currentModelState = model.state;
  if (previousViewState != currentModelState)
        {
           PlayNewStableAnimation();
        }
     previousViewState = currentModelState;
     isJumping = false;
     
  } // Update Ende

  
  void PlayNewStableAnimation()
  {
        var newModelState = model.state;
        //Animation nextAnimation;

     // Add conditionals to not interrupt transient animations.
        if (previousViewState == SpineBeginnerBodyState.Jumping && newModelState != SpineBeginnerBodyState.Jumping)
        {
           PlayFootstepSound();
        }
        if (newModelState == SpineBeginnerBodyState.Jumping)
        {
           jumpSource.Play();
               skeletonAnimation.AnimationState.SetAnimation(0, jump, true);
        }
        else
        {
           if (newModelState == SpineBeginnerBodyState.Running)
           {
              //nextAnimation = run;
              skeletonAnimation.AnimationState.SetAnimation(0, run, true);
           }
           else
           {
              //nextAnimation = idle;
              skeletonAnimation.AnimationState.SetAnimation(0, idle, true);
           }
        }
  }
  void PlayFootstepSound()
  {
        footstepSource.Play();
        footstepSource.pitch = GetRandomPitch(footstepPitchOffset);
  }

     [ContextMenu("Check Tracks")]
  void CheckTracks()
     {
        var state = skeletonAnimation.AnimationState;
        //Debug.Log(state.GetCurrent(0));
        //Debug.Log(state.GetCurrent(1));
  }

     #region Transient Actions
  public void PlayShoot()
     {
     // Play the shoot animation on track 1.
     
     var shootTrack = skeletonAnimation.AnimationState.SetAnimation(1, shoot, false);
        shootTrack.AttachmentThreshold = 1f;
        shootTrack.MixDuration = 0f;
        var empty1 = skeletonAnimation.state.AddEmptyAnimation(1, 0.5f, 0.1f);
        empty1.AttachmentThreshold = 1f;

     // Update skeleton to apply animations
        skeletonAnimation.Update(0);

        boneFollower = GetComponent<BoneFollower>();

     // Play the aim animation on track 2 to aim at the mouse target.
     var aimTrack = skeletonAnimation.AnimationState.SetAnimation(2, aim, false);
        aimTrack.AttachmentThreshold = 1f;
        aimTrack.MixDuration = 0f;
        var empty2 = skeletonAnimation.state.AddEmptyAnimation(2, 0.5f, 0.1f);
        empty2.AttachmentThreshold = 1f;

        gunSource.pitch = GetRandomPitch(gunsoundPitchOffset);
        gunSource.Play();
        //gunParticles.randomSeed = (uint)Random.Range(0, 100);
        gunParticles.Play();
        
        // Attack();
  }
  
  public class QuitOnClick : MonoBehaviour
  {      public void Quit()
     {
        //Spiel beenden
        //Debug.Log("Spiel beendet");

     #if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
     #else
           Application.Quit();
     #endif

     }
  }
  public void StartPlayingAim()
     {
        // Play the aim animation on track 2 to aim at the mouse target.
        var aimTrack = skeletonAnimation.AnimationState.SetAnimation(2, aim, true);
        aimTrack.AttachmentThreshold = 1f;
        aimTrack.MixDuration = 0f;
  }
  public void StopPlayingAim()
     {
        var empty2 = skeletonAnimation.state.AddEmptyAnimation(2, 0.5f, 0.1f);
        empty2.AttachmentThreshold = 1f;
  }
  public void Turn(bool facingLeft)
     {
        skeletonAnimation.Skeleton.ScaleX = facingLeft ? -1f : 1f;
        // Maybe play a transient turning animation too, then call ChangeStableAnimation.
  }
     #endregion
     #region Utility
  public float GetRandomPitch(float maxPitchOffset)
     {
        return 1f + Random.Range(-maxPitchOffset, maxPitchOffset);
     }
     #endregion
  }

"SpineboyBeginerInput"

public void OnValidate()
      {            
if (skeletonAnimation == null) skeletonAnimation = GetComponent<SkeletonAnimation>(); if (model == null) model = GetComponent<SpineboyBeginnerModel>(); } #endregion private void Start() { rb = GetComponent<Rigidbody2D>(); bone = skeletonAnimation.Skeleton.FindBone(boneName); } public void FixedUpdate() { //rb.MovePosition(rb.position + move * speed * Time.fixedDeltaTime); } public void Update() { if (model == null) return; currentHorizontal = joystick.Horizontal; model.TryMove(currentHorizontal); mousePosition = Input.mousePosition; //Touch touch = Input.GetTouch(0); if (Input.touchCount > 0) { //Debug.Log("Input: Touch - " + touch.position); touch = Input.GetTouch(0); dz++; PlayerPrefs.SetInt("Touches", dz); } else { return; } if (Input.touchCount < 0 || touch.phase != TouchPhase.Began) { return; } if (IsPointerOverGameObject()) { // keine shooting } else { mousePosition = Input.mousePosition; worldMousePosition = camera.ScreenToWorldPoint(mousePosition); var skeletonSpacePoint = skeletonAnimation.transform.InverseTransformPoint(worldMousePosition); skeletonSpacePoint.x *= skeletonAnimation.Skeleton.ScaleX; skeletonSpacePoint.y *= skeletonAnimation.Skeleton.ScaleY; bone.SetLocalPosition(skeletonSpacePoint); model.TryShoot(); } } // regelt den Touch über dem Joystick public bool IsPointerOverGameObject() { if (EventSystem.current.IsPointerOverGameObject()) { return true; } // Check touches for (int i = 0; i < Input.touchCount; i++) { var touch = Input.GetTouch(i); if (touch.phase == TouchPhase.Began) { if (EventSystem.current.IsPointerOverGameObject(touch.fingerId)) { return true; } } } return false; } }

Da spielt sich ganz schön was ab 🙂 Ohne das ausführen zu können tu ich mir schwer das auf Sicht zu debuggen. Ich habe aber eine Vermutung.

Du hast StartPlayingAim() und StopPlayingAim(). Die manipulieren Track 2 des AnimationState und sind dafür verantwortlich, dass die Gun dem Cursor folgt (oder eben nicht mehr). In StartPlayingAim() setzt du die aim Animation auf looping, d.h. Spineboy aimed bis die Animation vom Track gelöscht wird. Soweit so gut.

Aber in PlayShoot() manipulierst du Track 2 ebenfalls mit:

var aimTrack = skeletonAnimation.AnimationState.SetAnimation(2, aim, false);

Hier wird die Animation nur einmal abgespielt ohne looping. Ist die Animation beendet, zielt Spineboy nicht mehr.

Ich vermute PlayShoot() und StartPlayingAim() kommen sich in die Quere. PlayShoot() "gewinnt" dabei irgendwann, und Spineboy zielt daher nicht mehr auf den Cursor. Warum genau, kann ich wie gesagt in Ermangelung von Debug Möglichkeiten leider nicht sagen.

7 дней спустя

Danke,
ich werde mal die Stati debuggen ... so habe in alle Richtungen getestet, sogar kurzfristig Joystick auf Tastatur umgestellt. Der Shoot findet nach wie vor nur beim ersten Shooting ,inkl. der Armanimationen, das Cursor-Ziel. Nach einer Bewegung ist die Zielerfassung weg.
Intderessant war. Es erscheint beim Shooting ein kleines rotes Fadenkreuz. Beim Start ist es dort wo die Mouse ist, aber nach einem Move ist es immer kurz vor der gun-Mündung (auf dem Bildschirm ca. 3cm) und bleibt dort.

Welche Anweisung stellen den Basiszustand für die Animationen und Bones her ?

Habt Ihr ein Beispiel, wo das Zielschießen mit Spine-Character immer funktioniert ?

Guten Rutsch Wolfgang


Hat keiner eine Idee ?

Soeey, ich muss deine Antwoet übersehen haben! SetToSetupPose() stellt den Basiszustand her. Die kannst du im Event SkeletonAnimation.BeforeApply aneenden. Aber die sollte absolut nicht notwendig sein.

ich kann leider ohne ein Demoprojekt nicht sagen, was da schief läuft. Über die Examples im spine-runtimes repository hinaus haben wir nix. Du hast dein Projekt ja darauf aufgesetzt. Drum wäre es einfacher für uns, wenn wir deine Modifikationen im Ganzen, lokal sehen koennen.

Habe jetzt beim BoneFollower (auf bulletSpawn) als Bone "gun-tip" ausgewählt. Die Bullets starten jetzt richtig von der Pistole und geht in Touchrichtung, aber der Spineboy reagiert nicht !

Problem mit der Animation für die shoot direction gelöst: Vor den Animationen die Tracks bereinigt !! 😃
skeletonAnimation.AnimationState.ClearTracks();

public void PlayShoot()
         {
            skeletonAnimation.AnimationState.ClearTracks();   

     // Play the shoot animation on track 1.
        var shootTrack = skeletonAnimation.AnimationState.SetAnimation(1, shoot, false);
        //Debug.Log("PlayShoot: Shooting");
        shootTrack.AttachmentThreshold = 1f;
        shootTrack.MixDuration = 0f;
        var empty1 = skeletonAnimation.state.AddEmptyAnimation(1, 0.5f, 0.1f);
        empty1.AttachmentThreshold = 1f;

     // Play the aim animation on track 2 to aim at the mouse target.
        var aimTrack = skeletonAnimation.AnimationState.SetAnimation(2, aim, false);
        aimTrack.AttachmentThreshold = 1f;
        aimTrack.MixDuration = 0f;
        var empty2 = skeletonAnimation.state.AddEmptyAnimation(2, 0.5f, 0.1f);
        empty2.AttachmentThreshold = 1f;

        gunSource.pitch = GetRandomPitch(gunsoundPitchOffset);
        gunSource.Play();
        //gunParticles.randomSeed = (uint)Random.Range(0, 100);
        gunParticles.Play();
           
        Attack();
     }

Ohhhhh. 🙂

Spät aber doch nun auch von mir ein gutes neues Jahr! 🙂 Freut mich sehr, dass Du das Problem beheben konntest, Wolfgang!

Danke Harald und hübsch gesund bleiben 🙂