superkerni

  • 20. Jan 2022
  • Beitritt 9. Nov 2020
  • Danke Harald und hübsch gesund bleiben 🙂

  • 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();
         }
  • 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 ?

  • Thank you for your prompt reply .
    It's clear now. The shoot was in the PlayerAction script. I assigned a bullet script with the cloning to the spawn point.
    Spineboy now shoots bullets too.

  • Hallo,
    mein Problem mit der Shootind-direction ist zwar noch nicht gelöst, aber geradeaus kann der Player schießen.
    Also habe ich schon mal ein spawn eingerichtet und einen bullet Prefab definiert.

    clone = Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation);
                
    clone.AddForce(bulletSpawn.transform.right * bulletSpeed);

    Beim Ausführen wird auch ein Clone erstellt, aber der Player (Spineboy) selber, nicht der BulletPrefab. Die Clone werden bei jedem Click verdoppelt. Bei anderen Pbjekten außer Spines funktioniert das Schießen mit Bullets ?

    Wo könnte der Fehler liegen ?

  • 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; } }
  • 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 ??

  • Hallo Harald, Du hast hoffentlich Weihnachten gut überstanden.

    Zum Thema. Ich habe einige Analysen durchgeführt, aber man endet immer bei der folgenden Befehlsfolge, die nur vor dem ersten move seine Dienste tut.

    var mousePosition = Input.mousePosition;
             var worldMousePosition = camera.ScreenToWorldPoint(mousePosition);
             var skeletonSpacePoint = skeletonAnimation.transform.InverseTransformPoint(worldMousePosition);
             skeletonSpacePoint.x *= skeletonAnimation.Skeleton.ScaleX;
             skeletonSpacePoint.y *= skeletonAnimation.Skeleton.ScaleY;
             bone.SetLocalPosition(skeletonSpacePoint);

    Leider finde ich keine Anweisung, wie ich den Urzustand der Animation nach dem Move wieder herstellen kann. Das Bone-System wird nicht mehr mit der Gesamtrotation angesprochen. Gibt es Beispiele zur SkeletonRootMotion", wäre das eventuell der bessere Weg ?
    Oder liegt das auch nur am "life cicle" ?

    Danke Wolfgang

  • Vielen Dank

    Harald schrieb

    Welche Lösung von @vhristov? Mir fehlt jetzt leider der Kontext.

    Spineboy Object Oriented Sample and adding Prefab Bullet

    Ich sehe leider im Code oben nicht, wie das aim-target auf eine entsprechende touch-position gesetzt wird.

    I

    Die TouchPosition ist dieselbe wie die mousePosition habe ich ausführlich getestet.

    public class SpineboyTargetController : MonoBehaviour
       {
          public SkeletonAnimation skeletonAnimation;
          [SpineBone(dataField: "skeletonAnimation")]
          public string boneName;
          public new Camera camera;
          public Joystick joystick;
          Bone bone;
    
      void OnValidate()
      {
         if (skeletonAnimation == null) skeletonAnimation = GetComponent<SkeletonAnimation>();
      }
      void Start()
      {
         bone = skeletonAnimation.Skeleton.FindBone(boneName);
      }
      void Update()
      {
         var mousePosition = Input.mousePosition;
         var worldMousePosition = camera.ScreenToWorldPoint(mousePosition);
         var skeletonSpacePoint = skeletonAnimation.transform.InverseTransformPoint(worldMousePosition);
         skeletonSpacePoint.x *= skeletonAnimation.Skeleton.ScaleX;
         skeletonSpacePoint.y *= skeletonAnimation.Skeleton.ScaleY;
         bone.SetLocalPosition(skeletonSpacePoint);
      }
       }

    Im Update wird die Position gesetzt. Die Joystick-methode regelt einfach nur den "move" an sich. Die Bewegungen (bones) des Spineboys realisieren die Animationen.

  • Hallo Harald,
    in der ersten Phase möchte ich kein "bullet" hinzufügen. Habe zwar den Spawnpoint angelegt aber nicht aktiviert. Habe mir die Lösung von @vhristov angesehen. skeletonAnimation.Update(0) löst das Problem bei mir leider noch nicht. Mit BonFollower muß ich da eigentlich noch nicht arbeiten, da ich kein bullet habe.
    Der Spineboy schießt Feuer auch ohne Spawnelement und schießt auch zuerst in die Touchrichtung. Allerdings nach dem Joystick-Move nicht mehr, nur noch horizontal.
    Ich verstehe auch Deine prinzipiellen Hinweise auf die Doku "Life cicle", aber daß muß man erst einmal verdauen und die Stellschrauben erkennen.
    Muß ich nach jedem Joystickmove die Tracks initialisieren bzw. updaten ? Welche Parameter soll ich mir im Debug ansehen bzw. prüfen ob die Tracks noch nicht leer sind ?

    Grüße aus Mecklenburg Vorpommern

  • Hello
    i use for my private example for mobile your spineboy. With joystick the boy can run and jump. i shoot, the boons and the bullet follow the touch.
    After using the joystick the shooting is only horizontal , not in the touch direction. BeginnerInput for touching and the BeginnerView for shooting.

    public class SpineboyBeginnerInput

    public void OnValidate()
          {            
    if (skeletonAnimation == null) skeletonAnimation = GetComponent<SkeletonAnimation>(); if (model == null) model = GetComponent<SpineboyBeginnerModel>(); } #endregion private void Start() { rb = GetComponent<Rigidbody2D>(); //myScreenPos = Camera.main.WorldToScreenPoint(this.transform.position); } public void FixedUpdate() { rb.MovePosition(rb.position + move * speed * Time.fixedDeltaTime); } public void Update() { //Debug.Log("Target_update"); if (model == null) return; float currentHorizontal = Input.GetAxisRaw(horizontalAxis); currentHorizontal = joystick.Horizontal; model.TryMove(currentHorizontal); //if (Input.GetButton(attackButton)) if (Input.touchCount > 0) { //Debug.Log("Input: Touch - " + Input.touchCount); touch = Input.GetTouch(0); } else { return; } if (Input.touchCount < 0 || touch.phase != TouchPhase.Began) { return; } if (IsPointerOverGameObject()) { //Debug.Log("Input: Touch Over UI " + Input.touchCount); } else { model.TryShoot(); } if (Input.GetButtonDown(jumpButton)) model.TryJump(); } 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; } } }

    a part from public class SpineboyBeginnerView

    public void PlayShoot()
             {
             // 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();
            
         }
    • Bearbeitet
  • thank you very much, i will study and test the script !

  • Thanks Nate,
    i cant`found any shooting example in your Link.
    I search a example with bullet shooting. In time i use the skeleton-pro, this one has only fire on the weapon.
    With spawn-bullets in unity is it OK. But can i solve this problem (bullets) only with spine possibilities ?

    thanks Wolfgang

  • Hello,
    Can I create characters with shooting functions with spine essential ?
    And than using in Unity !
    Thanks
    Wolfgang

  • Danke Harald, schaun wir mal .....

    Merry Christmas

    Frohe Weihnachten für alle und gesund bleiben !!

  • Danke, muss ich erst einmal testen. Ist aber sehr spannend wenn in der Herstellung von SPINE-Charakteren soviel Spielraum ist, ohne das es beschrieben werden muss oder sollte. Als Anwender ist das natürlich sehr blöd. Habe jetzt erst einmal in meinem Testgame Euren Spineboy eingesetzt und das geht recht gut !! Darf ich diesen einbauen wenn ich mein Game nur privat verwende ? Wird garantiert nicht öffentlich gestellt !

  • Wechseln soll heißen:
    Walk und Idle passieren in Track 0, Shoot in Track 1, also wenn ich schieße wechsle ich den Track. Wenn ich von 1 komme laufen die Animationen von 0 nicht mehr richtig , z.B. statt Walk nur noch Zucken der Beine, später bleiben die Beine breitbeinig und bewegen sich gar nicht mehr.
    Der Hersteller sagt, er erstellt zwar die Grafik und die Animationen und weiß nicht was er falsch machen kann.
    Ich stehe jetzt dazwischen. Wie gesagt, ich teste das mit Eurem Script (Animation Tester) !
    Ich brauche irgendetwas für bzw. gegen den Hersteller, der Spineboy funktionier ja.


    Ich habe jetzt "Shoot" auch auf Track 0 gelegt und siehe es funktioniert auch. Ich habe aber die Empfehlung gelesen, Track 1 zu verwenden ?