Skip to content

impluse (Impuls)1

Um die Grundlagen der Engine Pi Physics zu testen, bauen wir eine einfache Kettenreaktion: Ein Ball wird gegen eine Reihe von Dominos geworfen.

Bevor wir die Physik einschalten, bauen wir das Spielfeld mit allen Objekten auf:

public class ImpulseDemo extends Scene
        implements FrameUpdateListener, MouseClickListener
{
    private Rectangle ground;

    private Rectangle wall;

    private Circle ball;

    private Rectangle angle;

    public ImpulseDemo()
    {
        setupBasicObjects();
        makeDominoes();
    }

    private void setupBasicObjects()
    {
        // Boden auf dem die Dominosteine stehen
        ground = new Rectangle(200, 2);
        ground.center(0, -5);
        ground.color("white");
        add(ground);
        // Der Ball, der die Dominosteine umwerfen soll.
        ball = new Circle(0.5);
        ball.color("red");
        ball.position(-10, -2);
        add(ball);
        // Eine senkrechte Wand links der Simulation
        wall = new Rectangle(1, 40);
        wall.position(-14, -4);
    }

    private void makeDominoes()
    {
        for (int i = 0; i < 20; i++)
        {
            Rectangle domino = new Rectangle(0.4, 3);
            domino.position(i * 3 * 0.4, -4);
            domino.makeDynamic();
            domino.color("blue");
            add(domino);
        }
    }
}

Dieser Code baut ein einfaches Spielfeld auf: Ein roter Ball, ein paar Dominosteine, und ein weißer Boden mit Wand.

Das Spielbrett ist aufgebaut, allerdings passiert noch nichts interessantes. Zeit für Physik!

Wir erwarten verschiedenes Verhalten von den physikalischen Objekten. Dies drückt sich in verschiedenen BodyTypes aus:

  • Der Ball und die Dominos sollen sich verhalten wie normale physische Objekte: Der Ball prallt an den Dominos ab und die Steine fallen um. Diese Actors haben einen dynamischen Körper.
  • Aber der Boden und die Wand sollen nicht wie die Dominos umfallen. Egal mit wie viel Kraft ich den Ball gegen die Wand werfe, sie wird niemals nachgeben. Diese Actors haben einen statischen Körper.

Mit der Methode Actor.setBodyType(BodyType) wird das grundlegende Verhalten eines Actors bestimmt. Zusätzlich wird mit Scene.setGracity(Vector) eine Schwerkraft gesetzt, die auf den Ball und die Dominos wirkt. Jetzt wirkt Schwerkraft auf die dynamischen Objekte und der statische Boden hält den Fall

In einer setupPhysics()-Methode werden die Body Types für die Actors gesetzt und die Schwerkraft (standardmäßige 9,81 m/s^2, gerade nach unten) aktiviert:

    private void setupPhysics()
    {
        ground.makeStatic();
        wall.makeDynamic();
        ball.makeDynamic();
        gravityOfEarth();
    }
Zum Java-Code: demos/docs/physics/ImpulseDemo.java

Zusätzlich werden die Dominos in makeDominoes() mit domino.makeDynamic(); eingerichtet.

Jetzt wirkt Schwerkraft auf die dynamischen Objekte und der statische Boden hält den Fall

Dynamische und statische Körper sind die essentiellsten Body Types in der Engine, allerdings nicht die einzigen. Du findest einen Umriss aller Body Types in der Dokumentation von BodyType und eine vergleichende Übersicht in der dedizierten Wikiseite Den Ball Werfen Mit einem Methodenaufruf fliegt der Ball

Zeit, die Dominos umzuschmeißen! Die Methode applyImpulse(Vector) erlaubt, den Ball physikalisch korrekt zu 'werfen'.

Mit der Zeile ball.applyImpulse(new Vector(15, 12)); kannst der erste Ballwurf getestet werden.

Mit einem Methodenaufruf fliegt der Ball

Um hieraus eine Spielmechanik zu bauen, soll der Spieler Richtung und Stärke des Wurfes mit der Maus kontrollieren können: Per Mausklick wird der Ball in Richtung des Mauscursors katapultiert. Das Angle-Objekt hilft dem Spieler

Hierzu wird ein weiteres Rechteck angle eingeführt, das die Richtung des Impulses markiert:

    private void setupAngle()
    {
        angle = new Rectangle(1, 0.1);
        angle.color(Color.GREEN);
        add(angle);
    }
Zum Java-Code: demos/docs/physics/ImpulseDemo.java

Visualisierung des Wurfwinkels

Wir wollen, dass das Rechteck stets Ball und Maus verbindet. Die einfachste Methode hierzu ist, in jedem Frame das Rechteck erneut an die Maus anzupassen. Dafür implementiert die Dominoes-Klasse das Interface FrameUpdateListener und berechnet frameweise anhand der aktuellen Mausposition die korrekte Länge und den korrekten Winkel, um die visuelle Hilfe richtig zu positionieren:

    @Override
    public void onFrameUpdate(double pastTime)
    {
        Vector mousePosition = mousePosition();
        Vector ballCenter = ball.center();
        Vector distance = ballCenter.distance(mousePosition);
        angle.position(ball.center());
        angle.width(distance.length());
        double rot = Vector.RIGHT.angle(distance);
        angle.rotation(rot);
    }
Zum Java-Code: demos/docs/physics/ImpulseDemo.java

Zuletzt muss der Ballwurf bei Mausklick umgesetzt werden. Hierzu wird noch das Interface MouseClickListener implementiert:

    @Override
    public void onMouseDown(Vector position, MouseButton button)
    {
        Vector impulse = ball.center().distance(position).multiply(5);
        ball.applyImpulse(impulse);
    }
Zum Java-Code: demos/docs/physics/ImpulseDemo.java

Der komplette Code

/*
 * Source: https://github.com/engine-alpha/tutorials/blob/master/src/eatutorials/physics/Dominoes.java
 *
 * Engine Alpha ist eine anfängerorientierte 2D-Gaming Engine.
 *
 * Copyright (c) 2011 - 2024 Michael Andonie and contributors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package demos.docs.physics;

import java.awt.Color;

import pi.Controller;
import pi.Scene;
import pi.Vector;
import pi.Circle;
import pi.Rectangle;
import pi.event.FrameUpdateListener;
import pi.event.MouseButton;
import pi.event.MouseClickListener;

public class ImpulseDemo extends Scene
        implements FrameUpdateListener, MouseClickListener
{
    private Rectangle ground;

    private Rectangle wall;

    private Circle ball;

    private Rectangle angle;

    public ImpulseDemo()
    {
        setupBasicObjects();
        setupPhysics();
        setupAngle();
        makeDominoes();
    }

    private void setupBasicObjects()
    {
        // Boden auf dem die Dominosteine stehen
        ground = new Rectangle(200, 2);
        ground.center(0, -5);
        ground.color("white");
        add(ground);
        // Der Ball, der die Dominosteine umwerfen soll.
        ball = new Circle(0.5);
        ball.color("red");
        ball.position(-10, -2);
        add(ball);
        // Eine senkrechte Wand links der Simulation
        wall = new Rectangle(1, 40);
        wall.position(-14, -4);
    }

    private void setupAngle()
    {
        angle = new Rectangle(1, 0.1);
        angle.color(Color.GREEN);
        add(angle);
    }

    private void setupPhysics()
    {
        ground.makeStatic();
        wall.makeDynamic();
        ball.makeDynamic();
        gravityOfEarth();
    }

    private void makeDominoes()
    {
        for (int i = 0; i < 20; i++)
        {
            Rectangle domino = new Rectangle(0.4, 3);
            domino.position(i * 3 * 0.4, -4);
            domino.makeDynamic();
            domino.color("blue");
            add(domino);
        }
    }

    @Override
    public void onFrameUpdate(double pastTime)
    {
        Vector mousePosition = mousePosition();
        Vector ballCenter = ball.center();
        Vector distance = ballCenter.distance(mousePosition);
        angle.position(ball.center());
        angle.width(distance.length());
        double rot = Vector.RIGHT.angle(distance);
        angle.rotation(rot);
    }

    @Override
    public void onMouseDown(Vector position, MouseButton button)
    {
        Vector impulse = ball.center().distance(position).multiply(5);
        ball.applyImpulse(impulse);
    }

    public static void main(String[] args)
    {
        Controller.instantMode(false);
        Controller.start(new ImpulseDemo(), 800, 300);
    }
}
Zum Java-Code: demos/docs/physics/ImpulseDemo.java


  1. Der Abschnitt stammt aus dem Engine-Alpha-Wiki: https://engine-alpha.org/wiki/v4.x/Physics