Gör ett match-3-spel i Construct 2 Eliminera färdiga matcher

I den tidigare handledningen fick vi slutligen vårt spel att flytta och lägga till rörelser till våra block. Utöver det skapade vi ett rudimentärt svårighetssystem för att göra spelet svårare eftersom spelaren spelar längre tid.

Med båda dessa funktioner i spelet är vi redo att implementera systemet som eliminerar förverkade matcher från brädet. Även om detta inte är den sista artikeln i serien, är detta det sista stora systemet vi behöver implementera - så bli bekväm, för att vi har vårt arbete klippt ut för oss.


Final Game Demo

Här är en demonstration av spelet vi arbetar mot genom hela serien:




Snabba korrigeringar

Innan vi börjar med huvuddelen av denna handledning vill jag ta en minut för att åtgärda två problem som jag upptäckte när jag skrev den tidigare handledningen.

Matchning

Den första frågan jag hänvisar till kommer upp i det scenario som du kan se nedan:

I den här situationen, om du drar Block B på den gröna pricken ska den falla på grund av de tomma utrymmena under pricken, och så småningom landa i märket C. I de flesta scenarier kommer detta att hända och spelet kommer att fungera normalt, men i vissa scenarier kommer spelet istället att upptäcka en match i det korta ögonblicket där Block B är bredvid gruppen en och det kommer att hamna förstör alla tre kvarter.

Problemet är inte exklusivt för det här scenariot ovan och kan också presentera sig när du gör samma sak i scenariot jag har markerat nedan.

Om vi ​​inte åtgärdar detta kommer spelaren att få kredit och poäng för ett antal matchningar som de aldrig hade för avsikt att göra och kan också bli förvirrade om varför så många block försvinner oväntat.

Tack och lov är det dock en enkel fråga att fixa. För att lösa detta problem ska vi skapa en ny Global variabel kallad MatchesPossible vilket kommer att diktera om matchningar kan göras och en ny händelse som kommer att upptäcka när ett block faller och kommer att modifiera MatchesPossible för att göra det så att inga träffar kan göras när detta händer.

Först kommer vi att skapa den globala variabeln:

Global Variable: MatchesPossible Typ = Number Initial Value = 1

Din nya variabel ska se ut så här:

Nu ska vi göra händelsen som kommer att lyssna på när ett block faller:

Händelse: Skick: Omvänd: Block> Överlappar vid förskjutning Objekt = Block Offset X = 0 Offset Y = 8 Skick: Block> Jämför Y Jämförelse = Mindre eller lika Y-koordinat = SPAWNY Åtgärd: System> Ange värde Variabel = Matcher Möjligt värde = 1

Denna händelse gör det så att när ett block visar sig ha ett tomt utrymme under det MatchesPossible är satt till 1, vilket betyder att matcher inte är möjliga. Du märker också att den kontrollerar blockets Y-position. Detta är för att säkerställa att blocket inte ligger på den lägsta raden av block som alltid har tomt utrymme under det.

Därefter behöver vi en händelse som sätter in MKatchesPossible tillbaka till 0 när inga block har ett tomt utrymme under dem. Denna andra händelse kommer att baseras på ett annat villkor:

Händelse: Skick: System> Övrig åtgärd: System> Ställ in värde Variabel = MatcherMotivt värde = 0

Se till att denna händelse omedelbart följer den första händelsen så att Else-satsen används korrekt.

Dina två nya händelser ska se ut så här:

Slutligen kommer vi att lägga till ett nytt villkor för CheckMatches så att den tittar på MatchesPossible för att avgöra om en matchning kan göras. Lägg till detta villkor i det ursprungliga funktionssamtalet för CheckMatches Händelse.

Skick: System> Jämför variabelvariabel = MatcherMöjlig Jämförelse = Lika Vale = 0

Med villkoret lagt till, din CheckMatches Händelsen ska nu se ut så här:

Till skillnad från de flesta problem som vi stött på fram till denna punkt uppstår det här problemet på en ganska inkonsekvent grund. Det betyder att vi inte kan testa för att se om vi löst problemet, vi kan bara testa för att se om vi orsakade andra problem. Om du spelar spelet nu ska du se att inga nya problem har orsakats av detta tillstånd.

Points

Den andra frågan jag ville fixa innan vi lägger till något nytt hänför sig till Points-systemet.

Under arbetet med projektet för denna handledning märkte jag att något jag gjorde orsakade att Pointsystemet uppträdde konstigt och ger spelaren fyra eller fem gånger så många poäng som de borde få för varje match. Även om jag inte kunde bestämma vilken förändring jag gjorde orsakade detta att börja hända, fann jag att roten till problemet var att GivePoints funktionen blev faktiskt kallad flera gånger eftersom blocken inte förstördes omedelbart. Tack och lov, som vår senaste fråga, kan det här lösas enkelt.

För att fixa detta kommer vi att skapa en ny variabel som kommer att berätta för systemet om det kan ge poäng. Då, när vi ska använda GivePoints funktionen kommer vi också att ändra variabeln så att händelsen bara brinner en gång. Slutligen, när poängen har givits kommer vi att ändra variabeln en gång till så att det inte kommer att bli ett problem nästa gång vi försöker ge poäng.

Skapa först en Global Variable kallad PointsGiven:

Global Variable: PointsGiven Type = Antal Initial Value = 0

Din variabel ska så här:

Nästa kommer vi att ändra delen av FindMatches funktion som faktiskt ger poängen genom att lägga till ett nytt villkor och två nya åtgärder.

Skick: System> Jämför variabelvariabel = PointsGiven Jämförelse = Likvärdig till värde = 0

Lägg nu till den här åtgärden till början av åtgärdslistan:

Åtgärd: System> Ange värde Variabel = PointsGiven Value = 1

Slutligen lägg till den här åtgärden till slutet av åtgärdslistan:

Åtgärd: System> Ange värde Variabel = PointsGiven Value = 0

Händelsen ska nu se ut så här:

Med dessa förändringar har vi gjort det så att händelsen som kallar GivePoints kommer endast att köras när PointsGiven variabel är 0. Eftersom vi omedelbart ställer värdet på 1 när vi startar evenemanget förhindras det att händelsen skjuter mer än en gång och säkerställer att spelaren får rätt antal poäng.

Om du kör spelet på denna punkt borde du få rätt antal poäng för varje match du gör, även om du inte hade det här problemet för att börja med.


Eliminerar Pre-Made Matches

Nu när vi har fått dessa korrigeringar ur vägen kan vi fortsätta att skapa systemet som eliminerar matchningar som skapas av systemet när det slumpmässigt tilldelar färger till blocken det skapar.

Problemet vi har nu är att eftersom blockfärgerna är helt slumpmässiga är det inte ovanligt att du startar spelet och ser en massa matchningar som görs omedelbart. Det här är ett problem eftersom det kan göra de första sekunderna av spelet mycket förvirrande för någon som aldrig spelat förut och eftersom det ger spelare poängen som de inte tjänat.

För att lösa problemet skapar vi en funktion som kommer att se på varje block och sedan se om det blocket är samma färg som någon av dess grannar. Om det är samma färg som en dess grannar, fortsätter det att ändra färg på det blocket tills det inte längre matchar någon av blocken som omger det.

För att detta system ska fungera måste vi också skapa flera supportfunktioner och evenemang samt lägga till en ny instansvariabel till Blockera objekt.

Gör det till jobbet

Skapa först en ny instansvariabel för Blockera objekt:

Instansvariabel: BlockID Type = Antal Initialt värde = 0

Denna variabel är vad vi ska använda för att enkelt identifiera ett block så att vi kan berätta några av de funktioner vi ska göra exakt vilket block vi vill titta på, oavsett position.

Innan vi går vidare måste vi börja använda denna variabel. Gå till Vid start av layout Händelse som skapar blocken och lägger till en ny åtgärd före åtgärden som ökar NumBlocks:

Åtgärd: Block> Ange värde Instansvariabel = BlockID Value = NumBlocks

Din blockgytningshändelse ska nu se ut så här:

Nästa måste vi göra en funktion som kommer att ta i X och Y-positionen i ett block och berätta vilken färg den blocket är:

Händelse: Villkor: Funktion> På funktion Namn = "GetBlockColor" Delhändelse: Villkor: Blockera> Jämför X Jämförelse = Lika X-koordinat = Funktion.Param (0) Skick: Blockera> Jämför Y Jämförelse = Jämställd med Y co -ordinate = Function.Param (0) Åtgärd: Funktion> Ställ in returvärde Värde = Block.Color Sub-Event: System> Övrig åtgärd: Funktion> Ställ in returvärde Värde = -1

Funktionen ska se ut så här när den är klar:

Nu när vi har en funktion som kan berätta färgen på ett block, kommer vi att skapa funktionen som faktiskt ser på ett block och bestämmer om det finns några närliggande block som är samma färg.

Sättet på den här funktionen är ganska enkelt.

  • Först ska vi passera en BlockID in i funktionen.
  • Om det för närvarande finns ett block med det BlockID, funktionen kommer att titta på de fyra närliggande blocken och bestämmer om blocket det ser på är samma färg som någon av dess grannar.
  • Om det konstateras att det finns en granne med samma färg, kommer det att börja ändra färg på blocket som man tittar på, och det kommer att fortsätta att ändra färgen tills blocket är en annan färg än alla dess grannar.

Innan vi kan göra denna händelse måste vi skapa en ny global variabel. I funktionen kommer vi att använda en While loop för att bestämma om blockets färg behöver ändras. Den variabel som vi ska skapa är den variabel som While Loop ska använda för att bestämma om den behöver fortsätta att köra:

Global Variabel: HasMatchingNeighbor Type = Antal Initialt värde = 0

Tips:Händelsen vi ska göra innehåller en Or-baserad händelse. Om du aldrig har gjort ett Händelseblock som har en eller attribut allt du behöver göra är att göra Händelseblocket som du normalt skulle och högerklicka på hela Block och välj Gör "Eller" Block. Till skillnad från ett standardhändelsesblock som kräver att alla villkor fylls innan det kommer att eldas, kommer ett eller ett block att elda om några villkoret är uppfyllt.

Så, låt oss göra händelsen:

Händelse: Skick: Funktion> På funktion Namn = "RemoveSpawnedMatches" Sub-Event: Skick: Blockera> Jämför instansvariabel Instansvariabel = BlockID-jämförelse = Likvärdig till Value = Function.param (0) Åtgärd: System> Ange värde Variabel = HasMatchingNeighbor Value = 1 Underhändelse: Skick: System> Medan tillstånd: System> Jämför variabelvariabel = HasMatchingNeighbor Jämförelse = Likvärdig till värde = 1 Underhändelse: Villkor: Blockera> Jämför instansvariabel Instansvariabel = Färgjämförelse = Likvärdig till värde = Funktion. Ring ("GetBlockColor", Block.X - (Block.Width + 2), Block.Y) Åtgärd: Block> Ange värde Instansvariabel = Färgvärde = golv (Slumpmässig (1,7)) Åtgärd: System> Ställ in värdevariabel = HasMatchingNeighbor Value = 1 Sub-Event: Skick: System> Övrigt: Block> Jämför instansvariabel Instansvariabel = Färgjämförelse = Likvärdig till Value = Function.Call ("GetBlockColor", Block.X + (Block.Width + 2) , Block.Y) Åtgärd: Block> Ange värde Instansvariabel = Färgvärde = golv (Slumpmässig (1,7)) Åtgärd: System> Ställ in värde Variabel = HasMatchingNeighbor Value = 1 Sub-Event: Skick: System> Övrigt: Block> Jämför instansvariabel Instansvariabel = Färgjämförelse = Likvärdig till Value = Function.Call ("GetBlockColor", Block.X, Block.Y - (Block.Width + 2)) Åtgärd: Block> Ange värde Instansvariabel = Färgvärde = golv (Slumpmässigt (1,7)) Åtgärd: System> Ange värde Variabel = HasMatchingNeighbor Value = 1 Sub-Event: Skick : System> Övrigt: Block> Jämför instansvariabel Instansvariabel = Färgjämförelse = Likvärdig till värde = Function.Call ("GetBlockColor", Block.X, Block.Y + (Block.Width + 2)) Åtgärd: Block> Set värde Instansvariabel = Färgvärde = golv (Slumpmässigt (1,7)) Åtgärd: System> Ange värde Variabel = HasMatchingNeighbor Value = 1 Sub-Event: Skick: System> Annan åtgärd: System> Ange värde Variabel = HasMatchingNeighbor Value = 0

Din händelse ska se ut så här:


Så hur fungerar den här funktionen exakt?

Det första som det gör är att checka på Blockera med BlockID att systemet passerade in. När det lokaliserar det Blockera det anger värdet av HasMatchingNeighbors till 1 och kör sedan Med-loopen.

Eftersom while loop går endast när HasMatchingNeighbors är 1, det är värdet det ställer på. Under While loop testar den för att se om det finns en grann Blockera till vänster är den högra, över eller under samma färg som Blockera vi tittar på. Om det hittar en matchning Blockera i några av dessa positioner, tilldelar den slumpmässigt en ny färg till Blockera och kör sedan testet igen genom att säkerställa HasMatchingNeighbors är satt till 1. När det äntligen hittar en färg för Blockera som inte matchar någon av sina grannar, ändras värdet av HasMatchingNeighbors till 0 så att While loopen slutar och programmet kan gå vidare.

Nu måste vi implementera denna funktion i spelet. För att göra detta måste vi skapa två nya variabler och två nya funktioner. Låt oss börja med variablerna.

Global variabel: CheckStartingMatches Typ = Nummervärde = 0
Global Variabel: CheckNewestRow Type = Nummervärde = 0

Dina variabler ska se ut så här:

De två variablerna vi just gjort kommer att användas för att utlösa de två händelserna vi ska skapa. Evenemangen själva kommer att användas för att iterera genom blocken omedelbart efter att de har skapats och skickar varje block till RemoveSpawnedMatches fungera.

Anledningen till att vi inte bara ringer till RemoveSpawnedMatches funktionen direkt efter att blocken skapats beror på att blockrutan måste vara komplett för att funktionen ska fungera korrekt. Så istället för att bara ringa funktionen direkt när block skapas kommer vi istället att utlösa en händelse som kan gå igenom blocken och ringa funktionen själv efter att gallret genereras.

Den första händelsen kommer att vara specifikt för iterering genom den första gruppen av block:

Händelse: Skick: System> Jämför variabel Instansvariabel = CheckStartingMatches Jämförelse = Lika värde = 1 Skick: System> För namn = "Block" Start index = 0 Slutindex = NumBlocks-1 Åtgärd: Funktion> Samtalsfunktion Name = "RemoveSpawnedMatches" Parameter 0 = loopindex ("Blocks") SubEvent: Skick: System> Jämför två värden Första värdet = loopindex ("Blocks") Jämförelse = Lika till andra värde = NumBlocks-1 Åtgärd: System> Ställ in variabel Instansvariabel = CheckStartingMatches Value = 0

Det här är vad din händelse ska se ut:

Den andra händelsen kommer att vara specifikt för att kontrollera nya rader av block när de är gjorda:

Händelse: Skick: System> Jämför variabel Instansvariabel = CheckNewestRow Jämförelse = Likvärdig = 1 Skick: System> För namn = "Block" Startindex = NumBlocks-9 Slutindex = NumBlocks-1 Åtgärd: Funktion> Samtalsfunktion Namn = " RemoveSpawnedMatches "Parameter 0 = loopindex (" Blocks ") SubEvent: Skick: System> Jämför två värden Första värdet = loopindex (" Blocks ") Jämförelse = Gäller andra värde = NumBlocks-1 Åtgärd: System> Ställ in variabel Instansvariabel = CheckNewestRow Value = 0

Det här är vad den andra händelsen ska se ut:

Genomförande

Med båda dessa funktioner på plats behöver vi nu bara implementera dem. Gå till den första händelsen som gör blocken, den Vid start av layout Händelse. Vi ska lägga till en delhändelse till detta som kommer att berätta för CheckStartingMatches Händelse för att aktivera.

Sub-Event: Condition: System> Jämför två värden Första värdet = loopindex ("X") Jämförelse Andra värdet: 7 Skick: System> Jämför två värden Första värdet = loopindex ("Y") Jämförelse Andra värdet: 3 Åtgärd: System> Ange värde Instansvariabel = CheckStartingMatches Value = 1

Din händelse ska nu se ut så här:

Den här delhändelsen lyssnar på när den nolllade For-loopen har slutat och ändrar sedan värdet på CheckStartingMatches variabel för att aktivera lämplig händelse.

Vi kommer nu att göra nästan exakt samma underhändelse och bifoga den till SpawnNewBlocks fungera.

Sub-Event: Condition: System> Jämför två värden Första värdet = loopindex ("X") Jämförelse Andra värde: 7 Åtgärd: System> Ange värde Instansvariabel = CheckNewestRow Value = 1

SpawnNewBlocks ska nu se ut så här:

Denna delhändelse gör samma sak som den förra, förutom att den aktiverar den andra händelsen som vi skapade. Om du kör spelet vid denna tidpunkt bör du se att när du startar spelet finns det inte längre några matchningar som uppträder automatiskt.


Slutsats

I denna handledning gjorde vi inte för många förändringar i spelet, men de vi gjorde var väldigt viktiga.

Vid denna tidpunkt tycker jag att det är bäst för oss att sluta för nu och spara de sista två spelelementen för nästa handledning där vi kommer att täcka kedjor / combos och Game Over-skärmen. Detta kommer att bli den sista delen av serien, så jag ska också prata om några spelmekanik som vi inte kommer att täcka i dessa handledning och ge dig några råd om hur du kan göra dessa system på egen hand.

Om du vill ha en nybörjare i nästa veckas innehåll, börja titta på hur du kan upptäcka när skärmen Game Over ska dyka upp, baserat på position eller höjd på några av blocken. Alternativt kan du börja tänka på hur du kan upptäcka när spelaren gör en kedjereaktion som orsakar att mer än en grupp bildas.

Vad du än gör hoppas jag se dig tillbaka här nästa vecka för den sista delen av serien.