Hittills har denna serie täckt grunderna för att skapa ett Match-3-spel och implementera de ursprungliga spelelementen, som block-swapping. I denna handledning ska vi bygga på allt detta och börja upptäcka när spelaren har gjort en match.
Här är en demonstration av spelet vi arbetar mot genom hela serien:
För närvarande ska vi bara implementera en grundläggande version av det matchande systemet, med fokus på att hitta när matchningar existerar och förstöra matchade block. I senare artiklar fortsätter vi att utveckla och utveckla systemet.
Tips: Du bör läsa in hur en rekursiv funktion fungerar, om du inte redan vet det; i huvudsak är det en funktion som kallar sig själv. Rekursiva funktioner kan fungera på samma sätt som loopar, men eftersom de också kan ta in och returnera variabler, har de många fler användningsområden än loopar gör.
Precis som med föregående handledning vill jag först diskutera hur systemet ska fungera, och försök sedan bygga det.
Blockera
objekt. Blockera
, det kommer att passera färgen och positionen av blocket som man tittar på i en rekursiv funktion som kommer att se på den horisontella eller vertikala grannan och bestämma om de har samma färg. matchade
med IsMatched
instansvariabel som vi gjorde i en av de föregående handledningarna; annars gör det ingenting. Först behöver vi en händelse som kan iterera genom var och en Blockera
. Det sätt jag byggt upp systemet, det är det faktiskt iterates genom blocken dubbelt: en gång för att söka efter vertikala träffar och en gång för att kontrollera om horisontella matchningar. Beroende på vilken kontroll det gör gör det en annan funktion att faktiskt leta efter matchen.
Det allra första vi behöver göra är att göra en Global variabel för att hålla reda på hur många matchande block vi har hittat vid en viss iteration:
Global Variable Name: "NumMatchesFound" Typ = Nummervärde = 0
Nu, låt oss göra Händelse som kommer att iterera genom blocken:
Händelse: Funktion> På funktion Namn: "FindMatches" Sub-Event: System> För varje objekt: Block Action: System> Ange värde NumMatchesFound = 1 Åtgärd: Funktion> Samtalsfunktion Namn: "CheckMatchesX" Parameter 0: Block.X Parameter 1 : Block.Y Parameter 2: Block.Color Sub-Event: System> Jämför Variabel NumMatchesFound> = 3 Åtgärd: Block> Ange Boolean IsMatched = Sann Sub-Event: System> För Varje Objekt: Block Action: System> Ange värde NumMatchesFound = 1 Funktion: Funktion> Samtalsfunktion Namn: "CheckMatchesY" Parameter 0: Block.X Parameter 1: Block.Y Parameter 2: Block.Color Sub-Event: System> Jämför Variable NumMatchesFound> = 3 Åtgärd: Block> Ställ in Boolean IsMatched = Sann Sub-Event: Block> Är Boolean Instansvariabel Set System> Vänta Second = 0.1 Block> Destroy
Din kod ska se så här ut:
I detta fall tar vi igenom varje block och skickar in dem CheckMatchesX
eller CheckMatchesY
, de funktioner som kommer att kontrollera om grannblocket är en matchning.
För att skicka blocket till funktionen överför vi funktionerna tre olika parametrar:
Efter varje block skickas till en av funktionerna och funktionen slutar köra, kontrollerar den NumMatchesFound
för att se om det hittat tre eller flera matchande block och märker sedan blocken som matchade
om det gjorde det.
Slutligen, varje block som är markerat som matchade
blir förstörd efter .1 sekunder passerar. Detta vänta
uttalande är där för att låta spelet växla bilderna för blocken till bilden som indikerar att de matchas och att ge spelaren ett ögonblick att märka denna förändring.
(Medan du kunde ta bort vänta
uttalande utan att negativt påverka gameplayen, det gör matchningen lättare för spelaren att förstå och saktar spelet tillräckligt så att spelaren enkelt kan hålla reda på vad som händer.)
Nästa måste vi göra CheckMatchesX
och CheckMatchesY
funktioner. Dessa funktioner fungerar på samma sätt som iteratorerna ovan, eftersom det kommer att finnas en version för kontroll av horisontella matchningar, CheckMatchesX
, och en för vertikala matcher, CheckMatchesY
.
Låt oss först bygga den horisontella kontrollfunktionen:
Händelse: Funktion> På funktion Namn: "CheckMatchesX" Sub-Event: Skick: Blockera> Jämför XX = Funktion.Param (0) + (Block.Width + 2) Skick: Blockera> Jämför YY = Function.Param (1) Skick : Block> Jämför instansvariabel Färg = Funktion.Param (2) Åtgärd: System> Lägg till Variabel = NumBlock Värde = 1 Åtgärd: Funktion> Samtalsfunktion Namn: "CheckMatchesX" Parameter 0: Funktion.Param (0) + (Block. Bredd + 2) Parameter 1: Funktion.Param (1) Parameter 2: Funktion.Param (2) Underhändelse: System> Jämför Variabel NumMatchesFound> = 3 Åtgärd: Block> Ställ in Boolean IsMatched = True
Din kod ska se så här ut:
Så vad gör den här funktionen??
NumMatchesFound
av en, och skickar det nyfunna Blocket till funktionen precis som det gjorde för originalet.Låt oss nu göra en annan version av den här funktionen, som gör samma sak för vertikala träffar. Detta kommer att bli vårt CheckMatchesY
fungera. Du kan antingen kopiera den ursprungliga funktionen och göra alla lämpliga ändringar, eller bygga bara den igen från början. i båda fallen är här hur din funktion ska se ut när den är klar:
Händelse: Funktion> På funktion Namn: "CheckMatchesY" Sub-Event: Skick: Blockera> Jämför XX = Funktion.Param (0) Skick: Block> Jämför YY = Funktion.Param (1) + (Block.Width + 2) Skick : Block> Jämför instansvariabel Färg = Funktion.Param (2) Åtgärd: System> Lägg till Variabel = NumBlock Värde = 1 Åtgärd: Funktion> Samtalsfunktion Namn: "CheckMatchesY" Parameter 0: Funktion.Param (0) Parameter 1: Funktion .Param (1) + (Block.Width + 2) Parameter 2: Funktion.Param (2) Underhändelse: System> Jämför Variabel NumMatchesFound> = 3 Åtgärd: Block> Ställ in Boolean IsMatched = True
Din kod ska se så här ut:
Slutligen måste vi faktiskt kalla FindMatches
fungera. Gå till SwapBlocks
funktion och lägg till en ny delhändelse till slutet av funktionen:
Händelse: Funktion> Sub-Event: Åtgärd: Funktion> Samtalsfunktion Namn: "FindMatches"
Du kommer märka att denna delhändelse faktiskt inte har några villkor. Om du aldrig har gjort en underhändelse som den här innan, gör bara en delhändelse med något villkor alls, eftersom det kräver att du ger ett villkor när du gör en underhändelse och sedan raderar villkoret, men lämna den sub-händelse. På så sätt kontrollerar du att underevenemanget alltid körs.
Din SwapBlocks
händelse ska nu se ut så här:
Om du kör spelet vid denna tidpunkt kommer du att se att blocken blir förstörda när matcher uppstår. Du kommer också att märka att alla matcher som finns där när spelet börjar, försvinner inte förrän du gör ett byte av något slag. Detta beror på att vi aldrig ringer till FindMatches
funktion efter att vi skapat rutnätet.
Anledningen till att vi inte har lagt till den här koden är att i den slutliga versionen kommer det att finnas en annan funktion som förhindrar att matchningar genereras automatiskt så här, så det finns verkligen ingen anledning att oroa sig för detta problem alls. (Men gärna ringa FindMatches
funktion tidigare, om du vill.)
Vid denna tidpunkt har vi ett ganska starkt matchande system, men problemet är att vår kod är överflödig. För närvarande har vi två olika funktioner som kontrollerar om det finns en matchande granne, och den enda skillnaden mellan dem är den som kontrollerar vertikalt och den andra kontrollerar horisontellt.
Eftersom den fria versionen av Construct 2 begränsar hur många händelser vi kan ha är det definitivt ett slöseri. För att lösa detta ska vi göra en ny version av funktionen som kan göra både kontroller.
Om du tittar på funktionen kommer du att se den enda skillnaden mellan de två versionerna är den som läggs till Block.Width + 2
till blockens x-position, och den andra lägger till den i Bocks y-position. Det hinder som vi måste komma förbi för att göra detta till en enda funktion, ger funktionen ett sätt att lägga till Block.Width + 2
till endast X
, eller bara Y
, utan använder en Om
uttalande eller flera funktioner, eftersom de kräver fler händelser som ska utföras.
Min lösning på detta är inte så komplicerad, men det blir lättare att förstå om vi kan se att den kommer ihop, så vi kommer att implementera det, och jag kommer att förklara hur det fungerar när vi kan se allting i aktion.
CheckMatchesY
händelse.CheckMatchesX
händelse till, helt enkelt, CheckMatches
.CheckMatchesX
under FindMatches
händelse: CheckMatches
istället för CheckMatchesX
.Parameter 3
. 1
.Parameter 4.
0
.CheckMatchesY
under FindMatches
händelse: CheckMatches
istället för CheckMatchesY
.Parameter 3
. 0
.Parameter 4
. 1
.Som jag kommer att förklara snart kommer dessa parametrar att berätta CheckMatches
om det gör en horisontell kontroll eller en vertikal kontroll. När vi skickar in 1
för Parameter 3
, och 0
för Parameter 4
, Det är en horisontell kontroll, och när vi skickar in 0
för Parameter 3
, och 1
för Parameter 4
, Det är en vertikal kontroll.
Gå nu tillbaka till CheckMatches
funktionen och ändra villkoren och åtgärderna så att den ser ut så här:
Händelse: Funktion> På funktion Namn: "CheckMatches" Sub-Event: Skick: Block> Jämför XX = Function.Param (0) + ((Block.Width + 2) * Function.Param (3)) Skick: Block> Jämför YY = Function.Param (1) + ((Block.Width + 2) * Function.Param (4)) Skick: Block> Jämför instansvariabel Färg = Funktion.Param (2) Åtgärd: Block> Ställ in Boolean IsMatched = True Action : Funktion> Samtalsfunktion Namn: "CheckMatches" Parameter 0: Funktion.Param (0) + ((Block.Width + 2) * Function.Param (3)) Parameter 1: Funktion.Param (1) + ((Block. Bredd + 2) * Funktion.Param (4)) Parameter 2: Funktion.Param (2) Parameter 3: Funktion.Param (3) Parameter 4: Funktion.Param (4) Underhändelse: System> Jämför Variabel NumMatchesFound> = 3 Åtgärd: Block> Ställ in Boolean IsMatched = True
Detta är vad din FindMatches
och CheckMatches
kod ska nu se ut som:
Så, vad är den här nya versionen av funktionen som faktiskt gör?
Tja, när du ringer CheckMatches
Du skickar nu ytterligare två parametrar, och snarare än att lägga till Block.Width + 2
till antingen x- eller y-läget, lägger den till (Block.Width + 2) * Function.Param (3)
till x-positionen och (Block.Width + 2) * Function.Param (4)
till y-positionen.
Eftersom en av dessa två parametrar alltid kommer att vara 1
, och den andra kommer alltid att vara 0
, det betyder att antingen x- eller y-positionen kommer att ändras - aldrig båda!
Till exempel, om vi passerar in 1
för Parameter 3
, och 0
för Parameter 4
, då lägger det till (Block.Width + 2) * 1
, vilket är helt enkelt Block.Width + 2
, till x-positionen och (Block.Width + 2) * 0
, vilket är 0
, till y-positionen.
Här är ett snabbt exempel för att visa vad jag menar och hur det beräknar placeringen av blocket där den kommer att kontrollera matchen. Låt oss säga att i det här exemplet är det ursprungliga Blocket på (200, 200)
, och blocken har en bredd av 40
. Så, om vi vill få positionen i det närliggande vertikala blocket, skulle formlerna fungera så här:
X = 200 + ((Block.Width + 2) * 0) = 200 + (40 + 2) * 0 = 200 + 0 = 200
Y = 200 + ((Block.Width + 2) * 1) = 200 + (40 + 2) * 1 = 200 + 42 = 242
Om vi ville få läget för det närliggande horisontella blocket skulle formlerna fungera så här:
X = 200 + ((Block.Width + 2) * 1) = 200 + (40 + 2) * 1 = 200 + 42 = 242
Y = 200 + ((Block.Width + 2) * 0) = 200 + (40 + 2) * 0 = 200 + 0 = 200
Om du kör spelet nu bör du se att matchningssystemet fortfarande fungerar som det ursprungligen gjorde, men ur vårt perspektiv är det faktiskt ett bättre system.
Vid denna tidpunkt är vår matchningsdetekteringsfunktion fortfarande ofullständig, men vi har redan gjort mycket i denna handledning och jag tycker att det är viktigt att låta allt detta sjunka innan vi lägger till något annat. Med det i åtanke kommer jag att avsluta denna artikel här. Kolla in demoen i sin nuvarande form.
I nästa artikel lägger vi till ett poängsystem, vi kommer att förbättra matchningssystemet, och vi lägger till "gravitation" så att blocken kommer att falla när block under dem elimineras.
Om du vill ha ett försprång i nästa artikel, ta en stund att överväga hur du skulle upptäcka när det finns ett tomt utrymme under ett block. Försök att titta på Block> Överlappar vid förskjutning
funktion för inspiration!