Game Engine Math

KWEngine, Teil 21: 2D-Objekte darstellen

Eigentlich ist die Engine für 3D-Objekte konzipiert und optimiert, aber wenn Sie möchten, können Sie auch 2D-Objekte darstellen. Dies geschieht, indem Sie als 3D-Modell das bereits in der Engine enthaltene Modell „KWQuad“ verwenden.
Ein KWQuad ist einfach nur ein flaches Quadrat, das intern jedoch eine unsichtbare 3D-Hitbox verwendet, damit es auch mit 3D-Objekten interagieren kann.

Für animierte 2D-Objekte brauchen Sie die einzelnen Animationsschritte in einer größeren Textur – einem sogenannten „Spritesheet„:

Beispiel für eine Spritesheet-Textur

Im oben dargestellten Spritesheet sind mehrere Stufen von Animationen zu sehen:

  • In der ersten Zeile befinden sich 10 Animationsstufen für die Idle-Animation,
  • in der zweiten Zeile befinden sich 10 Animationsstufen für die Lauf-Animation und
  • in der letzten Zeile befinden sich 10 Animationsstufen für die Sprung-Animation.

Zunächst legen wir ein Player-Objekt in unserer Welt an, um das Objekt anzuzeigen

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

public class GameWorld : World
{
    public override void Act()
    {

    }
    
    public override void Prepare()
    {
        // Um den 3D-Effekt weitestgehend zu minimieren, 
        // wählen Sie ein Blickfeld von nur 10° und 
        // platzieren die Kamera etwas weiter weg:
        SetCameraFOV(10);
        SetCameraPosition(0, 0, 100);

        // Legen Sie dann Ihre Spielfigur an und weisen ihr
        // das Modell "KWQuad" zu:
        Player p = new Player();
        p.SetModel("KWQuad");

        // Setzen Sie jetzt noch die entsprechende Textur:
        p.SetTexture("./texturefolder/spritesheet.png");
        // Da nicht die gesamte Textur (mit allen 30 Animationsstufen)
        // auf dem Objekt dargestellt werden sollen, müssen wir festlegen,
        // dass wir nur 1/10 der Textur in der Breite anzeigen (weil
        // es 10 Stufen in der Breite gibt) und nur 1/3 der Höhe anzeigen
        // wollen, weil die Textur die Animationsstufen in 3 Zeilen 
        // aufteilt:
        p.SetTextureRepeat(1f / 10f, 1f / 3f);

        // Normalerweise werden Änderungen an den Texturwerten (Repeat & Offset)
        // für das Zeichnen des aktuellen Bilds gewichtet interpoliert (also ein 
        // Mischwert aus dem aktuellen und dem letzten Frame erzeugt), um für 
        // das Auge flüssiger aussehende Bewegungen zu erzeugen. 
        // Bei Spritesheets sorgt dieses Verhalten jedoch für eine fehlerhaft 
        // Darstellung. Um das Verhalten für das jeweilige Objekt anzupassen,
        // setzen Sie dessen Eigenschaft 'BlendTextureStates' auf 'false':
        p.BlendTextureStates = false;

        AddGameObject(p);
    }
}

In der Player-Klasse schalten wir dann testweise durch die Lauf-Animation. Weil wir aber nicht genug Animationsschritte haben (in unserem Beispiel nur 10 Stück pro Animation), können wir nicht jedes Mal beim Aufruf der Act()-Methode einen neuen Animationsschritt anzeigen lassen. Bei 240 Act()-Aufrufen pro Sekunde würde die Animation viel zu schnell abgespielt werden.

Also muss sich die Spielfigur in einem eigenen Feld (bzw. einer Eigenschaft) merken, wann zuletzt die Animation gewechselt wurde und dann prüfen, ob seitdem genug Millisekunden vergangen sind, um den nächsten Animationsschritt anzuzeigen.
Und um sich merken zu können, welche der 10 Animationen dran ist, muss sie dafür ebenfalls ein eigenes Feld anlegen:

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

public class Player : GameObject
{
    // Lege ein Feld an, so dass der Player sich merken kann,
    // wann zuletzt ein Animationsschritt gewechselt wurde:
    private float _lastAnimationTime = 0.0f;

    // Lege ein weiteres Feld an, in dem sich der Player merkt,
    // welchen der 10 Schritte er gerade anzeigen soll:
    private int _animationStep = 0;

    public override void Act()
    {
        // Ziehe den Zeitpunkt des letzten Schrittwechsels
        // von der aktuellen Weltzeit ab, um herauszufinden,
        // wie viele Sekunden seit dem letzten Mal vergangen sind.
        // Hier testen wir, ob seitdem mindestens 33 Millisekunden
        // vergangen sind:
        if(WorldTime - _lastAnimationTime >= 0.033f)
        {
            // Aktualisiere den Zeitstempel, weil wir jetzt
            // die Animation wechseln werden:
            _lastAnimationTime = WorldTime;

            // Weil genug Zeit vergangen ist, erhöhe den 
            // Animationsschritt um 1 und wenn er 10 erreicht hat,
            // setze ihn wieder zurück auf 0:
            _animationStep = _animationStep + 1;
            if(_animationStep >= 10)
            {
                _animationStep = 0;
            }

            // Verschiebe die Textur so, dass der nächste 
            // Animationsschritt angezeigt wird:
            SetTextureOffset(
                _animationStep, // X-Verschiebung setzen,
                1               // Y-Verschiebung hier immer 1, weil die Lauf-
                                // Animation in der zweiten Zeile liegt
            );
        }
    }
}


Beitrag veröffentlicht

in

von

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.