Lag et Match-3-spill i Construct 2 Block-bevegelse

I den forrige delen av denne serien gjorde vi noen små, men viktige endringer i mange av systemene vi opprettet for vårt Match-3-spill. Med disse forbedringene implementert, skal vi nå komme tilbake på sporet og implementere ett av de to siste hovedsystemene for spillet: Block Movement-systemet.

Denne opplæringen tar deg gjennom hele utviklingen av systemet som gjør at blokkene kan stige til toppen av skjermen, og dekker også opprettelsen av alle de mindre systemene vi må implementere for å støtte bevegelsessystemet. Mens emnene jeg dekker i denne opplæringen ikke er for komplekse, er det mye å gå over - så la oss komme til det.


Final Game Demo

Her er en demonstrasjon av spillet vi jobber med i denne serien:




1. Flytte blokker opp

Før vi begynner å flytte blokkene, må vi gjøre en liten forandring til hendelsene som kaster Blocks. Gå til System> Ved oppstart hendelse og endre Y til sløyfe å gå fra 0 til 3, i stedet for fra 0 til 7 som det opprinnelig gjorde.

Hendelsen skal nå se slik ut:


Årsaken til at vi har gjort denne endringen, er at vi vil at spillet skal starte med færre blokker på skjermen, slik at det ikke slutter så fort når vi legger til et spill over i neste opplæringsveiledning.

Deretter skal vi opprette en variabel som representerer blokkens hastighet:

Global Variabel: Navn = CurrentSpeed ​​Type = Nummerverdi = 0.2

Nå skal vi opprette hendelsen som faktisk beveger blokkene:

Hendelse: Tilstand: System> Hver X Seconds Interval (sekunder) = CurrentSpeed ​​Action: Blokker> Flytt i vinkelvinkel = -90 Avstand = 1

Hendelsen skal se slik ut:


Hvis du kjører spillet etter at du har lagt til denne hendelsen, er det første som du bør se, at blokkene faller, på grunn av tyngdekraften vi implementerte i en tidligere opplæring. Deretter skal blokkene sakte stige til de er i sin opprinnelige posisjon, og deretter slippe igjen. Dette vil gjenta uendelig så lenge du ikke gjør noe for blokkene.

Dette skjer fordi blokkene beveger seg forbi punktet hvor tyngdekraften skal sparke inn, og de oppdager at det ikke er noen blokker under dem, noe som får dem til å falle. Selv om dette er et problem, er det ikke den første jeg vil se på.


2. Fixing Bytte

Kjør spillet, og prøv å lage en bytte av noe slag. Når du gjør dette, bør du se at blokkene begynner å stikke seg fast bak hverandre, sitter fast i stillinger som ikke er justert med rutenettet, og bare vanligvis mislykkes. Det er to grunner til dette problemet.

Det første problemet er at selv om vi beveger Blocks selv, flytter vi ikke LeftBlock, RightBlock, TopBlock, og BottomBlock objekter med dem, noe som betyr at blokkene du bruker for å oppdage swaps, ikke beveger seg med blokkruten - de sitter bare i den posisjonen de er satt til når du først henter en blokk.

Så når du prøver å bytte, blir blokkene satt ut av sted, fordi swap-deteksjonsblokkene ikke har justert seg til rutenettet. (Dette er også grunnen til det andre problemet vi har, som er at vi ikke endrer stillingene vi lagret i BlockPositions array heller.)

GIF nedenfor viser dette problemet:


Som du kan se i GIF, beveger swap-deteksjonsblokkene seg ikke, selv om blokkene selv er.

For å løse begge disse problemene, legger vi til noen flere handlinger til hendelsen vi nettopp har opprettet:

Handling: BottomBlock> Flytt i vinkelvinkel = -90 Avstand = 1 Handling: LeftBlock> Flytt i vinkelvinkel = -90 Avstand = 1 Handling: HøyreBlock> Flytt i vinkelvinkel = -90 Avstand = 1 Handling: TopBlock> Flytt i vinkelvinkel = -90 Avstand = 1 Action: BlockPositions> Angi XY X = 0 Y = 1 Verdi = BlockPositions.At (0,1) - 1 Action: BlockPositions> Angi XY X = 1 Y = 1 Verdi = BlockPositions.At ( 1,1) - 1

Hendelsen skal nå se slik ut:


De første fire handlingene vi nettopp har lagt til, justerer stillingene til LeftBlock, TopBlock, RightBlock, og BottomBlock gjenstander slik at de forblir inline med blokkruten. De to andre hendelsene justerer de Y-verdiene vi har lagret i BlockPositions array slik at de også forblir inline med blokkruten.

Hvis du tester spillet igjen på dette tidspunktet, bør bytte for det meste være fast.

På dette punktet er det fortsatt et annet problem vi må håndtere for å gjøre byttearbeid riktig. Kjør spillet og prøv å lage en nedbytting med noen av blokkene i den nederste raden mens den raden er delvis under bunnen av spillfeltet:

Gjør byttet mens blokkene ligger bak grensen, som de fremhevede i bildet ovenfor.

Hvis du gjorde dette riktig, bør du se at ingenting skjedde og blokkene byttet ikke. Hvis du ventet for lenge, kan blokkene ha byttet fordi de hadde flyttet over spillfeltets grense igjen, så hvis dette skjedde, prøv igjen når de faller, og du bør se dette problemet oppstå.

Dette problemet er ganske enkelt å løse og forstå. Hvis du ser på koden for nedadgående swaps, bør du finne hendelsen som vi la til i den forrige opplæringen, som forhindrer spilleren i å lage nedover swaps som fører til at Blokken faller bunnen av spillfeltet. Siden denne setningen forhindrer spilleren i å lage ned swaps når BottomBlock objektet er lavere enn blokkens innledende Y-posisjon, det forhindrer at blokkene blir byttet når de har falt og bare lar deg lage swaps igjen når de har flyttet forbi sin opprinnelige posisjon igjen.

For å fikse denne uttalelsen skal vi gjøre en liten endring til tilstanden:

Tilstand: BottomBlock> Sammenlign Y-sammenligning = Mindre eller lik Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Tilstanden skal nå se slik ut:


Denne endringen innebærer at en nedveksling kun kan skje mens BottomBlock objekt er høyst en halv blokk under Y-posisjon blokkene starter. Dette betyr også at når vi begynner å gyte nye rader med blokker og skyve dem på skjermen fra bunnen, vil disse blokkene bare kunne byttes inn denne måten når minst halvparten av blokken er synlig.

Vi vil også legge til en lignende begrensning i alle våre byttehendelser for å sikre at alle blir brukbare samtidig, og at en blokk ikke kan byttes i det hele tatt til minst halvparten av det er synlig. Igjen, dette vil også hjelpe når vi integrerer systemet som henter nye rader med blokker. For å gjøre dette legger vi til en ny betingelse for hver av de gjenværende tre bytte hendelsene.

Vilkårene vi legger til, vil være nøyaktig det samme som det vi nettopp har endret i BottomBlock arrangement, bortsett fra at de vil referere til TopBlock, RightBlock, og LeftBlock objekter i stedet for BottomBlock objekt, avhengig av hvilken hendelse den er i.

Den nye tilstanden for TopBlock Hendelsen skal være:

Tilstand: TopBlock> Sammenlign Y-sammenligning = Mindre eller lik Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Den nye tilstanden for LeftBlock Hendelsen skal være:

Tilstand: LeftBlock> Sammenlign Y-sammenligning = Mindre eller lik Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Den nye tilstanden for RightBlock Hendelsen skal være:

Tilstand: HøyreBlock> Sammenlign Y-sammenligning = Mindre eller lik Y-koordinat = SPAWNY ((Block.Width + 2) / 2)

Hele ditt På DragDrop slipp Hendelsen skal nå se slik ut:


Med disse nye forholdene på plass, har vi løst vår bytte mekanikk og vi har begynt å forberede eksisterende systemer for det neste systemet vi legger til: den som vil gyte nye rader med blokker.


3. Gyting Flere blokker

Nå som vi har blokkene flyttet opp med en konstant hastighet, må vi gjøre de nye radblokkene gyte på riktig tidspunkt, og la spilleren fortsette å spille så lenge de vil. Vi skal bruke en funksjon for å gyte de nye blokkene, og vi skal bruke et arrangement som oppdager når blokkene er inline med SPAWNY å utløse den funksjonen.

Så først, la oss gjøre funksjonen selv.

Begivenhet: Tilstand: Funksjon> På funksjon Navn = "SpawnNewBlocks" Tilstand: System> For Navn = "X" Start Indeks = 0 End Index = 7 Handling: System> Opprett objekt Objekt = Blokklag = 1 X = SPAWNX + (loopIndex "X")) * (Block.Width + 2) Y = SPAWNY + (Block.Width + 2) Handling: Blokk> Angi verdi Instansvariabel = Fargeverdi = gulv (Tilfeldig (1,7)) Handling: System> Legg til til Variabel = NumBlocks Verdi = 1

Din nye hendelse skal se slik ut:


Når den brukes, vil denne funksjonen opprette en rad blokker under den nederste raden av blokker i spillfeltet. Som det står nå, bruker vi imidlertid ikke denne funksjonen når som helst, så la oss gjøre arrangementet som gjør det:

Begivenhet: Tilstand: System> Hvert X sekunder Intervall (sekunder) = CurrentSpeed ​​Tilstand: Blokk> Sammenlign Y Sammenligning = Lik til Y = SPAWNY Tilstand: Inverter: Blokk> Er Dra Action: Funksjon> Anropsfunksjon Navn = "SpawnNewBlocks"

Din nye hendelse skal se slik ut:


Hendelsen vi nettopp har opprettet, kontrollerer Y-posisjonen til blokkene hver gang de blir flyttet. Hvis det finner noen blokker som er inline med SPAWNY, det utløser SpawnNewBlocks () fungere som vi diskuterte tidligere. Det sjekker også for å sikre at blokken den finner er ikke den som blir trukket av spilleren.

Hvis du tester spillet ditt på dette punktet, vil det fungere, men du bør legge merke til et merkelig problem. I det øyeblikket du starter spillet, vil blokkene dine falle som om det ikke er noen blokker under dem, men etter det punktet virker alt perfekt, og nye blokker blir tilplantet når de trengs.

Dette skjer fordi, når spillet først starter, behandler det tyngdekraften før koden som skaper nye rader med blokker. For å fikse dette, skal vi gjøre en liten tilpasning til koden som gyter den første gruppen av blokker, slik at de blir skapt under det punktet der en ny rad ville være nødvendig. Dette gjør det mulig å unngå å kjøre gravitasjonskoden umiddelbart, og lar den lage den nye raden med blokker når de eksisterende blokkene er på riktig sted.

Gå til arrangementet som henter den første gruppen av blokker og endre handlingen som faktisk lager blokken. Endre handlingen til dette:

Handling: System> Opprett objekt Objekt = Blokkelag = 1 X = SPAWNX (loopIndex ("X")) * (Block.Width + 2) Y = SPAWNY (loopIndex ("Y")) * (Block.Width + 2) + 5

Hendelsen skal nå se slik ut:


Denne modifikasjonen betyr at blokkene vil gi fem piksler under SPAWNY. Dette betyr at blokkene faktisk må flytte opp fem ganger før en ny rad vil gyte, og løser problemet vårt.


4. En bit av animasjon

På dette punktet beveger våre blokker seg, og vi har nye rader opprettet. På toppen av det, husk at tidligere vi forhindret spilleren til å bruke noen blokk til minst halvparten av blokken er synlig. Selv om dette er en god funksjon, kan spilleren ikke forstå hvorfor en blokk ikke kan brukes umiddelbart når den blir synlig, selv om ikke mye av det er synlig på det tidspunktet.

På grunn av dette potensielle brukergrensesnittet, skal vi gjøre hver blokk bruk den grå blokksprite (i begynnelsen av blokkens animasjonsrammer) når den er i denne ubrukelige tilstanden. Dette vil gjøre det klart for spilleren når en blokk blir brukbar, og det vil gi oss sjansen til å endelig bruke vårt siste blokkbilde.

Du kan se et eksempel på hvordan det vil se ut når blokkene går fra å være inaktive til aktive i GIF nedenfor:


Hendelsen vi lager vil også inkludere en annen betingelse som kontrollerer for å sikre at blokken den ser på, blir ikke trukket. Denne betingelsen tillater oss å sikre at når spilleren drar en blokk under det punktet hvor blokkene blir brukbare, vil det ikke endre bildet slik at det er grått og vil forbli den fargen den skal være.

For å gjøre dette animasjonsarbeidet må vi først legge til en ny hendelse:

Begivenhet: Tilstand: Blokk> Sammenlign Y Sammenligning = Større enn Y = SPAWNY + ((Blokk.Width + 2) / 2) Tilstand: Omvendt: Blokk> Slår handling: Blokk> Angi ramme Ramme nummer = 0

Den nye hendelsen skal se slik ut:


Du bør nå kunne teste spillet ditt, og du bør se at blokkene bruker det grå bildet når de er under det punktet de blir brukbare.


5. Aktivere og deaktivere dra / slipp

Hvis du kjører spillet nå, vil du legge merke til at selv om blokkene ikke kan byttes med hverandre når de er grå, kan de grå blokkene fortsatt bli trukket rundt og manipulert. Dette skyldes at vi aldri deaktiverte Drag / Drop-funksjonene til blokken når vi forhindret spilleren i å bytte med dem.

For å hindre at de grå blokkene kan flyttes, endrer vi hendelsen vi opprettet i forrige seksjon. Først vil vi legge til en ny handling som slår av dra når blokken er under punktet det blir brukbart.

Legg til denne handlingen til hendelsen vi opprettet tidligere:

Handling: Blokk (DragDrop)> Sett aktivert State = Deaktivert

Vi kommer også til å legge til en Else-setning for denne hendelsen som lar blokken bli trukket igjen når den er over det punktet at blokken blir brukbar:

Hendelse: Tilstand: Annet handling: Blokk (DragDrop)> Sett aktivert State = Aktivert

Med begge disse endringene skal arrangementet se slik ut:


Hvis du tester spillet på dette tidspunktet, bør blokkene ikke lenger brukes når de er grå, og skal fungere på samme måte som de alltid har når de ikke er.


6. Hastighetsendringer

Det siste jeg vil dekke i denne artikkelen er systemet som gjør at vi kan endre spillets fart over tid. Spesielt er dette systemet som vil gjøre blokkene bevege seg raskere ettersom spilleren eliminerer flere av dem.

Systemet vi skal skape er relativt enkelt: Hver gang spilleren får noen poeng, vil spillets hastighet øke basert på en modifikator som vi skal skape, og antall poeng spilleren trenger for å få neste hastighetsøkning vil endres basert på en andre modifikator.

Før vi faktisk kan begynne å lage hendelsene for dette systemet, oppretter vi et par globale variabler for å håndtere de nye funksjonene for oss:

Global Variabel: SPEEDMOD Type = Antall Initial verdi = 0,8 Konstant = Ja Global Variabel: PointsForSpeedUp Type = Antall Initial verdi = 400 Konstant = Ingen Global Variabel: PointsBetweenSpeedUps Type = Antall Initial verdi = 400 Konstant = Ingen Global Variabel: POINTSFORSPEEDUPMOD Type = Antall Initial verdi = 1,4 Konstant = Ja

Dine nye variabler skal se slik ut:


Nå som vi har variablene på plass, forklarer jeg hva hver gjør.

  • SPEEDMOD er variabelen vi vil multiplisere hastigheten ved å endre den når spilleren når antallet poeng de trenger for å øke hastigheten.
  • PointsForSpeedUp er antall poeng spilleren trenger for å slå neste hastighet opp.
  • PointsBetweenSpeedUps representerer hvor mye PointsForSpeedUp variabel vil øke når spilleren får fart, for å justere det slik at neste hastighet opp tar enda flere poeng. Akkurat nå er det 400, som PointsForSpeedUp, men når spilleren faktisk får fart, blir det multiplisert med POINTSFORSPEEDUPMOD før det legges til PointsForSpeedUp.
  • Endelig, POINTSFORSPEEDUPMOD er variabelen vi vil bruke til å endre antall poeng spilleren trenger for å få økt hastighet en annen gang forbi den som de sist fikk.

Sammen med å sette opp variablene, må vi også opprette et nytt spriteobjekt som vil fungere som varsel for spilleren når hastigheten øker.

Gå til Layout 1 og følg disse trinnene for å opprette den nye sprite:

  1. Sett inn en ny Sprite objekt på Layout 1.
  2. Med animasjonseditoren åpner du bildet SpeedIncImage.png.
    1. Sett Navn til SpeedIncreaseIndicator.
    2. Sett Lag til Spillfelt.
    3. Sett Stilling til 188, 329.
    4. Sett Innledende synlighet til Usynlig.
      1. Legg til en Falme Oppførsel til sprite.
      2. Sett Aktiv ved Start til Nei.
      3. Sett Fade ut tid til 2.5.
      4. Sett Ødelegge til Nei.

Din layout bør nå se slik ut:


Nå skal vi faktisk opprette hendelsen som endrer hastigheten:

Begivenhet: Tilstand: Funksjon> På funksjon Navn = "CheckForSpeedUp" Tilstand: System> Sammenlign variabel Variabel = Score Sammenligning = Større eller lik verdi = PointsForSpeedUp Action: SpeedIncreaseIndicator> Sett synlig Sikt = Synlig handling: SpeedIncreaseIndicator> Start fade Action System> Angi verdi Variabel = CurrentSpeed ​​Value = CurrentSpeed ​​* SPEEDMOD Action System> Angi verdi Variabel = PointsBetweenSpeedUp Value = PointsBetweenSpeedUp * POINTSFORSPEEDUPMOD Handling: System> Legg til Variabel = PointsForSpeedUp Value = PointsBetweenSpeedUp

Din begivenhet skal se slik ut:


Når denne funksjonen blir kalt, kontrollerer den å se om spilleren har scoret nok poeng for å garantere en hastighetsøkning. Hvis de har, så:

  • det aktiverer sprite som forteller spilleren hastigheten har økt ved å gjøre den synlig og starte Fade
  • det øker hastigheten ved å multiplisere det med modifikatoren
  • Det bestemmer antall poeng som trengs før neste fart, og
  • det legger til verdien til det totale antall poeng spilleren må ha før hastigheten øker igjen.

Med denne funksjonen fullført, må vi bare sørge for at den blir kalt. Gå til GivePoints () funksjon og legg til denne handlingen til slutten av den primære hendelsen og underarrangementet:

Handling: Funksjon> Anropsfunksjon Navn = "CheckForSpeedUp"

De GivePoints () funksjonen skal nå se slik ut:


Med den hendelsen fullført, bør du kunne teste spillet ditt og se fartfartssystemet i gang.

Tips: Da jeg spilte med det mer, fant jeg ut at disse verdiene følte seg litt av, så jeg foreslår at du tar litt tid å eksperimentere med systemet og finne de verdiene du føler deg mest komfortabel med.


Konklusjon

Vi har dekket mange forskjellige emner i denne artikkelen, men alt vi behandlet var direkte eller indirekte relatert til å få bevegelsessystemet til å fungere som vi ønsket. Mens det tok litt tid, og forpliktet oss til å lage flere systemer enn vi kunne ha forventet i begynnelsen, var utbetalingen verdt det, og vi endte opp med et veldig sterkt system til slutt.

På grunn av hvor mye vi allerede har dekket, synes jeg dette er et bra sted å avslutte denne artikkelen. Den neste artikkelen bør være den endelige opplæringen i denne serien, og vi skal dekke mange mindre emner innenfor den, men den største tingen vi dekker er definitivt eliminering av prefabrikkerte kamper.

Hvis du vil begynne å prøve å finne ut hvordan vi vil eliminere dem, ta en titt på hvordan vi oppdager samsvarer til å begynne med. Systemet vi lager vil være veldig lik det systemet, bortsett fra at det vil bruke kamper den finner på en annen måte. Begynn å tenke på det og se hva du kan komme med, og jeg vil se deg tilbake her neste gang for den siste store opplæringen i serien.