v4.x/Collision: Unterschied zwischen den Versionen
(→Vorlage: Doodle Jump) |
(→Kollisionen mit CollisionListener kontrollieren) |
||
(11 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 10: | Zeile 10: | ||
* Nutzt du verschiedene Möglichkeiten, um auf Kollisionen zu reagieren und diese aufzulösen | * Nutzt du verschiedene Möglichkeiten, um auf Kollisionen zu reagieren und diese aufzulösen | ||
* Entwickelst du einen Doodle Jump clone | * Entwickelst du einen Doodle Jump clone | ||
+ | |||
== Spielkonzept und grundlegender Aufbau == | == Spielkonzept und grundlegender Aufbau == | ||
− | Ein Frosch soll fröhlich durch das Spiel springen, wann immer er die Chance hat, sich vom Boden abzustoßen. | + | [[Datei:FrogJump Tutorial.png|mini|Dieser Frosch soll sich durch das Spiel springen]] |
+ | |||
+ | Ein Frosch soll fröhlich durch das Spiel springen, wann immer er die Chance hat, sich vom Boden abzustoßen. In der Scene <code>FroggyJump</code> kann der Spieler ein Objekt der Klasse <code>Frog</code> steuern. Zusätzlich geben Objekte der Klasse <code>Platform</code> halt. | ||
+ | |||
+ | Damit ergibt sich das Codegerüst für das Spiel: | ||
+ | |||
+ | <source lang="java"> | ||
+ | package eatutorials.collision; | ||
+ | |||
+ | import ea.*; | ||
+ | import ea.actor.*; | ||
+ | import ea.collision.CollisionEvent; | ||
+ | import ea.collision.CollisionListener; | ||
+ | import ea.event.KeyListener; | ||
+ | |||
+ | import java.awt.event.KeyEvent; | ||
+ | |||
+ | |||
+ | public class FroggyJump extends Scene { | ||
+ | |||
+ | private Frog frog; | ||
+ | |||
+ | public FroggyJump() { | ||
+ | frog = new Frog(); | ||
+ | add(frog); | ||
+ | setGravity(Vector.DOWN.multiply(10)); | ||
+ | Camera cam = getCamera(); | ||
+ | cam.setFocus(frog); | ||
+ | cam.setOffset(new Vector(0, 4)); | ||
+ | makePlatforms(10); | ||
+ | } | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | FroggyJump fj = new FroggyJump(); | ||
+ | Game.start(400, 600, fj); | ||
+ | //Game.setDebug(true); | ||
+ | } | ||
+ | |||
+ | private void makePlatforms(int heightLevel) { | ||
+ | for(int i=0; i < heightLevel; i++) { | ||
+ | Platform platform = new Platform(5, 1); | ||
+ | platform.setPosition(0, i*4); | ||
+ | add(platform); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Frog extends Image implements FrameUpdateListener { | ||
+ | |||
+ | private static float MAX_SPEED = 4; | ||
+ | |||
+ | public Frog() { | ||
+ | super("eatutorials/collision/assets/Jump (32x32).png", 25f); | ||
+ | setBodyType(BodyType.DYNAMIC); | ||
+ | setRotationLocked(true); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void onFrameUpdate(float deltaSeconds) { | ||
+ | Vector velocity = this.getVelocity(); | ||
+ | //A: Image direction | ||
+ | if(velocity.getX() < 0) { | ||
+ | setFlipHorizontal(true); | ||
+ | } else { | ||
+ | setFlipHorizontal(false); | ||
+ | } | ||
+ | |||
+ | //B: Horizontal Movement | ||
+ | if (Game.isKeyPressed(KeyEvent.VK_A)) { | ||
+ | if(velocity.getX() > 0) { | ||
+ | setVelocity(new Vector(0, velocity.getY())); | ||
+ | } | ||
+ | applyForce(Vector.LEFT.multiply(600)); | ||
+ | } else if (Game.isKeyPressed(KeyEvent.VK_D)) { | ||
+ | if(velocity.getX() < 0) { | ||
+ | setVelocity(new Vector(0, velocity.getY())); | ||
+ | } | ||
+ | applyForce(Vector.RIGHT.multiply(600)); | ||
+ | } | ||
+ | if(Math.abs(velocity.getX()) > MAX_SPEED) { | ||
+ | setVelocity(new Vector(MAX_SPEED * Math.signum(velocity.getX()) , velocity.getY())); | ||
+ | } | ||
+ | |||
+ | //C: Jump if possible | ||
+ | if (isGrounded()) { | ||
+ | this.setVelocity(new Vector(velocity.getX(), 0)); | ||
+ | this.applyImpulse(Vector.UP.multiply(180)); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Platform extends Rectangle { | ||
+ | |||
+ | public Platform(float width, float height) { | ||
+ | super(width, height); | ||
+ | setBodyType(BodyType.STATIC); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | [[Datei:FrogTutorial1.gif|mini|Der Frosch kann sich bewegen, knallt aber unangenehmerweise noch gegen die Decke]] | ||
+ | |||
+ | Ein paar Erklärungen zum Codegerüst für <code>FroggyJump</code>: | ||
+ | |||
+ | |||
+ | === Physikalische Eigenschaften === | ||
+ | |||
+ | Wie im [[v4.x/Physics|Physics-Tutorial]] beschrieben, werden die physikalischen Eigenschaften der Spielobjekte und ihrer Umgebung bestimmt: | ||
+ | |||
+ | * Platformen sind '''statische Objekte''': Sie ignorieren Schwerkraft und können nicht durch andere Objekte verschoben werden (egal mit wie viel Kraft der Frosch auf sie fällt). | ||
+ | * Der Frosch ist ein '''dynamisches Objekt''': Er lässt sich von der Schwerkraft beeinflussen und wird von den statischen Platformen aufgehalten. | ||
+ | * In der Scene <code>FroggyJump</code> existiert eine Schwerkraft von 10 m/s^2. Sie wird mit [https://docs.engine-alpha.org/4.x/ea/Scene.html#setGravity-ea.Vector- <code>setGravity(Vector)]</code> gesetzt. | ||
+ | |||
+ | === Bewegung des Frosches === | ||
+ | |||
+ | Die Bewegung des Frosches wird in jedem Frame kontrolliert. Wie im [[v4.x/Game Loop|Game Loop Tutorial]] beschrieben, wird hierzu das Interface <code>FrameUpdateListener</code> genutzt. | ||
+ | |||
+ | In jedem Frame wird die Bewegung des Frosches in dreierlei hinsicht kontrolliert: | ||
+ | |||
+ | # '''Teil A: Blickrichtung des Frosches''': Das Bild des Frosches wird gespiegelt, falls er sich nach links bewegt. | ||
+ | # '''Teil B: Horizontale Bewegung des Frosches''': Jeden Frame, in dem der Spieler den Frosch (per Tastendruck) nach links oder rechts steuern möchte, wird eine Bewegungskraft auf den Frosch angewendet. Wird der Frosch in die Gegenrichtung seiner aktuellen Bewegung gesteuert, wird seine horizontale Geschwindigkeit zuvor auf 0 gesetzt, um ein langsames Abbremsen zu verhindern. Das ermöglicht schnelle Reaktion auf Nutzereingabe und ein besseres Spielgefühl. Zusätzlich wird seine Geschwindigkeit auf die Konstante <code>MAX_SPEED</code> begrenzt. | ||
+ | # '''Teil C: Springe, wenn möglich''': Mit der Funktion [https://docs.engine-alpha.org/4.x/ea/actor/Actor.html#isGrounded-- <code>isGrounded()</code>] bietet die Engine einen einfachen Test, um sicherzustellen, dass der Frosch Boden unter den Füßen hat. Wenn dies gegeben ist, wird ein Sprungimpuls auf den Frosch angewandt. Zuvor wird die vertikale Komponente seiner Geschwindigkeit auf 0 festgesetzt - das garantiert, dass der Frosch jedes mal die selbe Sprunghöhe erreicht. | ||
− | [[ | + | === Die Kamera folgt dem Frosch === |
+ | |||
+ | Der Frosch soll stets sichtbar bleiben. Hierzu werden zwei Funktionen der [[v4.x/Camera|Engine-Kamera]] genutzt: | ||
+ | |||
+ | # Der Frosch wird mit [https://docs.engine-alpha.org/4.x/ea/Camera.html#setFocus(ea.actor.Actor) <code>Camera.setFocus(Actor)</code>] in den Mittelpunkt der Kamera gesetzt. Sie folgt dem Frosch. | ||
+ | # Gleichzeitig soll der Frosch nicht exakt im Mittelpunkt des Bildschirms sein: Weil das Spielziel ist, sich nach oben zu bewegen, braucht der Spieler mehr Blick nach oben als nach unten. Mit [https://docs.engine-alpha.org/4.x/ea/Camera.html#setOffset(ea.Vector) <code>Camera.setOffset(Vector)</code>] wird die Kamera nach oben verschoben. | ||
− | == Kollisionen | + | == Durch Platformen Springen: Kollisionen kontrollieren == |
Das Interface [https://docs.engine-alpha.org/4.x/ea/collision/CollisionListener.html <code>CollisionListener</code>] wurde bereits in seiner grundlegenden Form im [[v4.x/User Input|Nutzereingabe-Tutorial]] benutzt. | Das Interface [https://docs.engine-alpha.org/4.x/ea/collision/CollisionListener.html <code>CollisionListener</code>] wurde bereits in seiner grundlegenden Form im [[v4.x/User Input|Nutzereingabe-Tutorial]] benutzt. | ||
− | <code>CollisionListener</code> kann mehr als nur melden, wenn zwei [https://docs.engine-alpha.org/4.x/ea/actor/Actor.html <code>Actor</code>-Objekte] sich überschneiden. | + | <code>CollisionListener</code> kann mehr als nur melden, wenn zwei [https://docs.engine-alpha.org/4.x/ea/actor/Actor.html <code>Actor</code>-Objekte] sich überschneiden. Um das <code>FroggyJump</code>-Spiel zu implementieren, nutzen wir weitere Features. |
− | |||
Unser Frosch soll fähig sein, von unten "durch die Platform hindurch" zu springen. Von oben fallend soll er natürlich auf der Platform stehen bleiben. | Unser Frosch soll fähig sein, von unten "durch die Platform hindurch" zu springen. Von oben fallend soll er natürlich auf der Platform stehen bleiben. | ||
Zeile 31: | Zeile 157: | ||
* Kollidiert der Frosch von unten, so soll die Kollision ignoriert werden. Er prallt so nicht von der Decke ab und kann weiter nach oben Springen. | * Kollidiert der Frosch von unten, so soll die Kollision ignoriert werden. Er prallt so nicht von der Decke ab und kann weiter nach oben Springen. | ||
* Kollidiert der Frosch von oben, so soll die Kollision normal aufgelöst werden, sodass er nicht durch den Boden fällt. | * Kollidiert der Frosch von oben, so soll die Kollision normal aufgelöst werden, sodass er nicht durch den Boden fällt. | ||
+ | |||
+ | Hierzu stellt das [https://docs.engine-alpha.org/4.x/ea/collision/CollisionEvent.html <code>CollisionEvent</code>]-Objekt in der <code>onCollision</code>-Methode Funktionen bereit. | ||
+ | |||
+ | <source lang="java"> | ||
+ | class Platform extends Rectangle implements CollisionListener<Frog> { | ||
+ | |||
+ | public Platform(float width, float height) { | ||
+ | super(width, height); | ||
+ | setBodyType(BodyType.STATIC); | ||
+ | this.addCollisionListener(Frog.class, this); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void onCollision(CollisionEvent<Frog> collisionEvent) { | ||
+ | float frogY = collisionEvent.getColliding().getPosition().getY(); | ||
+ | if(frogY<this.getY()) { | ||
+ | collisionEvent.ignoreCollision(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | == Anregung zum Experimentieren == | ||
+ | |||
+ | * '''Optische Verbesserung''': Der Frosch ist bisher nur ein einzelnes Bild. Das ist nur ein kleiner Teil eines großen, kostenfrei verfügbaren [https://pixelfrog-assets.itch.io/pixel-adventure-1 Asset Packs von Pixel Frog]. Das Tutorial zu [[v4.x/Stateful Animation|Animationen mit mehreren Zuständen]] kann dir helfen, den Frosch lebendiger aussehen zu lassen. | ||
+ | * '''Weitere Spielobjekte''': Neben Bällen, die herunterfallen, können mehr gefahren auf den Spieler warten: Fliegende Gegner, die auf einer horizontalen hin und her fliegen, Felsbrocken, die von Paltform zu Platform herunterfallen. Pick Ups können dem Spieler zusätzliche Punkte geben... Lass deiner Kreativität freien lauf! |
Aktuelle Version vom 11. Juni 2023, 13:42 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.
Inhaltsverzeichnis
Inhalt
In diesem Tutorial:
- Reagierst du auf Kollisionen verschiedener
Actor
-Objekte - Nutzt du verschiedene Möglichkeiten, um auf Kollisionen zu reagieren und diese aufzulösen
- Entwickelst du einen Doodle Jump clone
Spielkonzept und grundlegender Aufbau
Ein Frosch soll fröhlich durch das Spiel springen, wann immer er die Chance hat, sich vom Boden abzustoßen. In der Scene FroggyJump
kann der Spieler ein Objekt der Klasse Frog
steuern. Zusätzlich geben Objekte der Klasse Platform
halt.
Damit ergibt sich das Codegerüst für das Spiel:
package eatutorials.collision;
import ea.*;
import ea.actor.*;
import ea.collision.CollisionEvent;
import ea.collision.CollisionListener;
import ea.event.KeyListener;
import java.awt.event.KeyEvent;
public class FroggyJump extends Scene {
private Frog frog;
public FroggyJump() {
frog = new Frog();
add(frog);
setGravity(Vector.DOWN.multiply(10));
Camera cam = getCamera();
cam.setFocus(frog);
cam.setOffset(new Vector(0, 4));
makePlatforms(10);
}
public static void main(String[] args) {
FroggyJump fj = new FroggyJump();
Game.start(400, 600, fj);
//Game.setDebug(true);
}
private void makePlatforms(int heightLevel) {
for(int i=0; i < heightLevel; i++) {
Platform platform = new Platform(5, 1);
platform.setPosition(0, i*4);
add(platform);
}
}
}
class Frog extends Image implements FrameUpdateListener {
private static float MAX_SPEED = 4;
public Frog() {
super("eatutorials/collision/assets/Jump (32x32).png", 25f);
setBodyType(BodyType.DYNAMIC);
setRotationLocked(true);
}
@Override
public void onFrameUpdate(float deltaSeconds) {
Vector velocity = this.getVelocity();
//A: Image direction
if(velocity.getX() < 0) {
setFlipHorizontal(true);
} else {
setFlipHorizontal(false);
}
//B: Horizontal Movement
if (Game.isKeyPressed(KeyEvent.VK_A)) {
if(velocity.getX() > 0) {
setVelocity(new Vector(0, velocity.getY()));
}
applyForce(Vector.LEFT.multiply(600));
} else if (Game.isKeyPressed(KeyEvent.VK_D)) {
if(velocity.getX() < 0) {
setVelocity(new Vector(0, velocity.getY()));
}
applyForce(Vector.RIGHT.multiply(600));
}
if(Math.abs(velocity.getX()) > MAX_SPEED) {
setVelocity(new Vector(MAX_SPEED * Math.signum(velocity.getX()) , velocity.getY()));
}
//C: Jump if possible
if (isGrounded()) {
this.setVelocity(new Vector(velocity.getX(), 0));
this.applyImpulse(Vector.UP.multiply(180));
}
}
}
class Platform extends Rectangle {
public Platform(float width, float height) {
super(width, height);
setBodyType(BodyType.STATIC);
}
}
Ein paar Erklärungen zum Codegerüst für FroggyJump
:
Physikalische Eigenschaften
Wie im Physics-Tutorial beschrieben, werden die physikalischen Eigenschaften der Spielobjekte und ihrer Umgebung bestimmt:
- Platformen sind statische Objekte: Sie ignorieren Schwerkraft und können nicht durch andere Objekte verschoben werden (egal mit wie viel Kraft der Frosch auf sie fällt).
- Der Frosch ist ein dynamisches Objekt: Er lässt sich von der Schwerkraft beeinflussen und wird von den statischen Platformen aufgehalten.
- In der Scene
FroggyJump
existiert eine Schwerkraft von 10 m/s^2. Sie wird mitsetGravity(Vector)
gesetzt.
Bewegung des Frosches
Die Bewegung des Frosches wird in jedem Frame kontrolliert. Wie im Game Loop Tutorial beschrieben, wird hierzu das Interface FrameUpdateListener
genutzt.
In jedem Frame wird die Bewegung des Frosches in dreierlei hinsicht kontrolliert:
- Teil A: Blickrichtung des Frosches: Das Bild des Frosches wird gespiegelt, falls er sich nach links bewegt.
- Teil B: Horizontale Bewegung des Frosches: Jeden Frame, in dem der Spieler den Frosch (per Tastendruck) nach links oder rechts steuern möchte, wird eine Bewegungskraft auf den Frosch angewendet. Wird der Frosch in die Gegenrichtung seiner aktuellen Bewegung gesteuert, wird seine horizontale Geschwindigkeit zuvor auf 0 gesetzt, um ein langsames Abbremsen zu verhindern. Das ermöglicht schnelle Reaktion auf Nutzereingabe und ein besseres Spielgefühl. Zusätzlich wird seine Geschwindigkeit auf die Konstante
MAX_SPEED
begrenzt. - Teil C: Springe, wenn möglich: Mit der Funktion
isGrounded()
bietet die Engine einen einfachen Test, um sicherzustellen, dass der Frosch Boden unter den Füßen hat. Wenn dies gegeben ist, wird ein Sprungimpuls auf den Frosch angewandt. Zuvor wird die vertikale Komponente seiner Geschwindigkeit auf 0 festgesetzt - das garantiert, dass der Frosch jedes mal die selbe Sprunghöhe erreicht.
Die Kamera folgt dem Frosch
Der Frosch soll stets sichtbar bleiben. Hierzu werden zwei Funktionen der Engine-Kamera genutzt:
- Der Frosch wird mit
Camera.setFocus(Actor)
in den Mittelpunkt der Kamera gesetzt. Sie folgt dem Frosch. - Gleichzeitig soll der Frosch nicht exakt im Mittelpunkt des Bildschirms sein: Weil das Spielziel ist, sich nach oben zu bewegen, braucht der Spieler mehr Blick nach oben als nach unten. Mit
Camera.setOffset(Vector)
wird die Kamera nach oben verschoben.
Durch Platformen Springen: Kollisionen kontrollieren
Das Interface CollisionListener
wurde bereits in seiner grundlegenden Form im Nutzereingabe-Tutorial benutzt.
CollisionListener
kann mehr als nur melden, wenn zwei Actor
-Objekte sich überschneiden. Um das FroggyJump
-Spiel zu implementieren, nutzen wir weitere Features.
Unser Frosch soll fähig sein, von unten "durch die Platform hindurch" zu springen. Von oben fallend soll er natürlich auf der Platform stehen bleiben.
Um diesen Effekt zu erzeugen, müssen Kollisionen zwischen Frosch und Platform unterschiedlich behandelt werden:
- Kollidiert der Frosch von unten, so soll die Kollision ignoriert werden. Er prallt so nicht von der Decke ab und kann weiter nach oben Springen.
- Kollidiert der Frosch von oben, so soll die Kollision normal aufgelöst werden, sodass er nicht durch den Boden fällt.
Hierzu stellt das CollisionEvent
-Objekt in der onCollision
-Methode Funktionen bereit.
class Platform extends Rectangle implements CollisionListener<Frog> {
public Platform(float width, float height) {
super(width, height);
setBodyType(BodyType.STATIC);
this.addCollisionListener(Frog.class, this);
}
@Override
public void onCollision(CollisionEvent<Frog> collisionEvent) {
float frogY = collisionEvent.getColliding().getPosition().getY();
if(frogY<this.getY()) {
collisionEvent.ignoreCollision();
}
}
}
Anregung zum Experimentieren
- Optische Verbesserung: Der Frosch ist bisher nur ein einzelnes Bild. Das ist nur ein kleiner Teil eines großen, kostenfrei verfügbaren Asset Packs von Pixel Frog. Das Tutorial zu Animationen mit mehreren Zuständen kann dir helfen, den Frosch lebendiger aussehen zu lassen.
- Weitere Spielobjekte: Neben Bällen, die herunterfallen, können mehr gefahren auf den Spieler warten: Fliegende Gegner, die auf einer horizontalen hin und her fliegen, Felsbrocken, die von Paltform zu Platform herunterfallen. Pick Ups können dem Spieler zusätzliche Punkte geben... Lass deiner Kreativität freien lauf!