Dynamische Erstellung von Spielkarten mithilfe von Code für Game Jams

Was Sie erstellen werden

Dieses Tutorial unterscheidet sich von meinen früheren Tutorials, da es sich auf Game Jam und Game Prototyping, insbesondere Kartenspiele, konzentriert. Wir werden in Unity ein 2D-Kartenspiel ohne Code erstellen.

1. Bestandteile eines Spielkartendecks

Ein Spielkartendeck hat insgesamt 52 Karten mit 13 Karten mit jeweils 4 verschiedenen Symbolen. Um eines mit Code zu erstellen, müssen wir diese 4 Symbole, die abgerundete rechteckige Basis für die Karte und das Design auf der Rückseite der Karte erstellen.

Das Design auf der Rückseite der Karte kann ein beliebiges abstraktes Muster sein, und es gibt zahlreiche Möglichkeiten, eines zu erstellen. Wir erstellen ein einfaches kachelförmiges Muster, das dann gekachelt wird, um das Design zu erstellen. Wir haben kein spezielles Design für die A-, K-, Q- und J-Karten.

2. Alternative Lösungen

Bevor wir beginnen, muss ich erwähnen, dass es einfachere Lösungen gibt, mit denen wir ein Kartendeck erstellen können. Einige davon sind unten aufgeführt.

  1. Es liegt auf der Hand, für alle Designs vorgerenderte Kunst zu verwenden.
  2. Weniger offensichtlich ist die Verwendung einer Schriftart, die alle notwendigen Symbole enthält. Wir können die genannte Schriftart auch in eine Bitmap-Schriftart umwandeln, um Zeichenaufrufe zu reduzieren und die Leistung zu steigern.

Die font-basierte Lösung ist die schnellste und einfachste Lösung, wenn Sie schnelle Prototypen erstellen möchten.

3. Erstellen von Texturen zur Laufzeit

Der erste Schritt besteht darin, zu lernen, wie man eine erstellt Textur2D mit Code, der dann verwendet werden kann, um eine Sprite in der Einheit. Der folgende Code zeigt die Erstellung einer leeren Textur von 256x256.

Texture2D-Textur = new Texture2D (256, 256, TextureFormat.ARGB4444, false); texture.filterMode = FilterMode.Trilinear; texture.wrapMode = TextureWrapMode.Clamp; Textur.Apply ();

Die Idee ist, alle Designs auf die Textur zu zeichnen, bevor wir das verwenden Sich bewerben Methode. Wir können Designs mit der Pixel Pixel für Pixel auf die Textur zeichnen SetPixel wie unten gezeigt.

texture.SetPixel (x, y, Color.white);

Wenn wir zum Beispiel die gesamte Textur mit einer Farbe ausfüllen möchten, können wir eine Methode wie diese verwenden.

private void PaintRectangle (Texture2D-Textur, Rect rectBounds, Color-Farbe) für (int i = (int) rectBounds.x; i

Sobald wir eine haben Textur2D erstellt, können wir damit eine erstellen Sprite auf dem Bildschirm angezeigt werden.

Sprite Sprite = Sprite.Create (Textur, neues Rect (0.0f, 0.0f, Texturbreite, Texturhöhe), neuer Vector2 (0.5f, 0.5f), 1);

Der komplizierte Teil bei all dem ist das Erstellen der notwendigen Designs für die Textur.

4. Erstellen der Herzform

Bei der Erstellung der Herzform gibt es viele verschiedene Ansätze, darunter einige komplizierte Gleichungen sowie einfaches Mischen von Formen. Wir verwenden die Methode der Mischung von Formen wie unten gezeigt, insbesondere die mit dem Dreieck.

Wie Sie beobachtet haben, können wir zwei Kreise und ein Quadrat oder ein Dreieck verwenden, um die grundlegende Herzform zu erstellen. Dies bedeutet, dass diese besonders schönen Kurven fehlen würden, aber genau zu unserem Zweck passen würden.

Einen Kreis malen

Lassen Sie uns einige Gleichungen auffrischen, um einen Kreis zu zeichnen. Für einen Kreis mit Mittelpunkt am Ursprung und Radius r, die Gleichung für den Punkt (x, y) auf dem Kreis ist x2 + y2 = r2. Nun, wenn der Mittelpunkt des Kreises liegt (h, k) dann wird die Gleichung  (x-h)2 + (y-k)2 = r2. Wenn wir also ein quadratisches Begrenzungsrahmenrechteck haben, können wir alle Punkte innerhalb dieses Rechtecks ​​durchlaufen und bestimmen, welche Punkte in den Kreis fallen und welche nicht. Wir können leicht unsere erstellen PaintCircle Methode basierend auf diesem Verständnis, wie unten gezeigt.

private void PaintCircle (Textur2D-Textur, Float-Radius, Vector2 midPoint, Farbe) Rect circleBounds = new Rect (); circleBounds.x = Mathf.Clamp (midPoint.x- (radius), 0, Auflösung); circleBounds.y = Mathf.Clamp (midPoint.y- (radius), 0, Auflösung); circleBounds.width = Mathf.Clamp (2 * Radius, 0, Auflösung); circleBounds.height = Mathf.Clamp (2 * Radius, 0, Auflösung); Float iValue; für (int i = (int) circleBounds.x; imidPoint.x-iValue && i

Sobald wir die haben PaintCircle Auf diese Weise können wir unsere Herzform wie unten gezeigt erstellen.

void PaintHearts (Texture2D-Textur) // 2 Kreise auf dem oberen Float-Radius = Auflösung * 0.26f; Vector2 mid = new Vector2 (Radius, Auflösungsradius); PaintCircle (Textur, Radius, Mitte, Color.red); mid = new Vector2 (Auflösungsradius, Auflösungsradius); PaintCircle (Textur, Radius, Mitte, Color.red); // Dreieck am unteren Rand des Gleitkommas Breite = Auflösung * 0.58f; int endJ = (int) (Auflösung * 0,65f); int startJ = (int) (Auflösung * 0,1f); Float Delta = (Breite / EndeJ); Float MidI = Auflösung * 0.5f; für (int i = 0; i(midI- (delta * (j-startJ))) && i<(midI+(delta*(j-startJ)))) texture.SetPixel(i, j, Color.red);    

Die Variable Auflösung ist die Breite und Höhe der Textur.

5. Erstellen der Rautenform

Wir werden zwei Möglichkeiten diskutieren, um die Rautenform zu zeichnen.

Einen einfachen Diamanten malen

Am einfachsten ist es, den für das Dreieck verwendeten Code zu erweitern und oben ein umgekehrtes Dreieck hinzuzufügen, um die erforderliche Form zu erzeugen (siehe unten).

 void PaintDiamond (Texture2D-Textur) Floatbreite = Auflösung * 0,35f; für (int i = 0; imidJ) j = Auflösung-j; if (i> (midI- (delta * j)) && i<(midI+(delta*j))) isValid=true;  else if(i>(midI- (delta * j)) && i<(midI+(delta*j))) isValid=true;   return isValid;  PaintDiamond(texture);

Einen kurvigen Diamanten malen

Die zweite besteht darin, eine andere Gleichung zu verwenden, um eine bessere, kurvigere Version unserer Diamantform zu erstellen. Wir werden dieses verwenden, um das Kacheldesign für die Rückseite unserer Karte zu erstellen. Die Gleichung für einen Kreis leitet sich von der ursprünglichen Gleichung einer Ellipse ab (x / a)2 + (j / b)2 = r2.

Diese Gleichung ist die gleiche wie die des Kreises bei den Variablen ein und b sind beide 1. Die Ellipsengleichung kann dann für ähnliche Formen zu einer Superellipsengleichung erweitert werden, indem einfach die Potenz verändert wird, (x / a)n + (j / b)n = rn. Also wann n ist 2 Wir haben die Ellipse und für andere Werte von n Wir werden verschiedene Formen haben, eine davon ist unser Diamant. Wir können den Ansatz verwenden, um zum zu gelangen PaintCircle Methode, um zu unserem neuen zu gelangen PaintDiamond Methode.

private void PaintDiamond (Textur2D-Textur, Rect rectBounds, Vector2 midPoint, Farbe, Float n = 0.8f) Float iValue; int a = (int) (rectBounds.width / 2); int b = (int) (rectBounds.height / 2); Float nRoot = 1 / n; Float Delta; Schwimmer partiell ein; rectBounds.width = Mathf.Clamp (rectBounds.x + rectBounds.width, 0, Auflösung); rectBounds.height = Mathf.Clamp (rectBounds.y + rectBounds.height, 0, Auflösung); rectBounds.x = Mathf.Clamp (rectBounds.x, 0, Auflösung); rectBounds.y = Mathf.Clamp (rectBounds.y, 0, Auflösung); für (int i = (int) rectBounds.x; imidPoint.x-iValue && i

Malen eines abgerundeten Rechtecks

Dieselbe Gleichung kann verwendet werden, um die Basisform für abgerundete Rechteckkarten zu erstellen, indem der Wert von geändert wird n.

private void PaintRoundedRectangle (Textur2D-Textur) für (int i = 0; imid.x-iValue && i

Kacheln eines Designs bemalen

Verwenden Sie dies PaintDiamond Auf diese Weise können wir fünf Diamanten zeichnen, um die Kacheltextur für das Design auf der Rückseite unserer Karte zu erstellen.

Der Code zum Zeichnen des Kacheldesigns ist wie folgt.

 private void PaintTilingDesign (Texture2D-Textur, int tileResolution) Vector2 mid = new Vector2 (tileResolution / 2, tileResolution / 2); Floatgröße = 0,6f * tileResolution; PaintDiamond (Textur, neues Rect (mid.x-size / 2, mid.y-size / 2, Größe, Größe), mid, Color.red); mid = new Vector2 (0,0); PaintDiamond (Textur, neues Rect (mid.x-size / 2, mid.y-size / 2, Größe, Größe), mid, Color.red); mid = new Vector2 (tileResolution, 0); PaintDiamond (Textur, neues Rect (mid.x-size / 2, mid.y-size / 2, Größe, Größe), mid, Color.red); mid = new Vector2 (tileResolution, tileResolution); PaintDiamond (Textur, neues Rect (mid.x-size / 2, mid.y-size / 2, Größe, Größe), mid, Color.red); mid = new Vector2 (0, tileResolution); PaintDiamond (Textur, neues Rect (mid.x-size / 2, mid.y-size / 2, Größe, Größe), mid, Color.red); 

6. Erstellen der Pik-Form

Die Spatenform ist nur die vertikale Drehung unserer Herzform zusammen mit einer Basisform. Diese Basisform wird auch für die Keulenform gleich sein. Die folgende Abbildung zeigt, wie wir diese Basisform mit zwei Kreisen erstellen können.

Das PaintSpades Methode wird wie unten gezeigt sein.

void PaintSpades (Textur2D-Textur) // 2 Kreise auf mittlerem Fließradius = Auflösung * 0.26f; Vector2 mid = new Vector2 (Radius, Auflösung-2,2f * Radius); PaintCircle (Textur, Radius, Mitte, Color.black); mid = new Vector2 (Auflösungsradius, Auflösung-2.2f * Radius); PaintCircle (Textur, Radius, Mitte, Color.black); // Dreieck im oberen Bereich Breite Breite = Auflösung * 0.49f; int startJ = (int) (Auflösung * 0,52f); Float Delta = (Breite / (Auflösung-StartJ)); Float MidI = Auflösung * 0.5f; int verändertJ; Radius = Auflösung * 0,5f; Float MidJ = Auflösung * 0.42f; Float iValue; für (int i = 0; i(midI- (delta * modifiedJ)) && i<(midI+(delta*alteredJ))) texture.SetPixel(i, j, Color.black);   //bottom stalk for (int k=0;kmid.x + iValue) mid = new Vector2 (Auflösung, midJ); iValue = (Mathf.Sqrt (Radius * Radius - ((k-mid.y) * (k-mid.y)))) // // mid.x; wenn ich

7. Erstellen der Keulenform

An diesem Punkt bin ich sicher, dass Sie herausfinden können, wie einfach es geworden ist, die Form eines Clubs zu erstellen. Alles, was wir brauchen, sind zwei Kreise und die Basisform, die wir für die Pikform erstellt haben.

Das PaintClubs Methode wird wie unten gezeigt sein.

 void PaintClubs (Textur2D-Textur) int radius = (int) (Auflösung * 0,24f); // 3 Kreise Vector2 mid = new Vector2 (Auflösung * 0.5f, Auflösungsradius); PaintCircle (Textur, Radius, Mitte, Color.black); mid = new Vector2 (Auflösung * 0,25f, Auflösung- (2,5f * Radius)); PaintCircle (Textur, Radius, Mitte, Color.black); mid = new Vector2 (Auflösung * 0,75f, Auflösung- (2,5f * Radius)); PaintCircle (Textur, Radius, Mitte, Color.black); // Basisstielradius = (int) (Auflösung * 0.5f); Float MidY = Auflösung * 0.42f; int stalkHeightJ = (int) (Auflösung * 0,65f); Float iValue; für (int i = 0; imid.x + iValue) mid = new Vector2 (Auflösung * 1,035f, midY); iValue = (Mathf.Sqrt (Radius * Radius - ((j-mid.y) * (j-mid.y)))) // // mid.x; wenn ich

8. Texturen verpacken

Wenn Sie die Unity-Quelldateien für dieses Projekt untersuchen, finden Sie eine TextureManager Klasse, die all das schwere Heben macht. Nachdem wir alle notwendigen Texturen erstellt haben, wird der TextureManager Klasse verwendet die PackTextures Diese Methode kombiniert sie in einer einzigen Textur, wodurch die Anzahl der erforderlichen Draw-Aufrufe reduziert wird, wenn wir diese Formen verwenden.

Rect []packAssets = PackTexture.PackTextures (allGraphics, 1);

Verwendung der GepackteAssets Array können wir die Begrenzungsrahmen einzelner Texturen aus der genannten Mastertextur abrufen GepackteTextur.

public Rect GetTextureRectByName (String TexturName) TexturName = TexturName.ToLower (); int textureIndex; Rect textureRect = new Rect (0,0,0,0); if (textureDict.TryGetValue (textureName, out textureIndex)) textureRect = ConvertUVToTextureCoordinates (loadedAssets [textureIndex]);  else Debug.Log ("keine solche Textur" + TexturName);  return textureRect;  private Rect ConvertUVToTextureCoordinates (Rect rect) return new Rect (rect.x * gepackteTexture.width, rect.y * gepackteTexture.height, rect.width * gepackteTexture.width, rect.height * gepackteTexture.height); 

Fazit

Nachdem alle erforderlichen Komponenten erstellt wurden, können wir mit der Erstellung unseres Kartensets fortfahren, da es nur eine Frage der richtigen Anordnung der Formen ist. Wir können entweder die Unity-Benutzeroberfläche verwenden, um Karten zusammenzusetzen, oder wir können die Karten als individuelle Texturen erstellen. Sie können den Beispielcode untersuchen, um zu verstehen, wie ich die erste Methode zum Erstellen von Kartenlayouts verwendet habe.

Wir können dieselbe Methode zum Erstellen beliebiger dynamischer Grafiken zur Laufzeit in Unity verwenden. Das Erstellen von Grafiken zur Laufzeit ist ein leistungshungriger Vorgang, der jedoch nur einmal durchgeführt werden muss, wenn diese Texturen effizient gespeichert und wiederverwendet werden. Durch das Packen der dynamisch erstellten Objekte in eine einzige Textur erhalten wir auch die Vorteile eines Texturatlas.

Jetzt, da wir unser Spielkartendeck haben, lassen Sie mich wissen, welche Spiele Sie damit erstellen möchten.