Speelkaarten dynamisch maken met code voor gameproblemen

Wat je gaat creëren

Deze tutorial is anders dan mijn eerdere tutorials omdat deze is gericht op gameproblemen en game-prototyping, met name kaartspellen. We gaan een 2D-speelkaartdek in Unity maken zonder enige kunst-puur met code te gebruiken.

1. Onderdelen van een speelkaartdek

Een speelkaartenbord heeft in totaal 52 kaarten met 13 kaarten van elk 4 verschillende symbolen. Om een ​​code te maken, moeten we deze 4 symbolen maken, de afgeronde rechthoekige basis voor de kaart en het ontwerp op de achterkant van de kaart.

Het ontwerp op de achterkant van de kaart kan elk abstract patroon zijn en er zijn talloze manieren om er een te maken. We zullen een eenvoudig betegelbaar patroon maken dat vervolgens wordt betegeld om het ontwerp te maken. We hebben geen speciaal ontwerp voor de kaarten A, K, Q en J.

2. Alternatieve oplossingen

Voordat we beginnen, moet ik vermelden dat er eenvoudiger oplossingen zijn die we kunnen gebruiken om een ​​pak kaarten te maken. Sommige daarvan staan ​​hieronder vermeld.

  1. De voor de hand liggende is om vooraf gegenereerde kunst te gebruiken voor alle ontwerpen.
  2. Het minder voor de hand liggende is om een ​​lettertype te gebruiken dat alle benodigde symbolen bevat. We kunnen het lettertype ook omzetten in een bitmap-lettertype om het aantal tekenoproepen te verminderen en de prestaties te verbeteren.

De oplossing op basis van lettertypen is de snelste en gemakkelijkste oplossing als u snelle prototypen wilt maken.

3. Texturen maken tijdens runtime

De eerste stap is om te leren hoe je een kunt maken Texture2D met behulp van code die vervolgens kan worden gebruikt om een ​​te maken sprite in eenheid. De volgende code toont de creatie van een 256x256 lege textuur.

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

Het idee is om alle ontwerpen op de textuur te tekenen voordat we de Van toepassing zijn methode. We kunnen ontwerpen pixel voor pixel op de textuur tekenen met behulp van de SetPixel methode, zoals hieronder weergegeven.

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

Als we bijvoorbeeld de hele textuur met een kleur willen invullen, kunnen we een methode als deze gebruiken.

private void PaintRectangle (Texture2D texture, Rect rectBounds, Color color) for (int i = (int) rectBounds.x; i

Zodra we een hebben Texture2D gemaakt, kunnen we het gebruiken om een ​​te maken sprite worden weergegeven op het scherm.

Sprite sprite = Sprite.Create (textuur, nieuwe Rect (0.0f, 0.0f, texture.width, texture.height), new Vector2 (0.5f, 0.5f), 1);

Het gecompliceerde deel in dit alles is het creëren van de nodige ontwerpen op de textuur.

4. De hartvorm maken

Als het gaat om het creëren van de hartvorm, zijn er veel verschillende benaderingen die we zouden kunnen gebruiken, waaronder enkele gecompliceerde vergelijkingen en eenvoudige menging van vormen. We gebruiken de methode voor het mengen van vormen zoals hieronder wordt weergegeven, met name degene met de driehoek.

Zoals je hebt gezien, kunnen we twee cirkels en een vierkant of een driehoek gebruiken om de basishartvorm te creëren. Dit betekent dat het die extra mooie bochten zou missen maar perfect bij ons doel zou passen.

Een cirkel schilderen

Laten we wat vergelijkingen oppoetsen om een ​​cirkel te tekenen. Voor een cirkel met middelpunt van oorsprong en straal r, de vergelijking voor het punt (X, y) op de cirkel is X2 + Y2 = r2. Nu als het midden van de cirkel zich bevindt (H, k) dan wordt de vergelijking  (X-h)2 + (Y-k)2 = r2. Dus als we een vierkante rechthoek hebben, kunnen we alle punten in die rechthoek doorlopen en bepalen welke punten binnen de cirkel vallen en welke niet. We kunnen gemakkelijk onze maken PaintCircle methode gebaseerd op dit begrip, zoals hieronder getoond.

private void PaintCircle (Texture2D texture, float radius, Vector2 midPoint, Color color) Rect circleBounds = new Rect (); circleBounds.x = Mathf.Clamp (midPoint.x- (straal), 0, resolutie); circleBounds.y = Mathf.Clamp (midPoint.y- (straal), 0, resolutie); circleBounds.width Mathf.Clamp = (2 * radius, 0, resolutie); circleBounds.height Mathf.Clamp = (2 * radius, 0, resolutie); zweven iValue; voor (int i = (int) circleBounds.x; imidPoint.x-iValue && i

Zodra we de hebben PaintCircle methode, kunnen we doorgaan met het creëren van onze hartvorm zoals hieronder getoond.

void PaintHearts (Texture2D texture) // 2 cirkels bovenop zwevende straal = resolutie * 0.26f; Vector2 mid = new Vector2 (radius, resolutie-radius); PaintCircle (textuur, radius, mid, Color.red); mid = new Vector2 (resolutie-straal, resolutie-radius); PaintCircle (textuur, radius, mid, Color.red); // driehoek onderaan vlotterbreedte = resolutie * 0,58f; int endJ = (int) (resolutie * 0,65f); int startJ = (int) (resolutie * 0,1f); zweeft delta = (breedte / eindJ); zweven midI = resolutie * 0,5f; voor (int i = 0; i(MIDI (delta * (j-StartJ))) && i<(midI+(delta*(j-startJ)))) texture.SetPixel(i, j, Color.red);    

De variabele resolutie is de breedte en hoogte van de textuur.

5. De diamantvorm maken

We zullen twee manieren bespreken om de ruitvorm te tekenen.

Een eenvoudige diamant schilderen

De eenvoudigste is om de code die wordt gebruikt voor de driehoek uit te breiden en een omgekeerde driehoek aan de bovenkant toe te voegen om de benodigde vorm te maken, zoals hieronder weergegeven.

 void PaintDiamond (Textuur2D-textuur) zweefbreedte = resolutie * 0,35f; voor (int i = 0; imidJ) j = resolution-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);

Een curvy-diamant schilderen

De tweede is om een ​​andere vergelijking te gebruiken om een ​​betere, bochtige versie van onze diamantvorm te creëren. We zullen deze gebruiken om het tegelontwerp voor de achterkant van onze kaart te maken. De vergelijking voor een cirkel is afgeleid van de oorspronkelijke vergelijking van een ellips, namelijk (X / a)2 + (Y / b)2 = r2.

Deze vergelijking is dezelfde als die van de cirkel wanneer de variabelen een en b zijn beide 1. De ellipsvergelijking kan vervolgens worden uitgebreid tot een superellipsvergelijking voor vergelijkbare vormen door de macht te veranderen, (X / a)n + (Y / b)n = rn. Dus wanneer n is 2 we hebben de ellips en voor andere waarden van n we zullen verschillende vormen hebben, een daarvan is onze diamant. We kunnen de gebruikte aanpak gebruiken om tot de. Te komen PaintCircle methode om te komen tot onze nieuwe PaintDiamond methode.

private void PaintDiamond (Texture2D texture, Rect rectBounds, Vector2 midPoint, Color color, float n = 0.8f) float iValue; int a = (int) (rectBounds.width / 2); int b = (int) (rectBounds.height / 2); zweven nRoot = 1 / n; zwevende delta; float partialOne; rectBounds.width = Mathf.Clamp (rectBounds.x + rectBounds.width, 0, resolutie); rectBounds.height = Mathf.Clamp (rectBounds.y + rectBounds.height, 0, resolutie); rectBounds.x = Mathf.Clamp (rectBounds.x, 0, resolutie); rectBounds.y = Mathf.Clamp (rectBounds.y, 0, resolutie); voor (int i = (int) rectBounds.x; imidPoint.x-iValue && i

Een afgeronde rechthoek maken

Dezelfde vergelijking kan worden gebruikt om de basisvorm van onze afgeronde rechthoekkaart te maken door de waarde van te variëren n.

private void PaintRoundedRectangle (Texture2D texture) for (int i = 0; imid.x-iValue && i

Een betegeling ontwerpen

Dit gebruiken PaintDiamond methode kunnen we vijf diamanten tekenen om de tegelstructuur voor het ontwerp op de achterkant van onze kaart te maken.

De code voor het tekenen van het tegelontwerp is zoals hieronder.

 private void PaintTilingDesign (Texture2D texture, int tileResolution) Vector2 mid = new Vector2 (tileResolution / 2, tileResolution / 2); vlotterafmeting = 0,6f * tegelResolutie; PaintDiamond (textuur, nieuwe Rect (mid.x-size / 2, mid.y-size / 2, size, size), mid, Color.red); mid = new Vector2 (0,0); PaintDiamond (textuur, nieuwe Rect (mid.x-size / 2, mid.y-size / 2, size, size), mid, Color.red); mid = new Vector2 (tileResolution, 0); PaintDiamond (textuur, nieuwe Rect (mid.x-size / 2, mid.y-size / 2, size, size), mid, Color.red); mid = new Vector2 (tileResolution, tileResolution); PaintDiamond (textuur, nieuwe Rect (mid.x-size / 2, mid.y-size / 2, size, size), mid, Color.red); mid = new Vector2 (0, tileResolution); PaintDiamond (textuur, nieuwe Rect (mid.x-size / 2, mid.y-size / 2, size, size), mid, Color.red); 

6. De schoppenvorm maken

De schoppenvorm is slechts de verticale omslag van onze hartvorm samen met een basisvorm. Deze basisvorm zal ook hetzelfde zijn voor de vorm van de club. De onderstaande afbeelding illustreert hoe we twee cirkels kunnen gebruiken om deze basisvorm te maken.

De PaintSpades methode zal zijn zoals hieronder getoond.

void PaintSpades (texture2D texture) // 2 cirkels op middelste zwevende straal = resolutie * 0.26f; Vector2 midden = nieuwe Vector2 (straal, resolutie-2.2f * straal); PaintCircle (textuur, radius, mid, Color.black); midden = nieuwe Vector2 (resolutie-radius, resolutie-2.2f * straal); PaintCircle (textuur, radius, mid, Color.black); // driehoek bovenaan zwevende breedte = resolutie * 0.49f; int startJ = (int) (resolutie * 0.52f); float delta = (width / (resolution-startJ)); zweven midI = resolutie * 0,5f; int veranderdeJ; radius = Resolutie * 0.5f; zweven midJ = resolutie * 0.42f; zweven iValue; voor (int i = 0; i(MIDI (delta * alteredJ)) && i<(midI+(delta*alteredJ))) texture.SetPixel(i, j, Color.black);   //bottom stalk for (int k=0;kmid.x + iValue) mid = new Vector2 (resolution, midJ); iValue = (Mathf.Sqrt (radius * radius - ((k-mid.y) * (k-mid.y)))); // + mid.x; als ik

7. De clubvorm maken

Op dit moment ben ik er zeker van dat je kunt uitvinden hoe gemakkelijk het is geworden om de vorm van de club te creëren. Alles wat we nodig hebben zijn twee cirkels en de basisvorm die we hebben gemaakt voor de vorm van de schoppen.

De PaintClubs methode zal zijn zoals hieronder getoond.

 void PaintClubs (Texture2D texture) int radius = (int) (resolutie * 0,24f); // 3 cirkels Vector2 midden = nieuwe Vector2 (resolutie * 0,5f, resolutie-radius); PaintCircle (textuur, radius, mid, Color.black); mid = new Vector2 (resolutie * 0,25f, resolutie- (2,5f * radius)); PaintCircle (textuur, radius, mid, Color.black); mid = nieuwe Vector2 (resolutie * 0,75f, resolutie- (2,5f * radius)); PaintCircle (textuur, radius, mid, Color.black); // base steel radius = (int) (resolutie * 0,5f); zweven midY = resolutie * 0.42f; int stalkHeightJ = (int) (resolutie * 0,65f); zweven iValue; voor (int i = 0; imid.x + iValue) mid = new Vector2 (resolutie * 1.035f, midY); iValue = (Mathf.Sqrt (radius * radius - ((j-mid.y) * (j-mid.y)))); // + mid.x; als ik

8. Verpakkende texturen

Als u de Unity-bronbestanden voor dit project verkent, vindt u een TextureManager klasse die al het zware werk doet. Zodra we alle benodigde texturen hebben gemaakt, is de TextureManager class gebruikt de PackTextures methode om ze te combineren tot een enkele textuur, waardoor het aantal tekenbeurten vermindert dat vereist is wanneer we deze vormen gebruiken.

Rect [] packedAssets = packedTexture.PackTextures (allGraphics, 1);

De ... gebruiken packedAssets matrix, kunnen we de selectiekaders van afzonderlijke structuren uit de opgegeven hoofdstructuur ophalen packedTexture.

public Rect GetTextureRectByName (string textureName) textureName = textureName.ToLower (); int textureIndex; Rect textureRect = new Rect (0,0,0,0); if (textureDict.TryGetValue (textureName, out textureIndex)) textureRect = ConvertUVToTextureCoordinates (packedAssets [textureIndex]);  else Debug.Log ("niet zo'n structuur" + textureName);  return textureRect;  private Rect ConvertUVToTextureCoordinates (Rect rect) retourneer nieuwe Rect (rect.x * packedTexture.width, rect.y * packedTexture.height, rect.width * packedTexture.width, rect.height * packedTexture.height); 

Conclusie

Met alle benodigde componenten gemaakt, kunnen we doorgaan met het maken van ons spel kaarten, omdat het gewoon een kwestie is van het correct opmaken van de vormen. We kunnen de Unity UI gebruiken om samengestelde kaarten te maken of we kunnen de kaarten als afzonderlijke structuren maken. U kunt de voorbeeldcode verkennen om te begrijpen hoe ik de eerste methode heb gebruikt om kaartlay-outs te maken.

We kunnen dezelfde methode volgen voor het maken van elke vorm van dynamische kunst tijdens runtime in Unity. Het maken van kunst tijdens runtime is een prestatie-hongerige operatie, maar het hoeft maar één keer gedaan te worden als we die texturen efficiënt opslaan en hergebruiken. Door de dynamisch gemaakte items in één structuur in te pakken, profiteren we ook van de voordelen van het gebruik van een structuuratlas.

Nu we ons speelkaartenbord hebben, laat me weten welke games je ermee wilt maken.