Skip to content

Scene (Szene)1

Ein Spiel hat oftmals mehrere verschiedene „Teile“, zwischen denen der Spieler während des Spielens wechselt. Zum Beispiel gibt es neben der Hauptdarstellung, Pausenmenüs, Inventare, Hauptmenüs, etc. Es wäre unnötig komplex, für den Wechsel zwischen diesen Szenen stets alle grafischen Objekte zu zerstören und wieder neu aufzubauen. Stattdessen werden alle grafischen Objekte in einer Scene hinzugefügt. Dies passiert - wie in den vorigen Tutorials - über die Methode add(...).

Über die Klasse Controller kann schnell zwischen Szenen gewechselt werden. Dazu gibt es die Methode Controller.transitionToScene(Scene).

Szenen in der Engine: Beispiel mit Pausenmenü

Ein Pausenmenü

Das folgende Beispiel enthält zwei Szenen: Eine einfache Animation und ein Pausenmenü. Ein Wechsel zwischen Hauptszene zu Pausenmenü und wieder zurück

Ein Wechsel zwischen Hauptszene zu Pausenmenü und wieder zurück

Die zwei Szenen

Die Hauptszene ist MainScene. Hier könnte ein Game Loop für ein Spiel stattfinden. Dieses Tutorial zeigt stattdessen eine kleine Animation.

import java.awt.event.KeyEvent;

import pi.Controller;
import pi.Rectangle;
import pi.Scene;
import pi.Text;
import pi.animation.CircleAnimation;
import pi.event.KeyStrokeListener;
import pi.graphics.geom.Vector;

public class MainScene extends Scene implements KeyStrokeListener
{
    private final PauseMenu pauseMenu;

    public MainScene()
    {
        pauseMenu = new PauseMenu(this);
        Rectangle toAnimate = new Rectangle(5, 2);
        toAnimate.center(0, -5);
        toAnimate.color("orange");
        CircleAnimation animation = new CircleAnimation(toAnimate,
                new Vector(0, 0), 8, true, true);
        addFrameUpdateListener(animation);
        add(toAnimate);
        addKeyStrokeListener(this);
        Text info = new Text("Pause mit P");
        info.center(-7, -5);
        add(info);
    }

    @Override
    public void onKeyDown(KeyEvent keyEvent)
    {
        if (keyEvent.getKeyCode() == KeyEvent.VK_P)
        {
            gotoPause();
        }
    }

    private void gotoPause()
    {
        Controller.transitionToScene(pauseMenu);
    }

    public static void main(String[] args)
    {
        Controller.instantMode(false);
        Controller.start(new MainScene(), 600, 400);
    }
}
Zum Java-Code: demos/subprojects/demos/src/main/java/demos/docs/main_classes/scene/tutorial/MainScene.java

Die zweite Szene heißt PauseMenu. In ihr gibt es eine Textbotschaft und einen kleinen Knopf, um das Menü wieder zu verlassen.

import pi.Scene;
import pi.Text;
import pi.graphics.geom.Vector;

public class PauseMenu extends Scene
{
    public PauseMenu(Scene mainScene)
    {
        MenuItem back = new MenuItem(mainScene, new Vector(0, -5), "Zurück");
        add(back, back.label);
        Text headline = new Text("Mach mal Pause.");
        headline.height(2);
        headline.center(0, 3);
        add(headline);
    }
}
Zum Java-Code: demos/subprojects/demos/src/main/java/demos/docs/main_classes/scene/tutorial/PauseMenu.java

Die Haupt-Szene wird per Knopfdruck pausiert. Wird der P-Knopf gedrückt, wird die Transition ausgeführt:

    private void gotoPause()
    {
        Controller.transitionToScene(pauseMenu);
    }
Zum Java-Code: demos/subprojects/demos/src/main/java/demos/docs/main_classes/scene/tutorial/MainScene.java

Das Pausenmenü wird statt mit Tastatur per Mausklick geschlossen. Im internen Steuerelement MenuItem wird dafür die entsprechende Methode aufgerufen, wann immer ein Mausklick auf dem Element landet - dies wird durch die Methode contains(Vector) geprüft:

    @Override
    public void onMouseDown(Vector clickLoc, MouseButton mouseButton)
    {
        if (contains(clickLoc))
        {
            Controller.transitionToScene(mainScene);
        }
    }
Zum Java-Code: demos/subprojects/demos/src/main/java/demos/docs/main_classes/scene/tutorial/MenuItem.java

Kosmetische Kleinigkeiten

In der Hauptszene findet eine interpolierte Rotationsanimation statt. Diese rotiert ein oranges Rechteck wiederholend um den Punkt (0|0). Eine volle Rotation im Uhrzeigersinn dauert 8 Sekunden.

        Rectangle toAnimate = new Rectangle(5, 2);
        toAnimate.center(0, -5);
        toAnimate.color("orange");
        CircleAnimation animation = new CircleAnimation(toAnimate,
                new Vector(0, 0), 8, true, true);
        addFrameUpdateListener(animation);
        add(toAnimate);
Zum Java-Code: demos/subprojects/demos/src/main/java/demos/docs/main_classes/scene/tutorial/MainScene.java

Das Pausenmenü hat einen Hover-Effekt. Hierzu wird in jeden Einzelbild überprüft, ob die Maus derzeit innerhalb des Steuerelementes liegt und abhängig davon die Rechtecksfarbe ändert. Hierzu wird die Methode Controller.getMousePositionInCurrentScene() genutzt:

    @Override
    public void onFrameUpdate(double pastTime)
    {
        if (contains(Controller.mousePosition()))
        {
            color("blue");
        }
        else
        {
            color("blueGreen");
        }
    }
Zum Java-Code: demos/subprojects/demos/src/main/java/demos/docs/main_classes/scene/tutorial/MenuItem.java

Kompletter Code MenuItem

import pi.Controller;
import pi.Rectangle;
import pi.Scene;
import pi.Text;
import pi.event.FrameUpdateListener;
import pi.event.MouseButton;
import pi.event.MouseClickListener;
import pi.graphics.geom.Vector;

public class MenuItem extends Rectangle
        implements MouseClickListener, FrameUpdateListener
{
    final Text label;

    private final Scene mainScene;

    public MenuItem(Scene mainScene, Vector center, String labelText)
    {
        super(10, 1.5);
        this.mainScene = mainScene;
        label = new Text(labelText);
        label.layerPosition(1);
        label.color("black");
        label.center(center);
        layerPosition(0);
        color("blueGreen");
        center(center);
    }

    @Override
    public void onMouseDown(Vector clickLoc, MouseButton mouseButton)
    {
        if (contains(clickLoc))
        {
            Controller.transitionToScene(mainScene);
        }
    }

    @Override
    public void onFrameUpdate(double pastTime)
    {
        if (contains(Controller.mousePosition()))
        {
            color("blue");
        }
        else
        {
            color("blueGreen");
        }
    }
}
Zum Java-Code: demos/subprojects/demos/src/main/java/demos/docs/main_classes/scene/tutorial/MenuItem.java

Anmerkungen und Beobachtungen

Die Kreisrotation in der Hauptszene geht nicht weiter, solange das Pausenmenü die aktive Szene ist. Dies liegt daran, dass die Animation als FrameUpdateListener in der Hauptszene angemeldet wurde (addFrameUpdateListener(animation)). Alle Beobachter einer Szene können nur dann aufgerufen werden, wenn die Szene aktiv ist. Deshalb lässt sich das Pausenmenü nicht durch drücken von P beenden. Der KeyStrokeListener, der bei Druck von P zum Pausenmenü wechselt, ist in der Hauptszene angemeldet.


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