Jump to content
Unity Insider Forum

2D PixelArt Animation


Ismoh

Recommended Posts

Plan war:

Puppet2D -> Bones -> Meshes verzerren/ändern -> Unitys Animator benutzen

 

Plan verworfen, weil man beim verzerren der Meshes keine 100%tigen Pixel hinbekommt.

 

Also wird jetzt über ein Spritesheet animiert.

Simpel n-Sprites nacheinander anzeigen.

Nur wie?

ForSchleife und Coroutine/IEnumerator?

Ich habe ein Sprite-Array und will das iterieren. Update Methode klappt nicht, da immer das letzte Sprite angezeigt wird, macht Sinn weil die ForSchleife durchgelaufen wird und dann erst gerendert.

 

Dann dachte ich an eine IEnumertor Methode gestartet über StartCoroutine(), aber irgendwie sagt mir mein Bauchgefühl, dass es nicht wirklich die richtige Lösung ist.

 

Kann mich jemand auf den richtigen Weg bringen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das mit der Coroutine erscheint mir doch ganz sinnvoll zu sein. Da kannst du wunderbar festlegen wann der nächste Frame angezeigt werden soll dank der Möglichkeit explizit einen gewisse zeitspanne bis zur Ausführung des nächsten Schrittes warten zu können.

 

http://docs.unity3d.com/ScriptReference/WaitForSeconds.html

Link zu diesem Kommentar
Auf anderen Seiten teilen

Okay cool :P

Ich mache relativ viel mit Coroutines, deswegen dachte ich, dass es nicht gerade der beste Weg ist, weil es so extrem simpel ist.

Aber wenn du das sagst, bleibe ich dann doch bei der ersten Überlegung.

Danke Mark.

 

PS:

Wo sind deine coolen Profilbilder hin?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo alle zusammen,

ich muss das Thema nochmal aufgreifen:

 

Die Animation - einfaches Sprite-Wechseln in einer IEnumerator Methode - des Charakters funktioniert.

Problem ist das Timing:

Die Taste → wird gedrückt. Es dauert n-Sekunden und die Walk Animation wird ausgeführt.

Ich vermute stark, dass der Tastendruck in der Update Methode sofort ausgewertet wird, sobald das Frame beginnt. Keinen Schimmer von welchen Zeiten wir sprechen, aber ich gehe von Millisekunden aus.

Die Coroutine kann sich in dem Moment jedoch in "new WaitForSeconds(n)" befinden.

 

Ich habe leider absolut keine Idee, wie ich das Problem lösen kann.

 

Weiß Jemand Rat?

 

Quellcode folgt.

 

//EDIT: Quellcode

 

 

public void Start()
   {
       rb2d = GetComponent<Rigidbody2D>();
       sR = GetComponent<SpriteRenderer>();
       currentAnimation = AnimationState.Idle;
       StartCoroutine(Animate());
   }

   void Update()
   {
       MovementTriggers();

       if (Input.GetKey(ActionKeyCodes.Instance.Shoot))
       {
           DoShoot();
       }        
   }

   void FixedUpdate() //RigidbodyMovement
   {
       if (isGrounded)
       {
           rb2d.velocity = velocity;
           velocity = new Vector2(0, 0);
       }
   }

   void MovementTriggers()
   {
       isGrounded = Physics2D.OverlapArea(checkGroundLeft.transform.position, checkGroundRight.transform.position, groundLayer);

       if (isGrounded)
       {
           if (Input.GetKey(ActionKeyCodes.Instance.MoveRight))
           {
               velocity.x = speed;
               currentAnimation = AnimationState.Walk;
           }

           if (Input.GetKey(ActionKeyCodes.Instance.MoveLeft))
           {
               velocity.x = -speed;
               currentAnimation = AnimationState.Walk;
           }

           if (Input.GetKey(ActionKeyCodes.Instance.Run) && (Input.GetKey(ActionKeyCodes.Instance.MoveLeft) || Input.GetKey(ActionKeyCodes.Instance.MoveRight)))
           {
               velocity.x *= 2;
               currentAnimation = AnimationState.Run;
           }

           if (Input.GetKey(ActionKeyCodes.Instance.JumpOrMoveUp))
           {
               velocity.y = speed * 2;
               currentAnimation = AnimationState.Jump;
               rb2d.AddForce(velocity, ForceMode2D.Force);
           }
       }
   }


   IEnumerator Animate()
   {
       while(this != null)
       {
           if (currentAnimation == AnimationState.Idle)//velocity.x == 0)//)
           {
               for(int i = 0; i < idleSprites.Length; i++)
               {
                   sR.sprite = idleSprites[i];
                   yield return new WaitForSeconds(0.5f);
               }
           }
           yield return null; //Kurze Pause in der Coroutine, da AnimationState geändert worden sein könnte

           if (currentAnimation == AnimationState.Walk)//velocity.x == Math.Abs(speed))//
           {
               for (int i = 0; i < walkSprites.Length; i++)
               {
                   sR.sprite = walkSprites[i];
                   yield return new WaitForSeconds(0.5f);
               }
           }
           yield return null; //Kurze Pause in der Coroutine, da AnimationState geändert worden sein könnte

           if (currentAnimation == AnimationState.Run) //velocity.x == (speed*2))//
           {
               for (int i = 0; i < runSprites.Length; i++)
               {
                   sR.sprite = runSprites[i];
                   yield return new WaitForSeconds(0.2f);
               }
           }
           yield return null; //Kurze Pause in der Coroutine, da AnimationState geändert worden sein könnte

           if (currentAnimation == AnimationState.Crouch)
           {
               for (int i = 0; i < crouchSprites.Length; i++)
               {
                   sR.sprite = crouchSprites[i];
                   yield return new WaitForSeconds(0.5f);
               }
           }
           yield return null; //Kurze Pause in der Coroutine, da AnimationState geändert worden sein könnte

           if (currentAnimation == AnimationState.Jump)
           {
               for (int i = 0; i < jumpSprites.Length; i++)
               {
                   sR.sprite = jumpSprites[i];
                   yield return new WaitForSeconds(0.5f);
               }
           }
           yield return null; //Kurze Pause in der Coroutine, da AnimationState geändert worden sein könnte

           if (currentAnimation == AnimationState.Die)
           {
               for (int i = 0; i < dieSprites.Length; i++)
               {
                   sR.sprite = dieSprites[i];
                   yield return new WaitForSeconds(0.5f);
               }
           }
           yield return null; //Kurze Pause in der Coroutine, da AnimationState geändert worden sein könnte
       }
       yield break;
   }

 

 

 

Ich hatte soeben mal versucht, die Coroutine zu stoppen und anschließend zu starten.

Da dieser Aufruf jedoch über die Update läuft, zappelt der gute Herr einfach spaßeshalber rum.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Da du ja scheinbar alles auf Konfiguration (Eingabegeräte und Aussehen soll voll Änderbar sein) ausgelegt hast,

nutzt du ja nicht die tollen Möglichkeiten wie z.B. den Animator. Da hättest du es viel einfacher.

 

Nun denn. Ich würde den Spritewechsel NICHT in einer Coroutine machen, da du eben genau dieses Umschaltproblem bekommst.

Mach es in der Update oder eine void, die du in der Update aufrufst.

Natürlich kannst du jetzt kein WaitForSeconds nutzen, aber das geht ja auch mit der Time.time!

 

// Beispiel für eine Walk-Animation

float walkIntervall =0.5f;
float walkStartTime =0f;
int walkFrame =-1;
string oldState ="idle";  // wird gebraucht um bei einer neuen Animation immer mit dem ersten Frame zu beginnen
void Update(){
//... deine Abfragen und andere Dinge
  if(currentAnimation == AnimationState.Walk)
  Walk();
}
void Walk(){ // hier wird rein gesprungen, wenn der Walkstate ansteht
  if(oldState != "walk"){ // vorher wurde eine andere Animation ausgeführt? Wenn ja dann beginne die Animation von vorne
  oldState = "walk"; // state setzen
  walkFrame=-1; // Animation von vorne beginnen
  }
  if(walkStartTime+walkIntervall < Time.time){ //wird nur ausgeführt, wenn die Zeit abgelaufen ist
  walkStartTime = Time.time;
  if(walkFrame< walkSprites.Length){
	 walkFrame++;
  }else{
	 walkFrame=0;
  }
  sr.sprite=walkSprite[walkFrame];
  }
}

 

Den Code habe ich jetzt so ausm Kopf rein geballert. Könnten Fehler drin sein.

Es sollte aber zur Veranschaulichung reichen.

Und jetzt komm mir nicht damit, dass es zuviel Code sei! :D

Link zu diesem Kommentar
Auf anderen Seiten teilen

Da du ja scheinbar alles auf Konfiguration (Eingabegeräte und Aussehen soll voll Änderbar sein) ausgelegt hast,

nutzt du ja nicht die tollen Möglichkeiten wie z.B. den Animator. Da hättest du es viel einfacher.

Verstehe ich nicht. Leichter Unterton vorhanden? :D

Ich versuche lediglich einige Lösungsansätze zu suchen und Coroutine kam mir zu dem Zeitpunkt in den Kopf.

 

In meinem jugendlichen Leichtsinn dachte ich, dass die Animation über Quellcode einfacher ist, was aber soeben widerlegt wurde.

 

Ich setze mich dann mal an den Animator.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ne, so arg war das nicht gemeint. :)

Ich weiß einfach nicht genau, was du erreichen willst. Und wenn ich so sehe, dass du die einzelnen Sprites duchschaltest, anstatt eine Animation damit zu erzeugen und diese dann im Animator zu steuern, gehe ich eben davon aus, dass du das alles konfigurierbar machen willst und deswegen auf Animationsspuren und den Animator verzichtest.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Achso.

Nein.

Bin einfach nur noch der absolute Unity Anfänger.

Ich habe nun die Animationen über "Animation" erstellt und an den Animator weitergeleitet, bzw das hat UNity übernommen. Funktioniert alles wunderbar.

Folgende Frage hat sich aber ergeben:

Beim Anfang eines Sprungs soll ein Sprite angezeigt werden.

Beim Landen des Sprungs soll aber ein anderes Sprite angezeigt werden.

Also hat die Jump-Animation zwei Sprites.

Ein Sprite beim Frame 0 und das andere soll nun wohin? :D

Problem ist, dass ich irgendwie die Höhe abfragen muss.

Sollte ich in dem Fall evtl nicht den Animator nutzen?

Oder doch den Animator und dann über einen Parameter "float height"?

Ich werkel mal ein wenig.

Mal sehen was bei raus kommt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn das so ist, dann mach dich mit dem Animator vertraut.

Erzeuge Animationsspuren für die einzelnen Gegebenheiten und steuere die dann über Transitions im Animator.

Diese Transitions reagieren ja auf Variablen, die vom Code her gesetzt werden.

Beim Sprung wäre es dann so, dass du z.B. 3 Animationen hintereinander abspielen würdest.

Eine fur die Startphase, die immer abgespielt wird. Eine für die Flugphase (was auch nur ein Frame an Animationsdauer sein kann), die dann direkt nach der Startphase geloopt wird. Und eine fürs Landen. Ab wann gelandet wird, ermittelst du mit nem Raycast nach unten und sobald du eine gewisse Höhe erreicht hast, wird diese Landeanimation gestartet (egal wieviele Frames das wirklich sind. Wenn du nur ein Sprite dafür hast, dann ist das halt nur dieses eine Sprite).

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...