Worklog RK Rocket #1: Basics

Aktuell arbeite ich relativ viel an der Integration von Direct2D in Seeing#. Diese API ist für mich der interessanteste Weg, 2D-Funktionen in eine auf Direct3D basierende 3D-Engine zu bringen. Man arbeitet mit aus anderen APIs bekannten einfachen Funktionen wie DrawRect, DrawLine oder DrawBitmap, ist aber vollständig hardwarebeschleunigt unterwegs und man kann direkt auf Texturen zeichnen, welche auf ein 3D-Objekt geklatscht werden. Oder schlich in den Vorder- bzw. Hintergrund der 3D-Ansicht. RK Rocket ist ein kleines Spiel, anhand dem ich die 2D-Funktionen in Seeing# integriere und direkt teste. Es ist ein einfaches kleines Game, inspiriert vom Rockout-Beispiel der Programmiersprache BlitzMax [1]. Es wird als UWP App erstellt und ist somit auf möglichst allen Windows 10 Geräten lauffähig.

Über das Spiel

Das Spiel selbst ist sehr simpel. Am oberen Bildschirmrand entstehen nach und nach mehrere Reihen an Blöcken, welche der Spieler abschießen muss. Problem ist, dass die Geschosse auch wieder zurück fliegen und den Spieler selbst treffen können, wodurch er selbst Lebenspunkte verliert. Wenn die Lebenspunkte aufgebraucht oder einer der Blöcke relativ weit unten angekommen ist, ist das Spiel vorbei.

RK Rocket

Übersicht über das Coding

Aktuell befindet sich das Coding von RK Rocket direkt in der Projektmappe von Seeing# auf Github [2]. Die für die Spielelogik und dem Rendering relevanten Klassen befinden sich im Unterordner Game. Die Spielelogik ist auf verschiedene Klassen aufgeteilt, die wichtigsten sind im nachfolgenden Screenshot markiert. Jede dieser Klassen ist hierbei verantwortlich für die eigene Animationslogik und das Rendering. Die Kollissionsprüfung, also die Erkennung, ob ein Geschoss (ProjectileEntity) einen der Blöcke getroffen hat, ist in der Klasse CollisionSystem implementiert. Die Kommunikation zwischen den verschiedenen Spiele-Objekten erfolgt über Nachrichten, welche im Unterordner Messages definiert sind.

Rendering

Das Rendering erfolgt folgendermaßen: Seeing# stellt die API von Direct2D über einen eigenen Namensraum zur Verfügung [3]. Diese Klassen sind überwiegend Wrapper rund um Direct2D und haben den Sinn, dass SharpDX nicht direkt von der Spiele-Assembly aus referenziert werden muss. Weiterhin werden die Besonderheiten von Seeing# beachtet, beispielsweise eine Ausgabe auf mehrere Monitore, das Rendering über mehrere Grafikkarten hinweg oder die Verwaltung der Hardware-Ressourcen. In oben genannten Spieleobjekten werden dann die jeweils nötigen Funktionen von Seeing# aufgerufen, nachfolgend z. B. die Render-Logik für die Blöcke.

/// <summary>
/// Contains all 2D rendering logic for this object.
/// </summary>
/// <param name="renderState">The current state of the renderer.</param>
protected override void OnRender_2DOverlay(RenderState renderState)
{
    Graphics2D graphics = renderState.Graphics2D;

    RectangleF destRectangle = new RectangleF(
        m_position.X - Constants.BLOCK_VPIXEL_WIDTH / 2f,
        m_position.Y - Constants.BLOCK_VPIXEL_HEIGHT / 2f,
        Constants.BLOCK_VPIXEL_WIDTH,
        Constants.BLOCK_VPIXEL_HEIGHT);

    Matrix3x2 prevMatrix = Matrix3x2.Identity;
    if(m_rotation > 0f)
    {
        // Apply transform based on local rotation
        prevMatrix = graphics.Transform;
        graphics.Transform = Matrix3x2.CreateRotation(m_rotation, m_position) * prevMatrix;
    }
    try
    {
        graphics.DrawBitmap(
            m_blockBitmap, destRectangle,
            opacity: m_opacity,
            interpolationMode: BitmapInterpolationMode.Linear);
    }
    finally
    {
        // Restore previous transform
        if (m_rotation > 0f)
        {
            graphics.Transform = prevMatrix;
        }
    }
}

Grundsätzlich wird hier zunächst die Größe des Blocks berechnet und irgendwann DrawBitmap aufgerufen. Falls ein Block getroffen wurde, fällt dieser nach unten und rotiert dabei leicht (Siehe Screenshot). Aus diesem Grund kann beim Rendering auch eine Rotation mittels einer Transformationsmatrix an das Graphics-Objekt übergeben werden. Abschließend wird beim DrawImage noch ein Transparenz-Wert übergeben, denn wie im Screenshot ersichtlich, werden herabfallende Blöcke mit höherer Transparenz gerendert. Für Direct2D ist das dank der Hardwarebeschleunigung performancetechnisch glücklicherweise kein Problem.

Verweise

  1. http://www.blitzbasic.com/Products/blitzmax.php
  2. https://github.com/RolandKoenig/SeeingSharp/tree/master/Games/RKRocket
  3. https://github.com/RolandKoenig/SeeingSharp/tree/master/SeeingSharp.Multimedia/Drawing2D

Schreibe einen Kommentar

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