Skip to content

impluse (Impuls)1

Um die Grundlagen der Physics Engine 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 Figuren auf:

public class DominosDemoBasic extends Scene
{
    /**
     * Der Boden, auf dem die Dominosteine stehen.
     */
    private Rectangle ground;

    /**
     * Der Ball, der die Dominosteine umwerfen soll.
     */
    private Circle ball;

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

    private void setupBasicObjects()
    {
        ground = new Rectangle(200, 2);
        ground.center(0, -5);
        ground.color("blue");
        ground.friction(1);
        add(ground);

        ball = new Circle(0.5);
        ball.color("red");
        ball.anchor(-17, -2);
        add(ball);
    }

    private void makeDominoes()
    {
        for (int i = 0; i < 20; i++)
        {
            Rectangle domino = new Rectangle(0.2, 3);
            domino.anchor(-10 + i * 1.3, -4);
            domino.color("white");
            add(domino);
        }
    }

    public static void main(String[] args)
    {
        Controller.instantMode(false);
        Controller.start(new DominosDemoBasic(), 1200, 300);
    }
}
Zum Java-Code: demos//home/runner/work/engine-pi/engine-pi/subprojects/demos/src/main/java/demos/docs/physics/impulse/DominosDemoBasic.java

Dieser Code baut ein einfaches Spielfeld auf: Ein roter Ball, ein paar Dominosteine, und ein blauer Boden.

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 soll nicht wie die Dominos umfallen. Dieser Actor hat 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();
        ball.makeDynamic();
        gravityOfEarth();
    }
Zum Java-Code: demos//home/runner/work/engine-pi/engine-pi/subprojects/demos/src/main/java/demos/docs/physics/impulse/DominosDemoFinal.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 setupLine()
    {
        line = new Line();
        line.end2.arrow(true).arrowSideLength(0.1);
        line.strokeWidth(0.01);
        line.dashed();
        line.dashPattern(0.03);
        line.color("gray");
        line.makeSensor();
        line.gravityScale(0);
        add(line);
    }
Zum Java-Code: demos//home/runner/work/engine-pi/engine-pi/subprojects/demos/src/main/java/demos/docs/physics/impulse/DominosDemoFinal.java

Visualisierung des Wurfwinkels

Wir wollen, dass eine Linie stets Ball und Maus verbindet. Die einfachste Methode hierzu ist, in jedem Einzelbild die Linie erneut an die Maus anzupassen. Dafür implementiert die Dominoes-Klasse das Interface FrameListener 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 onFrame(double pastTime)
    {
        line.end1(ball.center());
        line.end2(mousePosition());
    }
Zum Java-Code: demos//home/runner/work/engine-pi/engine-pi/subprojects/demos/src/main/java/demos/docs/physics/impulse/DominosDemoFinal.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);
        ball.applyImpulse(impulse);
    }
Zum Java-Code: demos//home/runner/work/engine-pi/engine-pi/subprojects/demos/src/main/java/demos/docs/physics/impulse/DominosDemoFinal.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.impulse;

import pi.Controller;
import pi.Scene;
import pi.actor.Circle;
import pi.actor.Line;
import pi.actor.Rectangle;
import pi.event.FrameListener;
import pi.event.MouseButton;
import pi.event.MouseClickListener;
import pi.graphics.geom.Vector;

// Go to file:///data/school/repos/inf/java/engine-pi/docs/manual/physics/impulse.md

// Go to file:///data/school/repos/inf/java/engine-pi/subprojects/jbox2d/testbed/src/main/java/org/jbox2d/testbed/tests/DominoTest.java
// Go to file:///data/school/repos/inf/java/engine-pi/subprojects/jbox2d/testbed/src/main/java/org/jbox2d/testbed/tests/DominoTower.java

// https://de.wikipedia.org/wiki/Domino: Professionelle Spielsteine
// haben im Allgemeinen eine Größe von ca. 56 × 28 × 13 mm.

/**
 * Das fertig Dominos-Demo.
 */
public class DominosDemoFinal extends Scene
        implements FrameListener, MouseClickListener
{
    /**
     * Der Boden, auf dem die Dominosteine stehen.
     */
    private Rectangle ground;

    /**
     * Der Ball, der die Dominosteine umwerfen soll.
     */
    private Circle ball;

    /**
     * Zeigt den Impuls an, mit dem der Ball gegen die Dominosteine katapultiert
     * wird.
     */
    private Line line;

    public DominosDemoFinal()
    {
        setupBasicObjects();
        makeDominoes();
        setupPhysics();
        setupLine();
    }

    private void setupBasicObjects()
    {
        ground = new Rectangle(20, 0.2);
        ground.center(0, -0.5);
        ground.color("blue");
        ground.friction(1);
        add(ground);

        ball = new Circle(0.15);
        ball.color("red");
        ball.anchor(-3, -0.2);
        add(ball);
    }

    private void makeDominoes()
    {
        for (int i = 0; i < 10; i++)
        {
            Rectangle domino = new Rectangle(0.13, 0.56);
            domino.anchor(-1 + i * 0.5, -0.4);
            domino.color("white");
            domino.makeDynamic();
            domino.friction(1);
            domino.density(5);
            add(domino);
        }
    }

    // >>
    private void setupPhysics()
    {
        ground.makeStatic();
        ball.makeDynamic();
        gravityOfEarth();
    }
    // <<

    // >>
    private void setupLine()
    {
        line = new Line();
        line.end2.arrow(true).arrowSideLength(0.1);
        line.strokeWidth(0.01);
        line.dashed();
        line.dashPattern(0.03);
        line.color("gray");
        line.makeSensor();
        line.gravityScale(0);
        add(line);
    }
    // <<

    // >>
    @Override
    public void onFrame(double pastTime)
    {
        line.end1(ball.center());
        line.end2(mousePosition());
    }
    // <<

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

    public static void main(String[] args)
    {
        Controller.instantMode(false);
        Controller.config.graphics.pixelPerMeter(128);
        Controller.start(new DominosDemoFinal(), 1200, 300);
    }
}
Zum Java-Code: demos//home/runner/work/engine-pi/engine-pi/subprojects/demos/src/main/java/demos/docs/physics/impulse/DominosDemoFinal.java


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