Gör ett match-3-spel i Construct 2 Blockera rörelsen

I den föregående delen av denna serie gjorde vi några små men viktiga förändringar i många av systemen vi skapade för vårt Match-3-spel. Med dessa förbättringar genomförda, kommer vi nu att komma tillbaka på rätt spår och genomföra ett av de två senaste huvudsystemen för spelet: Block Movement-systemet.

Denna handledning tar dig igenom hela systemutvecklingen, vilket gör att blocken kan stiga till toppen av skärmen och täcker också skapandet av alla de mindre system som vi behöver implementera för att stödja rörelsessystemet. Medan de ämnen jag täcker i den här handledningen inte är för komplexa, det finns mycket att gå över - så låt oss ta itu med det.


Final Game Demo

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




1. Flytta blockerar upp

Innan vi börjar flytta blocken måste vi göra en liten förändring till händelserna som krossar Blocks. Gå till System> Vid start av layout händelse och ändra Y för slinga för att gå från 0 till 3, istället för från 0 till 7 som det ursprungligen gjorde.

Evenemanget ska nu se ut så här:


Anledningen till att vi gjorde denna förändring är att vi vill att spelet ska börja med färre block på skärmen så att det inte slutar så fort när vi lägger till ett spel över i nästa handledning.

Därefter kommer vi att skapa en variabel som representerar blockens hastighet:

Global variabel: Namn = CurrentSpeed ​​Type = Nummervärde = 0,2

Nu skapar vi händelsen som faktiskt flyttar blocken:

Händelse: Skick: System> Varje X Seconds Interval (sekunder) = CurrentSpeed ​​Action: Block> Flytta vid vinkelvinkel = -90 Avstånd = 1

Evenemanget ska se så här ut:


Om du kör spelet efter att ha lagt till den här händelsen är det första som du bör se att blocken faller på grund av gravitationen som vi genomförde i en tidigare handledning. Efter det ska blocken långsamt stiga tills de befinner sig i sin ursprungliga position och sedan släppa igen. Detta kommer att upprepas oändligt så länge du inte gör något för blocken.

Detta händer eftersom blocken flyttar sig över den punkt där tyngdkraften ska sparka in, och de upptäcker att det inte finns några block under dem, vilket får dem att falla. Även om det här är ett problem, är det inte den första jag vill titta på.


2. Fixing byta

Kör spelet och försök att göra en byte av något slag. När du gör detta bör du se att blocken börjar fastna bakom varandra, fastnar i positioner som inte är anpassade till rutnätet och bara vanligtvis misshandlar. Det finns två skäl till denna fråga.

Det första problemet är att även om vi flyttar Blocks själva flyger vi inte LeftBlock, RightBlock, TopBlock, och BottomBlock objekt med dem, vilket innebär att blocken du använder för att upptäcka swappar inte rör sig med blocknätet - de sitter bara i den position de är inställda på när du först hämtar ett block.

Så, när du försöker göra en byte, blir blocken sönderdelade eftersom växlingsdetekteringsklockorna inte har anpassats alls till nätet. (Detta är också orsaken till den andra frågan vi har, vilket är att vi inte ändrar de positioner vi lagrade i BlockPositions array heller.)

GIF nedan visar detta problem:


Som du kan se i GIF rör sig inte swap detection blocken, även om blocken själva är.

För att lösa båda dessa problem lägger vi till några fler åtgärder till händelsen som vi just skapat:

Åtgärd: BottomBlock> Flytta vid vinkelvinkel = -90 Avstånd = 1 Åtgärd: Vänsterblock> Flytta vid vinkelvinkel = -90 Avstånd = 1 Åtgärd: Högerblock> Flytta vid vinkelvinkel = -90 Avstånd = 1 Åtgärd: TopBlock> Flytta i vinkelvinkel = -90 Avstånd = 1 Åtgärd: BlockPositions> Ange vid XY X = 0 Y = 1 Värde = BlockPositions.At (0,1) - 1 Åtgärd: BlockPositions> Ange vid XY X = 1 Y = 1 Value = BlockPositions.At ( 1,1) - 1

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


De första fyra åtgärderna som vi just lagt till anpassar positionerna för LeftBlock, TopBlock, RightBlock, och BottomBlock föremål så att de ligger i linje med blockruten. De andra två händelserna justerar de Y-värden som vi har lagrat i BlockPositions array så att de också håller sig inline med blockruten.

Om du testar spelet igen vid denna punkt, bör byte oftast fixas.

Vid den här tiden finns det fortfarande ett annat problem som vi behöver hantera för att göra bytesarbete korrekt. Kör spelet och försök att göra en nedväxling med någon av blocken i den nedre raden medan den raden är delvis under den nedre delen av spelfältet:

Gör bytet medan blocken ligger bakom gränsen, som de markerade i bilden ovan.

Om du gjorde det korrekt bör du se att inget hände och blocken bytte inte ut. Om du väntade för länge kan blocken ha bytts eftersom de hade flyttat över spelfältets gräns igen, så om det hände försök igen när de faller och du borde se problemet uppstår.

Problemet är ganska enkelt att lösa och förstå. Om du tittar på koden för nedåtgående swappar borde du hitta händelsen som vi lade till i föregående handledning som hindrar spelaren från att göra nedåtväxlingar som leder till att Blocken faller ner längst ner i spelfältet. Eftersom detta uttalande förhindrar spelaren att göra nedåtväxlingar när BottomBlock objektet är lägre än blockets initiala Y-position, det förhindrar att blocken byts ut när de har fallit och tillåter dig bara att göra byte igen när de har flyttat förbi sin ursprungliga position igen.

För att åtgärda detta uttalande kommer vi att göra en liten förändring till villkoret:

Skick: BottomBlock> Jämför Y-jämförelse = Mindre eller lika Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Villkoret ska nu se ut så här:


Denna modifiering innebär att en nedväxling endast kan ske medan BottomBlock objektet är högst ett halvt block under Y-positionen som blocket börjar. Det betyder också att när vi börjar gyta nya rader av block och skjuta dem på skärmen från botten, kommer dessa block bara att kunna bytas in på så sätt när minst hälften av blocket är synligt.

Vi kommer också att lägga en liknande inskränkning i alla våra byteshändelser för att se till att alla blir användbara samtidigt, och att ett block inte kan bytas alls tills minst hälften är synlig. Återigen kommer det också att hjälpa till när vi integrerar systemet som genererar nya rader av block. För att göra detta lägger vi till ett nytt villkor för var och en av de återstående tre swap-händelserna.

Villkoren vi lägger till kommer att vara exakt samma som den vi just ändrade i BottomBlock händelse, förutom att de kommer att referera till TopBlock, RightBlock, och LeftBlock objekt istället för BottomBlock objekt, beroende på vilken händelse den är i.

Det nya villkoret för TopBlock Händelsen ska vara:

Skick: TopBlock> Jämför Y-jämförelse = Mindre eller lika Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Det nya villkoret för LeftBlock Händelsen ska vara:

Skick: LeftBlock> Jämför Y-jämförelse = Mindre eller lika Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Det nya villkoret för RightBlock Händelsen ska vara:

Skick: RightBlock> Jämför Y-jämförelse = Mindre eller lika Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Hela din På DragDrop droppe Händelsen ska nu se ut så här:


Med dessa nya förutsättningar på plats har vi fixat vår bytesmekanik och vi har börjat förbereda de befintliga systemen för nästa system som vi lägger till: den som kommer att spawna nya rader av block.


3. Gytning Fler block

Nu när vi har blocken att flytta upp i en konstant takt, måste vi göra de nya raderna av block spjut på rätt tid och låta spelaren fortsätta spela så länge som de vill. Vi ska använda en funktion för att gissa de nya raderna av block, och vi ska använda en händelse som upptäcker när blocken är inline med SPAWNY för att utlösa den funktionen.

Så först, låt oss göra själva funktionen.

Händelse: Skick: Funktion> På funktion Namn = "SpawnNewBlocks" Skick: System> För namn = "X" Startindex = 0 Slutindex = 7 Åtgärd: System> Skapa objekt Objekt = Blocklag = 1 X = SPAWNX + (loopIndex "X")) * (Block.Width + 2) Y = SPAWNY + (Block.Width + 2) Åtgärd: Block> Ange värde Instansvariabel = Färgvärde = golv (Slumpmässig (1,7)) Åtgärd: System> Lägg till Till Variabel = NumBlock Värde = 1

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


När den används kommer denna funktion att skapa en rad av block under den nedre raden av block i spelfältet. Som det står nu använder vi faktiskt inte denna funktion när som helst, så låt oss göra händelsen som gör det:

Händelse: Skick: System> Varje X sekunder Intervall (sekunder) = CurrentSpeed ​​Skick: Block> Jämför Y Jämförelse = Jämställd med Y = SPAWNY Villkor: Invertera: Block> Är draggning: Funktion> Samtalsfunktion Name = "SpawnNewBlocks"

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


Händelsen som vi just skapat kontrollerar blockens Y-position varje gång de flyttas. Om det hittar några block som är inline med SPAWNY, det utlöser SpawnNewBlocks () funktion som vi diskuterade tidigare. Det kontrollerar också att säkerställa att blocket det finner är inte det som släpas av spelaren.

Om du testar ditt spel på den här tiden kommer det att fungera, men du bör märka en konstig fråga. När du börjar spelet kommer dina block att falla som om det inte finns några block under dem, men efter det att allt fungerar perfekt och nya block sprungas när de behövs.

Detta händer därför att när spelet först börjar, behandlar det gravitationskoden innan koden som skapar nya rader av block. För att fixa detta kommer vi att göra en liten anpassning till koden som gör den första gruppen av block så att de hälls under den punkt där en ny rad skulle behövas. Detta gör det möjligt att undvika att köra gravitationskoden omedelbart och låter den skapa den nya raden av block när de befintliga blocken är på rätt plats.

Gå till händelsen som gör den första gruppen av block och ändra åtgärden som faktiskt skapar blocket. Ändra åtgärden till det här:

Åtgärd: System> Skapa objekt Objekt = Blocklag = 1 X = SPAWNX + (loopIndex ("X")) * (Block.Width + 2) Y = SPAWNY (loopIndex ("Y")) * (Block.Width + 2) + 5

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


Denna modifiering innebär att blocken kommer att spawna fem pixlar nedan SPAWNY. Det innebär att blocken faktiskt måste flytta upp fem gånger innan en ny rad kommer att hälla och löser vårt problem.


4. En liten bit av animering

Vid denna tidpunkt rör våra block och vi har nya rader skapade. Också i det, kom ihåg att vi tidigare hindrade spelaren att använda något block tills minst hälften av blocket är synligt. Medan det här är en bra funktion kan spelaren inte förstå varför ett block inte kan användas omedelbart när det blir synligt, även om inte mycket av det är synligt vid den tiden.

På grund av detta potentiella användargränssnitt problem kommer vi att göra varje block använda grå block sprite (i början av blockets animationsramar) när det är i detta oanvändbara tillstånd. Detta gör det klart för spelaren när ett block blir användbart och det ger oss chansen att äntligen använda vår senaste blockbild.

Du kan se ett exempel på hur det kommer att se ut när blocken går från att vara inaktiv till aktiv i GIF nedan:


Den händelse som vi skapar kommer också att innehålla ett andra villkor som kontrollerar för att se till att blocket det tittar på inte dras. Detta villkor tillåter oss att se till att när spelaren drar ett block under den punkt där block blir användbar, kommer det inte att ändra sin bild så att den är grå och kommer att förbli den färg som den ska vara.

För att göra detta animationsarbete måste vi först lägga till en ny händelse:

Händelse: Villkor: Block> Jämför Y Jämförelse = Större än Y = SPAWNY + ((Block.Width + 2) / 2) Skick: Invertera: Block> Slår åtgärd: Block> Ställ in ram Ramnummer = 0

Den nya händelsen ska se ut så här:


Du borde nu kunna testa ditt spel och du bör se att blocken använder den grå bilden när de ligger under den punkt som de blir användbara.


5. Aktivera och inaktivera Drag / Drop

Om du kör spelet nu kommer du märka att även om blocken inte kan bytas ut när de är gråa, kan de grå blocken fortfarande dras runt och manipuleras. Detta beror på att vi aldrig inaktiverade drag / släppfunktionerna i blocket när vi hindrade spelaren från att byta ut dem.

För att förhindra att de grå blocken flyttas, ändrar vi händelsen som vi skapade i föregående avsnitt. Först lägger vi till en ny åtgärd som stänger av dragen när blocket är under den punkt det blir användbart.

Lägg till den här åtgärden till händelsen som vi skapade tidigare:

Åtgärd: Block (DragDrop)> Ställ in aktiverat State = Disabled

Vi kommer också att lägga till ett annat uttalande för denna händelse som låter blocket dras igen när det ligger över den punkt som blocket blir användbart:

Händelse: Skick: Övrig åtgärd: Block (DragDrop)> Ställ in aktiverad State = Aktiverad

Med båda dessa ändringar bör händelsen se ut så här:


Om du testar spelet vid denna tidpunkt bör blocken inte längre användas när de är gråa och ska fungera på samma sätt som de alltid har när de inte är.


6. Hastighetsändringar

Det sista jag vill täcka i den här artikeln är det system som gör att vi kan ändra spelets hastighet över tiden. Specifikt är detta det system som gör blocken rörligare när spelaren eliminerar fler av dem.

Det system vi ska skapa är relativt enkelt: varje gång spelaren får ett antal poäng kommer spelets hastighet att öka baserat på en modifierare som vi ska skapa och antalet poäng som spelaren behöver för att få Nästa hastighetsökning ökar baserat på en andra modifierare.

Innan vi faktiskt kan börja göra händelserna för det här systemet skapar vi ett par globala variabler för att hantera de nya funktionerna för oss:

Global Variabel: SPEEDMOD Typ = Antal Initialt värde = 0,8 Konstant = Ja Global Variabel: PointsForSpeedUp Typ = Antal Initialt värde = 400 Konstant = Ingen Global Variabel: PointsBetweenSpeedUps Type = Antal Initialt värde = 400 Konstant = Ingen Global Variabel: POINTSFORSPEEDUPMOD Typ = Antal Initial värde = 1,4 Konstant = Ja

Dina nya variabler ska se ut så här:


Nu när vi har variablerna på plats, förklarar jag vad varje gör.

  • SPEEDMOD är variabeln vi kommer att multiplicera hastigheten genom att ändra den när spelaren når antalet poäng som de behöver för att öka hastigheten.
  • PointsForSpeedUp är antalet poäng spelaren behöver för att slå nästa steg upp.
  • PointsBetweenSpeedUps representerar hur mycket PointsForSpeedUp variabel ökar när spelaren snabbar uppåt, för att justera den så att nästa hastighet upp tar ännu fler poäng. Just nu är det 400, som PointsForSpeedUp, men när spelaren faktiskt blir snabbare blir den multiplicerad med POINTSFORSPEEDUPMOD innan det läggs till PointsForSpeedUp.
  • Till sist, POINTSFORSPEEDUPMOD är den variabel som vi kommer att använda för att ändra antalet poäng som spelaren behöver för att öka sin hastighet en gång över den som de senast fick.

Tillsammans med att vi ställer in variablerna måste vi också skapa ett nytt spriteobjekt som kommer att fungera som varning för spelaren när hastigheten ökar.

Gå till Layout 1 och följ dessa steg för att skapa den nya spriten:

  1. Lägg in en ny Sprite objekt på Layout 1.
  2. Med animeringsredigeraren öppnar du bilden SpeedIncImage.png.
    1. Ställ in namn till SpeedIncreaseIndicator.
    2. Ställ in Lager till Spelfält.
    3. Ställ in Placera till 188, 329.
    4. Uppsättning Initial synlighet till Osynlig.
      1. Lägg till en Blekna Beteende till Sprite.
      2. Uppsättning Aktiv vid Start till Nej.
      3. Ställ in Fade out tid till 2,5.
      4. Uppsättning Förstöra till Nej.

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


Nu ska vi faktiskt skapa händelsen som ändrar hastigheten:

Händelse: Skick: Funktion> På funktion Namn = "CheckForSpeedUp" Skick: System> Jämför variabel Variabel = Resultatjämförelse = Större eller lika värde = PointsForSpeedUp Åtgärd: SpeedIncreaseIndicator> Ställ synlig Synlighet = Synlig åtgärd: SpeedIncreaseIndicator> Starta fade Action System> Ange värde Variabel = CurrentSpeed ​​Value = CurrentSpeed ​​* SPEEDMOD Action System> Ange värde Variabel = PointsBetweenSpeedUp Value = PointsBetweenSpeedUp * POINTSFORSPEEDUPMOD Åtgärd: System> Lägg till Variabel = PointsForSpeedUp Value = PointsBetweenSpeedUp

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


När denna funktion kallas kontrollerar den att se om spelaren har gjort tillräckligt med poäng för att garantera en hastighetsökning. Om de har, då:

  • det aktiverar sprite som berättar spelaren hastigheten har ökat genom att göra den synlig och starta Fade
  • det ökar hastigheten genom att multiplicera den med modifieraren
  • Det bestämmer hur många poäng som behövs innan nästa steg upp, och
  • det lägger till det värdet till det totala antalet poäng som spelaren måste ha innan hastigheten ökar igen.

Med den här funktionen komplett måste vi bara se till att det blir kallat. Gå till GivePoints () funktionen och lägg till den här åtgärden till slutet av den primära händelsen och underevenemanget:

Åtgärd: Funktion> Samtalsfunktion Namn = "CheckForSpeedUp"

De GivePoints () funktionen ska nu se ut så här:


Med den händelsen slutförda borde du kunna testa ditt spel och se fartfartssystemet i åtgärd.

Tips: När jag spelade med det mer fann jag att dessa värden kände lite av, så jag föreslår att du tar lite tid att experimentera med systemet och hitta de värden du känner dig mest bekväma med.


Slutsats

Vi har behandlat många olika ämnen i den här artikeln, men allt vi behandlade var direkt eller indirekt relaterat till att rörelsessystemet fungerade som vi ville. Medan det tog lite tid och krävde att vi skulle göra fler system än vad vi kanske hade förväntat oss i början var utbetalningen värt det och vi slutade med ett mycket starkt system i slutändan.

På grund av hur mycket vi redan har täckt, tycker jag att det här är ett bra ställe att avsluta den här artikeln. Nästa artikel ska vara den slutliga handledningen i denna serie och vi kommer att täcka många mindre ämnen inom den, men den största tingen vi täcker är definitivt eliminering av färdiga matchningar.

Om du vill börja försöka lista ut hur vi kommer att eliminera dem, ta en titt på hur vi upptäcker matchningar till att börja med. Det system vi skapar kommer att vara mycket lik det systemet, förutom att det kommer att använda matcherna på ett annat sätt. Börja tänka på det och se vad du kan komma med, och jag kommer att se dig tillbaka här nästa gång för den senaste stora handledningen i serien.