K (Bewegungsgeschwindigkeit festlegen)
Zeile 136: Zeile 136:
 
=== Bewegungsgeschwindigkeit festlegen ===
 
=== Bewegungsgeschwindigkeit festlegen ===
  
Was die tatsächliche Bewegungsgeschwindigkeit der Snake ist, hängt davon ab, welche Taste der Nutzer zuletzt runtergedrückt hat und ist in der Snake über [https://docs.engine-alpha.org/4.x/ea/event/KeyListener.html <code>ea.event.KeyListener</code>] gelöst wie im [[[[v4.x/User Input|vorigen Tutorial]]:
+
Was die tatsächliche Bewegungsgeschwindigkeit der Snake ist, hängt davon ab, welche Taste der Nutzer zuletzt runtergedrückt hat und ist in der Snake über [https://docs.engine-alpha.org/4.x/ea/event/KeyListener.html <code>ea.event.KeyListener</code>] gelöst wie im [[v4.x/User Input|vorigen Tutorial]]:
  
 
<source lang="java">
 
<source lang="java">

Version vom 5. Januar 2020, 12:35 Uhr


Dies ist ein Tutorial für die Engine Alpha 4.x. Diese funktioniert anders als die EDU-Version. Du findest eine Übersicht über alle Tutorials hier.

Inhalt

In diesem Tutorial:

  • Baust du einen frame-weisen Game Loop mit Spiellogik
  • Animierst du flüssige Bewegung in deinem Spiel

Snake ohne Körper

Das folgende Program implementiert ein einfaches Snake-Spiel mit einem Steuerbaren Kreis und dem Ziel, Goodies zu sammeln.

Das Snake-Spiel: Der Kreis jagt die willkürlich auftauchenden Texte
import ea.*;
import ea.actor.Circle;
import ea.actor.Text;
import ea.collision.CollisionEvent;
import ea.collision.CollisionListener;
import ea.event.KeyListener;

import java.awt.Color;
import java.awt.event.KeyEvent;

public class SnakeHead
extends Scene {

    private Text scoreText = new Text("Score: 0", 1.4f);
    private int score = 0;

    private Snake snake = new Snake();

    public SnakeHead() {
        add(snake);

        scoreText.setPosition(-9, 5);
        add(scoreText);
        placeRandomGoodie();

    }

    public void setScore(int score) {
        this.score = score;
        this.scoreText.setContent("Score: " + score);
    }

    public void increaseScore() {
        setScore(score+1);
    }

    public void placeRandomGoodie() {
        float x = Random.nextFloat()*10 - 5;
        float y = Random.nextFloat()*10 - 5;
        Goodie goodie = new Goodie();
        goodie.setCenter(x, y);
        add(goodie);
        goodie.addCollisionListener(snake, goodie);
    }

    public static void main(String[] args) {
        Game.start(600, 400, new SnakeHead());
        //Game.setDebug(true);
    }

    private class Snake
    extends Circle
    implements FrameUpdateListener, KeyListener {
        private Vector v_per_s = new Vector(0,0);

        public Snake() {
            super(1);
            setColor(Color.GREEN);
        }

        @Override
        public void onFrameUpdate(float timeInS) {
            this.moveBy(v_per_s.multiply(timeInS));
        }

        @Override
        public void onKeyDown(KeyEvent keyEvent) {
            switch (keyEvent.getKeyCode()) {
                case KeyEvent.VK_W:
                    v_per_s = new Vector(0, 5);
                    break;
                case KeyEvent.VK_A:
                    v_per_s = new Vector(-5, 0);
                    break;
                case KeyEvent.VK_S:
                    v_per_s = new Vector(0, -5);
                    break;
                case KeyEvent.VK_D:
                    v_per_s = new Vector(5, 0);
                    break;
            }
        }
    }

    private class Goodie
    extends Text
    implements CollisionListener<Snake> {
        public Goodie() {
            super("Eat Me!",1);
            setColor(Color.RED);
        }

        @Override
        public void onCollision(CollisionEvent<Snake> collisionEvent) {
            increaseScore();
            this.remove();
            placeRandomGoodie();
        }
    }
}

Snake: Frame-Weise Bewegung

Die Snake ist der spielbare Charakter. Sie soll sich jeden Frame in eine der vier Himmelsrichtungen bewegen.

Die Bewegung der Snake soll möglichst flüssig sein. Daher wird die Bewegung in jedem einzelnen Frame ausgeführt, um maximal sauber auszusehen. Dazu implementiert die Snake das Engine-Interface ea.event.FrameUpdateListener, um in jedem Frame seine Bewegungslogik auszuführen.

Hierzu kennt die Snake ihre aktuelle Geschwindigkeit als gerichteten ea.Vektor (in Meter/Sekunde). Ein Frame ist deutlich kürzer als eine Sekunde. Mathematik zur Hilfe! v = s/t und damit s = v*t. Jeden Frame erhält die Snake die tatsächlich vergangene Zeit t seit dem letzten Frame-Update und verrechnet diese mit ihrer aktuellen Geschwindigkeit v:

@Override
public void onFrameUpdate(float timeInS) {
    this.moveBy(v_per_s.multiply(timeInS));
}

Bewegungsgeschwindigkeit festlegen

Was die tatsächliche Bewegungsgeschwindigkeit der Snake ist, hängt davon ab, welche Taste der Nutzer zuletzt runtergedrückt hat und ist in der Snake über ea.event.KeyListener gelöst wie im vorigen Tutorial:

@Override
public void onKeyDown(KeyEvent keyEvent) {
    switch (keyEvent.getKeyCode()) {
        case KeyEvent.VK_W:
            v_per_s = new Vector(0, 5);
            break;
        case KeyEvent.VK_A:
            v_per_s = new Vector(-5, 0);
            break;
        case KeyEvent.VK_S:
            v_per_s = new Vector(0, -5);
            break;
        case KeyEvent.VK_D:
            v_per_s = new Vector(5, 0);
            break;
        }
}