# Animation startet bei gedrückter Taste immer wieder von vorn



## baddestpoet (3. Mai 2010)

Ich dabei ein kleines Jump and Run zu erstellen und versuche gerade Animationen für den Helden einzbinden. Im Prinzip funktioniert es aber wenn ich eine Richtungstaste gedrück halte um die Figur zu bewegen, startet die Animation ständig neu, man sieht also nur leichtes Flackern der ersten beiden Bilder statt der ganzen Bildfolge.

So sieht es bei mir aus wenn eine Bewegung ausgeführt und die Animation gesetzt werden soll:
setLoop überliefert dabei das Start- und Endbild, left und right sind true wenn die jeweilige Taste gedrückt und false wenn sie losgelassen wird, blickl gibt an ob der Held nach links guckt und setDx übergibt die Geschwindigkeit


```
if(left && !right) { //nach links
            setDx(-250);
            blickl = true;
            setLoop(0,3);
        }
```

Ich hab schon versucht ein flag einzusetzen aber das klappt auch nicht wirklich wenn mehr als nur die Bewegungen nach links und rechts benutzt werden :-/ Hat vielleicht jemand ne Idee, wie man das umgehen kann?


----------



## Steev (3. Mai 2010)

Das ist kein Wunder, dass die Animation nicht korrekt funktioniert. Bei jedem Tastenereignis wird nämlich der loop neu gestartet und daher nicht korrekt abgespielt.
Ich würde das ganze mithilfe einer Integer-Variable machen, die ich so lange hochzähle, bis die maximale Anzahl der Bilder in der Szene erreicht ist. Auserdem würde ich die Animation nicht über ein Tastenereignis sondern über einen eigenen Animationsthread oder zur not im Renderthread durchführen.
Bei einem Tastendruck wird dann einfach nur das "goLeft"-Flag gesetzt und die Animation reagiert entsprechend darauf.

Gruß
Steev


----------



## baddestpoet (3. Mai 2010)

Dass es daran liegt, war mir schon klar 
Wie das funktionieren würde allerdings noch nicht ganz. Die von dir vorgeschlagene Variable würde ich ja in der Methode der Animation zählen lassen, die vergleicht ob alle Bilder durch sind und neu angefangen werden muss. Wenn ich die Animation dann setzen möchte (zB beim nach-links-gehen)würde ich diese Variable dann testen und die neue Animation nur starten, wenn die akutelle zu Ende gelaufen ist!? Dabei würde es doch ständig Konflikte geben, wenn der Charakter sich umdreht sieht man das ja dann erst wenn eine Animation zu Ende gelaufen ist? :autsch:

Wie ich meine ganze Animation in einen eigenen thread packen kann, weiß ich aber auch noch nicht, mit Threads hab ich bisher nur marginal zu tun gehabt. Im Allgemeinen hab ich mich was die Animation angeht grob an das Tutorial von Quaxli gehalten. Also ich hab ne Klasse Sprite, die den Bildwechsel abhängig von der Zeit regelt, von der ich meine Heldenklasse erben lass. Den keyListener hab ich in der Main drin, die übergibt die Richtungsflags einfach an den Helden, der dann neben der Bewegung auch die Animation ändert, das schien mir am praktischsten, da ich abhängig davon ob ich springe oder mich schnell bewege jeweils unterschiedliche Animationen benutze und die eben nicht nur davon abhängen, ob ich nach links drücke.


----------



## Steev (3. Mai 2010)

Wenn du sowieso schon ein flag hast, dann kannst du dieses doch Abfragen. Den Konflikt wird du nicht bekommen, wenn du die Animationen "umschaltest". Dein Bildzeiger wird dann je nach flag initialisiert und fortgezählt. So würde ich das machen. Das bedeutet, dass du nur einen "Bildzeiger" (Integer-Variable mit der Nummer des Bildes) verwendest und diesen je nach Status entsprechend behandelst. Eigendlich ändert sich pro Szene ja nur die Anzahl der Bilder und der verwendete Bild-Array (oder Collection,..), also musst du beim loopen der Bildvariable nur darauf achten, dass die aktuelle Anzahl der Szenen nicht überschritten wird. Desweiteren musst du beim rendern darauf achten, dass auch die Bilder der richtigen Szene gerendert werden. Daher würde ich ebenso wie bei den Bildern auch bei den Szenen einen "Zeiger" verwenden, den man dann je nach Status umsetzt. Denkbar währe sogar für die Character-Animation ein zweidimensionaler Array, wobei die erste die Szene und die zweite Dimension die Bilder darstellt. Wobei ich persönlich eine Klasse pro Szene bevorzugen würde.

[Java]public class Hero {
  public static final int SCENE_WALK_LEFT = 0;
  public static final int SCENE_WALK_RIGHT = 1;
  public static final int SCENE_JUMP = 3;
  public static final int SCENE_DIE = 4;

  private int currScene = 0;
  private int currFrame = 0;
  private Image[][] scenes = null;
  private boolean play = true;
  private boolean loop = true;

  // ...

  /**
   * Diese Methode wird in regelmäßigen abständen (z.B. vom Renderthread) aufgerufen.
   */
  public void doLogic() {
    if (play) {
      if (currFrame + 1 < scenes[currScene].length)
        currFrame++;
      else if (loop)
        currFrame = 0;
    }
  }
}[/Java]

Gruß
Steev


----------



## Quaxli (4. Mai 2010)

baddestpoet hat gesagt.:


> Im Allgemeinen hab ich mich was die Animation angeht grob an das Tutorial von Quaxli gehalten.



Aber wirklich nur sehr grob. 
Lies das mal genauer, da läuft das Ganze auch über einen Thread - in etwa so, wie von Steev skizziert.
Zwar verwende ich boolean-Werte anstelle der Integer aus dem Beispiel von Steev, aber das ist Geschmackssache und vom Prinzip her in etwa das gleiche Konzept.


----------



## baddestpoet (6. Mai 2010)

Also wenn ich mir das so anguck müsste ich das auch genau so gemacht haben:


```
public void doLogic(long delta) {
        animation += (delta/1000000);
        if(animation > delay) { //prüft ob schon neues Bild fällig ist
            animation = 0;
            computeAnimation();
        }
    }

    public void computeAnimation() {
        currentpic++;
        if(currentpic>loopto) //Wenn letztes Bild dran war
            currentpic = loopfrom; //kommt wieder das erste
    }

    public void setLoop(int from, int to) {
        loopfrom = from;
        loopto = to;
        currentpic = from;
    }
```

Für einen Moment hatte ich das Gefühl, dass es funktionierte. Mir ist aufgefallen, dass es bei mir in Ubuntu eine Tastatureinstellung gibt, die dafür sorgt, dass bei längerem Gedrückthalten einer Taste diese öfter ausgeführt wird. Also zB wenn man eine Zeile löschen würde indem man backspace lange gedrückt hält. Eigentlich ganz normal aber mein Eclipse hat das so umgesetzt, als würde man diese Taste (zB eine Richtungstaste) ganz schnell immer wieder drücken.

Wie auch immer, ich hab jetzt einfach einen String erzeugt, in den ich den Namen der aktuellen Animation reinschreibe und die Animation wird nur noch geändert falls im String was anderes steht als das was als nächstes gezeigt werden soll. Danke für eure Hilfe! 

Nachtrag: Mist, klappt nur, wenn ich nicht mehr als eine Taste drücke.  Ich benutz nämlich noch die NachOben Taste als Sprint Trigger, also wenn die zusätzlich zu NachLinks oder NachRechts gedrückt wird, läuft der Held schneller.


----------



## flortsch (19. Mai 2010)

Hallo 
Ich programmiere auch gerade einen Super Mario Klon, wobei ich sagen muss, dass es mein erstes richtiges Spiel ist (so kleine Spielerein wie TicTacTo, 4 Gewinnt etc. kann man mit sowas ja nicht vergleichen xD). Deswegen habe ich auch das Tutorial von Quaxli durchgearbeitet und das Meiste davon für mein Spiel übernommen (Hier ein Danke an dich ).

Das Problem bei mir ist auch das ständige Neustarten der Loops aufgrund der Tastenereignisse.
Deswegen habe ich versucht, das Ganze mit Hilfe einer booleschen Variable zu umgehen, die zu Beginn false ist.

Beim Drücken einer Pfeiltaste setze ich entsprechend ein Flag auf true, das dann in einer Methode überprüft und in der dementsprechend dann gehandelt wird. Das Flag wird nur behandelt, wenn die oben genannte Hilfsvariable auf false gesetzt ist. Dem Flag entsprechend passiert dort dann das Setzen der Geschwindigkeit und Laufrichtung, sowie das Setzen des Loops. Danach wird die Hilfsvariable auf true gesetzt, so dass die Anweisungen nur ein Mal durchlaufen werden, also das Loopen nur einmal gesetzt wird, und nicht aufgrund der Tastenereignisse ständig neu. Beim Loslassen einer Taste setze ich Flag und Hilfsvariable wieder auf false.

Das Ganze klappt ganz gut, jedoch funktioniert es nicht, wenn ich schneller laufen will, also wenn eine dritte Variable in den Bedingungen dazu kommt.
Das Loopen startet dann wieder ständig neu-.-^^

Hier ein Ausschnitt aus dem Code: Springen habe ich noch nicht gemacht..^^

```
//Überprüfung der Bedienungstasten
	public void checkKeys() {
        if(left && !once2) { 										// Gehen nach links
            mario.setHorizontalSpeed(-150);
            mario.setSpeed(false);
            mario.setDirection(false);
            mario.setLoop(8, 10);
            once2 = true;
        }
 
        if(right && !once2) { 										// Gehen nach rechts
            mario.setHorizontalSpeed(150);
            mario.setSpeed(false);
            mario.setDirection(true);
            mario.setLoop(0, 2);
            once2 = true;
        }
        
        if((speed&&right) || (speed&&left)) {
        	if(!once2) {			
   	     		if(mario.getHorizontalSpeed() < 0) {				// Laufen nach links
    	            mario.setHorizontalSpeed(-350);
               	 	mario.setDirection(false);
        	        mario.setSpeed(true);
    				mario.setLoop(8, 10);
            	}
            
            	else if(mario.getHorizontalSpeed() > 0) {			// Laufen nach rechts
                	mario.setHorizontalSpeed(350);
                	mario.setDirection(true);
                	mario.setSpeed(true);
    				mario.setLoop(0, 2);
        		}
        	once2 = true;
        	}
        }
 		
        if(!left && !right) { 										// Keine Bewegung nach rechts oder links
            mario.setHorizontalSpeed(0);
            
           	if (mario.getDirection()) mario.setLoop(0,0);			// Blickrichtung nach rechts
			else mario.setLoop(8,8);								// Blickrichtung nach links
		}
		
 		if(down) { 													// Ducken
 			mario.setHorizontalSpeed(0);
			if(mario.getDirection()) mario.setLoop(6,6);
			else mario.setLoop(14,14);
        }
        
		else if(!up && !down) mario.setVerticalSpeed(0);			// Keine Bewegung nach oben oder unten
	}

    // KeyListener-Methoden
	public void keyTyped(KeyEvent e) {}

	public void keyPressed(KeyEvent e) {
		if(e.getKeyCode()==KeyEvent.VK_UP) 		up = true;
		if(e.getKeyCode()==KeyEvent.VK_DOWN) 	down = true;
		if(e.getKeyCode()==KeyEvent.VK_LEFT) 	left = true;
		if(e.getKeyCode()==KeyEvent.VK_RIGHT) 	right = true;
		if(e.getKeyCode()==KeyEvent.VK_S)		speed = true;
	}

	public void keyReleased(KeyEvent e) {
		if(e.getKeyCode()==KeyEvent.VK_UP) 		{ up 	= false; once2 = false; }
		if(e.getKeyCode()==KeyEvent.VK_DOWN) 	{ down 	= false; once2 = false; }
		if(e.getKeyCode()==KeyEvent.VK_LEFT) 	{ left 	= false; once2 = false; }
		if(e.getKeyCode()==KeyEvent.VK_RIGHT) 	{ right = false; once2 = false; }
		if(e.getKeyCode()==KeyEvent.VK_S)		{ speed = false; once2 = false; }

		if(e.getKeyCode()==KeyEvent.VK_ENTER) {
			if(!isStarted()) {
				setStarted(true);
				doInitializations();
			}
		}

		if(e.getKeyCode()==KeyEvent.VK_ESCAPE) {
			if(isStarted()) stopGame();
			else {
				setStarted(false);
				System.exit(0);
			}
		}
	}
```

Mit den langsamen Geh-Bewegungen funktioniert das ganze ja..^^
Warum ist es nicht möglich, es so beim Laufen auch zu machen ?

Ich hoffe ihr könnt mir helfen 
Danke im Vorraus =)


----------



## flortsch (19. Mai 2010)

Huhu 
Das ganze hat sich erledigt^^
Ich prüfe einfach nicht zu Beginn, ob die Hilfsvariable false ist, sondern vor dem Befehl setLoop(int x, int y).


```
if((speed&&right) || (speed&&left)) {				
   	     		if(mario.getHorizontalSpeed() < 0) {				// Laufen nach links
    	            mario.setHorizontalSpeed(-350);
               	 	mario.setDirection(false);
        	        mario.setSpeed(true);
        	        if(!once2) mario.setLoop(8, 10);
            	}
            
            	else if(mario.getHorizontalSpeed() > 0) {			// Laufen nach rechts
                	mario.setHorizontalSpeed(350);
                	mario.setDirection(true);
                	mario.setSpeed(true);
    				if(!once2) mario.setLoop(0, 2);
        		}
        		once2 = true;
        }
```

mfg


----------



## flortsch (19. Mai 2010)

Hallo ich bins wieder..xD

Ich habe beim Laufen die Images der Gehbewegung verwendet, da ich die anderen Images noch nicht aufbereitet hatte. Als ich dann die Laufimages verwendet habe, musste ich feststellen, dass das Problem wieder da ist -.-

Die Loops starten wieder von vorne..und ich hab keine Ahnung warum.
Ich muss das Game in einer Woche fertig haben, und bin noch nicht sehr weit..^^
Hoffe ihr könnt mir helfen.


----------



## Steev (20. Mai 2010)

Die Animation ist unabhängig von den zu drückenden Tasten zu verwalten, mit einem Tastendruck wird lediglich ein Flag definiert, dass dann in dem Logik-Thread entsprechend abgegriffen und gehandelt wird. Damit ist es dann auch möglich mehere Tasten auf einmal zu drücken und entsprechend zu reagieren.


----------



## flortsch (20. Mai 2010)

Das mache ich doch..^^
Ich setze bei einem Tastendruck einen Flag.
checkKeys() wird dann in der Hauptschleife des Spiels aufgerufen.

Das Problem ist ja, wenn ich normal gehe, wird die Animation richtig geladen.
Wenn ich jedoch laufe, startet die Animation ständig von neu.

Obwohl ich beide Fälle gleich behandle.


----------



## Steev (20. Mai 2010)

Ich muss deinen Code jetzt nicht wirklich verstehen, oder?
Du machst bei deinen Speed-Abfragen exakt-dasselbe wie bei den normalen Lauf-Abfragen. Ist es in Ordnung dass bei den Speed-Abfragen noch zusätzlich die Lauf-Abfragen aufgerufen werden? Und mal so: Wenn speed nur eine erhöhung der Laufgesschwindigkeit bedeutet, was hindert dich daran die Geschwindigkeitswerte nicht konstant zu setzten sondern berechnen zu lassen (#setSpeed(basicSpeed * speedFactor)?

Gruß
Steev


----------



## flortsch93 (20. Mai 2010)

Hallo 

Herzlichen Dank für deinen Beitrag^^
Jetzt funktioniert es^^

Das Problem war, dass ich bei den Laufabfragen die Gehabfragen zusätzlich getätigt hab..^^

Jetzt mach ichs einfach so:

```
if(flag_rechts) {
       if(!speed) {
              Einstellungen für gehen
       }
       if(speed) {
              Einstellungen für laufen
       }
}
```

Eigentlich total verständlich..weiß nicht wieso ich nicht früher darauf gekommen bin..^^
Danke nochmal =)


----------

