Game Engine Math

KWEngine, Teil 24: Zwischen zwei Welten wechseln

Oft hat das Spiel mehr als nur eine Welt (wobei der Begriff „Welt“ gleichbedeutend mit den üblichen Begriffen „Level“ und „Map“ ist). In diesem Tutorial werden wir mithilfe zweier Schaltflächen zwischen zwei Welten hin- und herschalten.
Wir brauchen also zwei Testwelten: Welt #1 und Welt #2. Und weil wir gerade dabei sind, legen wir auch noch eine Klasse für die Spielfigur (Player) an.

Code für Welt #1

using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class World1 : World
    {
        private HUDObjectText _buttonInWorld1;
        private Player _player;
    
        public override void Act()
        {
            // Wenn der Mauszeiger über dem Text liegt und gleichzeitig die linke Maustaste
            // gedrückt wird...
            if(_buttonInWorld1.IsMouseCursorOnMe() && Mouse.IsButtonPressed(MouseButton.Left))
            {  
                // ...erstelle eine neue Instanz der Klasse 'World2' und teile dem aktuellen 
                // Fenster mit, das diese jetzt als aktuelle Welt angezeigt werden soll:
                World2 w2 = new World2();
                Window.SetWorld(w2);
            }
        }

        public override void Prepare()
        {
            _buttonInWorld1 = new HUDObjectText("Switch to World #2");
            _buttonInWorld1.CenterOnScreen();
            AddHUDObject(_buttonInWorld1);

            _player = new Player();
            AddGameObject(_player);
        }
    }
} 

Code für Welt #2 (nahezu identisch mit Welt #1)

using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class World2 : World
    {
        private HUDObjectText _buttonInWorld2;
        private Player _player;
    
        public override void Act()
        {
            if(_buttonInWorld2.IsMouseCursorOnMe() && Mouse.IsButtonPressed(MouseButton.Left))
            {  
                World1 w1 = new World1();
                Window.SetWorld(w1);
            }
        }

        public override void Prepare()
        {
            _buttonInWorld2 = new HUDObjectText("Switch to World #1");
            _buttonInWorld2.CenterOnScreen();
            AddHUDObject(_buttonInWorld2);

            _player = new Player();
            AddGameObject(_player);
        }
    }
} 

Code für Spielfigur in beiden Welten (Player)

using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class Player : GameObject
    {
        // Hier speichert die Spielfigur ihren aktuellen Gesundheitszustand
        // und ihren aktuellen Rüstungswert:
        // Wird das Player-Objekt erstellt (z.B. in der Prepare()-Methode einer Welt),
        // erhält sie zunächst den Gesundheitswert 100 (bzw. 100%):
        private int _health = 100;
        private int _armor = 0;
    
        public override void Act()
        {
            // Hier würde der ganze Code entstehen, der die Steuerung
            // der Spielfigur ermöglicht.
        }

        public int GetHealth() { return _health; }
        public int GetArmor() { return _armor; }
    }
} 

Dieser oben aufgeführte Code würde schon ausreichen, um zwischen Welten hin- und herwechseln zu können. Bei einem Weltwechsel wird die „alte“ Welt immer vollständig gelöscht. Auch alle darin befindlichen Objekte (Spielfigur, Gegnerfiguren, usw.) werden vollständig gelöscht.

Man stelle sich jetzt vor, dass die Spielfigur während ihrer Zeit in Welt #1 aufgrund gegnerischer Treffer inzwischen weniger als 100% Gesundheit hat. Bei einem Weltenwechsel würde diese Spielfigur im Arbeitsspeicher abgeräumt und in Welt #2 als neue Spielfigur wieder erstellt werden. Doch bei der Erstellung einer neuen Spielfigur erhält diese auch wieder sämtliche Standardwerte (z.B. 100% Gesundheit).

Lösungsansatz #1: Direktes Weiterreichen von Informationen

Neue Klasse: WorldSwitchInfo

Bevor wir dem Anwendungsfenster mitteilen, dass es unsere zweite Welt anzeigen soll, sammeln wir alle Informationen, die wir übertragen wollen, in einem separaten Objekt. Es ist an dieser Stelle wichtig zu betonen, dass die dafür anzulegende Klasse selbst kein GameObject ist. Die Klasse dient lediglich als Container für unsere Infos:

using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class WorldSwitchInfo
    {
        public int PlayerHealth;
        public int PlayerArmor;

        public WorldSwitchInfo()
        {
            PlayerHealth = 100;
            PlayerArmor = 0;
        }

        public WorldSwitchInfo(int health, int armor)
        {
            PlayerHealth = health;
            PlayerArmor = armor;
        }
    }
} 
Verwendung der neuen Klasse in den beiden World-Klassen
using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class World1 : World
    {
        private HUDObjectText _buttonInWorld1;
        private Player _player;
        private WorldSwitchInfo _info;
    
        public World1(WorldSwitchInfo info)
        {
            _info = info; // Ab jetzt hat die Instanz Zugriff auf die Daten in _info
        }

        public override void Act()
        {
            if(_buttonInWorld1.IsMouseCursorOnMe() && Mouse.IsButtonPressed(MouseButton.Left))
            {  
                World2 w2 = new World2(new WorldSwitchInfo(_player.GetHealth(), _player.GetArmor()));
                Window.SetWorld(w2);
            }
        }

        public override void Prepare()
        {
            _buttonInWorld1 = new HUDObjectText("Switch to World #2");
            _buttonInWorld1.CenterOnScreen();
            AddHUDObject(_buttonInWorld1);

            _player = new Player();
            AddGameObject(_player);
        }
    }
} 
using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class World2 : World
    {
        private HUDObjectText _buttonInWorld2;
        private Player _player;
        private WorldSwitchInfo _info;
    
        public World2(WorldSwitchInfo info)
        {
            _info = info; // Ab jetzt hat die Instanz Zugriff auf die Daten in _info
        }

        public override void Act()
        {
            if(_buttonInWorld1.IsMouseCursorOnMe() && Mouse.IsButtonPressed(MouseButton.Left))
            {  
                World1 w1 = new World1(new WorldSwitchInfo(_player.GetHealth(), _player.GetArmor()));
                Window.SetWorld(w1);
            }
        }

        public override void Prepare()
        {
            _buttonInWorld2 = new HUDObjectText("Switch to World #1");
            _buttonInWorld2.CenterOnScreen();
            AddHUDObject(_buttonInWorld2);

            _player = new Player();
            AddGameObject(_player);
        }
    }
} 

Lösungsansatz #2: Sicherung in einer globalen Klasse

Statt ein Info-Objekt zu erzeugen und zwischen den Welten hin- und herzureichen könnte man die beim Weltwechsel benötigten Informationen auch in eine globale, statische Klasse speichern. Dieser Ansatz ist leichter, birgt aber die Gefahr, dass man anfängt, alle Informationen global in dieser Klasse zu speichern. Dies führt langfristig zu unübersichtlicherem und schlechter wartbarem Code. Aus diesem Grund sollten wirklich nur die absolut nötigsten Informationen dort abgelegt werden. Diese Klasse sollte auf gar keinen Fall für die Steuerung von Gameplay-Mechanismen genutzt werden.

using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public static class Global
    {
        public static int PlayerHealth = 100;
        public static int PlayerArmor = 0;
    }
} 
Verwendung der globalen Klasse in beiden World-Klassen
using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class World1 : World
    {
        private HUDObjectText _buttonInWorld1;
        private Player _player;
    
        public override void Act()
        {
            if(_buttonInWorld1.IsMouseCursorOnMe() && Mouse.IsButtonPressed(MouseButton.Left))
            {  
                Global.PlayerHealth = _player.GetHealth();
                Global.PlayerArmor  = _player.GetArmor();
                
                World2 w2 = new World2();
                Window.SetWorld(w2);
            }
        }

        public override void Prepare()
        {
            _buttonInWorld1 = new HUDObjectText("Switch to World #2");
            _buttonInWorld1.CenterOnScreen();
            AddHUDObject(_buttonInWorld1);

            _player = new Player();

            // Hier könnte jetzt der Zugriff auf die globalen Werte stattfinden:
            int health = Global.PlayerHealth;
            int armor  = Globa.PlayerArmor;

            AddGameObject(_player);
        }
    }
} 
using KWEngine3;
using KWEngine3.GameObjects;
using KWEngine3.Helper;
using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace WorldSwitchTutorial
{
    public class World2 : World
    {
        private HUDObjectText _buttonInWorld2;
        private Player _player;
    
        public override void Act()
        {
            if(_buttonInWorld2.IsMouseCursorOnMe() && Mouse.IsButtonPressed(MouseButton.Left))
            {  
                Global.PlayerHealth = _player.GetHealth();
                Global.PlayerArmor  = _player.GetArmor();
                
                World1 w2 = new World1();
                Window.SetWorld(w1);
            }
        }

        public override void Prepare()
        {
            _buttonInWorld2 = new HUDObjectText("Switch to World #1");
            _buttonInWorld2.CenterOnScreen();
            AddHUDObject(_buttonInWorld2);

            _player = new Player();

            // Hier könnte jetzt der Zugriff auf die globalen Werte stattfinden:
            int health = Global.PlayerHealth;
            int armor  = Globa.PlayerArmor;
 
            AddGameObject(_player);
        }
    }
} 

Beitrag veröffentlicht

in

von

Schlagwörter:

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.