# KeyListener-Problem nach dem "Gedrückthalten" einer Taste



## DarXun (14. Jun 2010)

Heyho!

Ich bin gerade dabei 'n bisschen rumzuprobieren, was die Spieleprogrammierung angeht, hab da eher was retro-mäßiges im Sinne und will's grafisch usw. auch recht simpel halten.

Ich denke mal, dass so ziemlich jeder hier Pokémon kennt.
In Pokémon, oder auch diversen anderen Spielen wie z.B. Dragon Quest Monsters, steuert der Spieler seine Spielfigur über die Pfeiltasten (bzw. eigentlich das Steuerkreuz am Gameboy).
Drückt man die entsprechende Taste bewegt sich die Figur langsam auf das nächste Feld, man kann sich also eigentlich nur in einem vorgegebenen Gitter bewegen.

Das hab ich soweit auch umgesetzt. Ich hab meine Figur die sich ohne weiteres in ihrem 16px x 16px Gitter bewegt.
Mein Problem ist jedoch, dass, wenn ich die entsprechende Pfeiltaste gedrückt halte für kurze Zeit, dann aber loslasse, sich die Figur immernoch weiterbewegt.
Ich glaube, dass ganze liegt am KeyListener, der bei mir bei eben entsprechendem Tastendruck, die Figur passend bewegt.

Das Optimum wäre für mich, dass wenn ich eine Pfeiltaste gedrückt halte, die Figur sich bewegt und wenn ich wieder loslasse, die Figur aufhört sich zu bewegen, bzw. noch eben die Bewegung in's Gitter abschließt.

Ich hab schon versucht, den KeyListener wieder zu entfernen und nach der Bewegung wieder hinzuzufügen.
Ich hab natürlich auch schon versucht, einfach per Abfrage, ob die Figur sich grade bewegt, das ganze zu steuern.

Fazit: Will alles nicht!

Ich hab den ganzen Quatsch mal hochgeladen, damit Ihr euch ein Bild davon machen könnt.
Einfach die main-Methode der Klasse Game ausführen, und mal nach links und rechts usw. bewegen.
Die Figur kann noch außerhalb des Bildschirms kommen... das führt leider noch zu einer Exception, hab mich noch nicht drum gekümmert, aber kommt alles noch.
Wenn ihr die Bewegung dann mal gesehen habt, haltet die Tasse einfach mal gedrückt und lasst wieder los nach kurzer Zeit... genau DAS ist mein Problem 

Link: Klick mich :3


----------



## Gast2 (14. Jun 2010)

Zeig einfach mal dein KeyListener...


----------



## DarXun (15. Jun 2010)

Dafür hab ich ja extra den Quellcode hochgeladen...
Aber hier ist nochmal der KeyListener:


```
package Main;

import java.awt.event.*;

public class GameKeyAdapter extends KeyAdapter
{
    private GameField gf;

    public GameKeyAdapter(GameField gf)
    {
        this.gf = gf;
    }
    
    public void keyPressed(KeyEvent e)
    {                    
        switch(e.getKeyCode())
        {
            case(KeyEvent.VK_UP):
                Game.getInstance().getPlayer().move(1);
                break;
                            
            case(KeyEvent.VK_RIGHT):
                Game.getInstance().getPlayer().move(2);
                break;
                            
            case(KeyEvent.VK_DOWN):
                Game.getInstance().getPlayer().move(3);
                break;
                            
            case(KeyEvent.VK_LEFT):
                Game.getInstance().getPlayer().move(4);
                break;
        }
        gf.repaint();
    }
}
```


----------



## SlaterB (15. Jun 2010)

du kannst nicht einfach auf jedes Event reagieren, woher willst du wissen, ob pro Sekunde 5 Events oder 5000 kommen?
vielleicht variiert das auch je nach Systemlast, schön wird das nicht, auch wenn dein aktueller Fehler nicht direkt erklärbar ist,


da sollte generell ein anderes Konzept her:
ein Thread läuft, macht relativ kontrollierbare 30ms-Pausen und bewegt dann SOLANGE der ensprechende Status gesetzt ist,
was bei Beginn und Ende des Maus-Drückens je einmal geändert wird

schau dir einfach folgendes Tutorial an, da kommt das vor
http://www.ralf-bauer.org/java/tutorial/Tutorial.zip


----------



## Momolin (16. Jun 2010)

Hallo,

bei mir hat es gereicht, wenn ich in Deinem Code in der Klasse GameWindow folgende Änderung gemacht habe:


```
package Main;

import javax.swing.JFrame;

/**
 *
 */
public class GameWindow extends JFrame
{
    private GameField gf;
    private GameKeyAdapter k;

    public GameWindow(String title)
    {
        super(title);
        /* folgende zwei Zeilen habe ich getauscht */
        // gf muss vor der Übergabe an GameKeyAdapter
        // instantiiert werden 
        gf = new GameField();
        k = new GameKeyAdapter(gf);


        addKeyListener(k);
        add(gf);
    }

    public GameField getGameField()
    {
        return gf;
    }
}
```

dann musst Du in der Klasse Level an folgender Stelle noch die Arraygrenzen von o prüfen, bevor Du neu zeichnest.


```
for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				if (objects[x][y] != null) {
					int i = objects[x][y].getX();
					int j = objects[x][y].getY();	
					o[i][j] = objects[x][y];
				}
			}
		}
```

Viele Grüße
Momolins


----------



## DarXun (16. Jun 2010)

@Momolin

Vielen Dank für die Hilfe, leider ging's garnicht darum >.<
Aber halb so wild ;-)

@SlaterB

Also es kommen, wie du gesagt hast, natürlich *etliche* Events an, und wie du ebenfalls sagtest, kann ich nicht auf jedes reagieren.
Genau das ist mein Problem, ich will diese Events ja praktisch abfangen, bzw. alle nicht ausführen, wenn der Player eh noch in der Methode move(int dir) ist...
Es sollte praktisch erst dann nach weiteren Events gefragt werden, wenn move fertig ist, bzw. alle Events, die während move ankommen, sollten verworfen werden!

Dein Vorschlag, das gesamte Movement abzuändern finde ich nicht so schön.
Es ist ja gerade mein Ziel, so ein Movement-System hinzubekommen... ganz so wie bei den alten Gameboy-Spielen, wie eben Pokémon oder Dragon Quest Monsters


----------



## SlaterB (16. Jun 2010)

> Es ist ja gerade mein Ziel, so ein Movement-System hinzubekommen

was meinst du mit 'so ein System', hast du das andere so genau verstanden, dass du da Unterschiede erkennen kannst?

das was ich vorschlage bedeutet auch Bewegung solange gedrückt ist (nicht Aufhören bei einem zweiten Klick oder so),
nur viel sauberer implementiert, arbeite einfach das Tutorial durch, selbst wenn du das nicht magst hast du eine gute andere Sicht kennengelernt


----------



## Momolin (16. Jun 2010)

Hi ich schlage vor die Klasse Player mit einer stopMotion Methode und einem Timer für  die Bewegung auszustatten:

```
public class Player extends DisplayableObject implements Moveable,
		ActionListener {
	private int speed;

	private Timer moveTimer;

	public Player() {
		super();
		sprite = new MovingSprite("Data/player");

		direction = 1;
		solid = true;

		speed = sprite.getWidth(); // because width and height are equal
                //der erste Parameter bestimmt die Geschwindigkeit
		moveTimer = new Timer(50, this);
		moveTimer.setInitialDelay(0);
	}

	public void move(int dir) {
		direction = dir;
// so könnte man während der Bewegung neue Events übergehen 
           if (!moveTimer.isRunning())
		moveTimer.restart();
	}

	public void stopMotion() {
		moveTimer.stop();
	}

	public void turn(int dir) {
	}
/**
entspricht der "GameLoop"
*/
	@Override
	public void actionPerformed(ActionEvent arg0) {

		sprite.setDirection(direction);
		Game g = Game.getInstance();
		Level l = g.getLevel();

		switch (direction) {
		case 1:
			setY(getY() - 2);
			break;

		case 2:
			setX(getX() + 2);
			break;

		case 3:
			setY(getY() + 2);
			break;

		case 4:
			setX(getX() - 2);
			break;
		}

		l.refresh();
		g.repaintField();
	}
}
```

und dann in der Klasse GameKeyAdapter beim Loslassen eine Pfeiltaste den Player zu stoppen:


```
public class GameKeyAdapter extends KeyAdapter {
	private GameField gf;

	public GameKeyAdapter(GameField gf) {
		this.gf = gf;
	}

	@Override
	public void keyReleased(KeyEvent e) {
		Game.getInstance().getPlayer().stopMotion();
	}

	@Override
	public void keyPressed(KeyEvent e) {
		switch (e.getKeyCode()) {

		case (KeyEvent.VK_UP):
			Game.getInstance().getPlayer().move(1);
			break;

		case (KeyEvent.VK_RIGHT):
			Game.getInstance().getPlayer().move(2);
			break;

		case (KeyEvent.VK_DOWN):
			Game.getInstance().getPlayer().move(3);
			break;

		case (KeyEvent.VK_LEFT):
			Game.getInstance().getPlayer().move(4);
			break;
		}
		gf.repaint();
	}
}
```

meine Änderunge aus dem ersten Posting sind aber weiter erforderlich.

Viele Grüße
Momolin


----------



## SlaterB (16. Jun 2010)

genau, so in der Art meinte ich das auch bzw. steht es im Tutorial


----------



## DarXun (18. Jun 2010)

Erstmal: Vielen Dank für die freundliche und lehrreiche Hilfe.
Ich bin das Tutorial teilweise durchgegangen und hab den Quellcode selbstständig programmieren können, kam etwa auf's gleiche raus...

Ein Problem habe ich jedoch noch.
Ich hatte ja anfangs erläutert, dass der Player nach einer Bewegung in einem 16px x 16px Gitter landen soll.
Durch das Movement-System, wie es jetzt besteht, landet der Spieler aber in einem 2px x 2px Gitter.
Was ich aber vermeiden will, ist, dass ich den Player um 16px bewege, also zumindest bei jedem Schritt.

Mein Ansatz das zu lösen wäre, sobald die Taste gelöst wurde, zu schauen, ob der Spieler sich in dem Gitter befindet, und falls nicht, ihn eben je nach `direction` weiter in 2er Schritten so zu bewegen, dass er in eben genannten Gitter landet.

Ich bin mir nur nicht sicher, wo ich das ganze einbauen soll. Ich denke, dass es wohl in die stopMotion()-Methode gehört, kann das ganze aber auch gerade nicht ausprobieren, werd' ich aber, sobald ich die Zeit dazu habe!

Vielen Dank nochmal!!


----------



## Momolin (21. Jun 2010)

Hallo, 


DarXun hat gesagt.:


> Mein Ansatz das zu lösen wäre, sobald die Taste gelöst wurde, zu schauen, ob der Spieler sich in dem Gitter befindet, und falls nicht, ihn eben je nach `direction` weiter in 2er Schritten so zu bewegen, dass er in eben genannten Gitter landet.
> 
> Ich bin mir nur nicht sicher, wo ich das ganze einbauen soll. Ich denke, dass es wohl in die stopMotion()-Methode gehört, kann das ganze aber auch gerade nicht ausprobieren, werd' ich aber, sobald ich die Zeit dazu habe!


würde ich genau so machen, da diese Methode ja am Ende der Bewegung steht und das ist doch der richtige Zeitpunkt Position (oder auch andere Eigenschaften) des Players zu korrigieren.

Grüße
Momolin


----------

