Lag et Match-3-spill i Construct 2 Match Detection

Så langt har denne serien dekket grunnlaget for å sette opp et Match-3-spill, og implementere de første spillelementene som blokkbytte. I denne opplæringen skal vi bygge videre på alt dette, og begynne å oppdage når spilleren har laget en kamp.


Final Game Demo

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




1. Oppdage en kamp

For nå skal vi bare implementere en grunnleggende versjon av det matchende systemet, med fokus på å finne når kampene eksisterer og ødelegge matchede blokker. I senere artikler vil vi fortsette å utvikle og fremme systemet.

Tips: Du bør lese inn hvordan en rekursiv funksjon fungerer, hvis du ikke allerede vet det; I hovedsak er det en funksjon som kaller seg. Rekursive funksjoner kan fungere på samme måte som sløyfer, men siden de også kan ta inn og returnere variabler, har de mange flere bruksområder enn sløyfer gjør.

Som med den forrige opplæringen vil jeg først diskutere hvordan systemet skal fungere, og deretter prøve å bygge det.

  • Kampsystemet vil gjenta gjennom hver forekomst av Blokkere gjenstand.
  • For hver Blokkere, det vil passere farge og posisjon av blokken den ser på i en rekursiv funksjon som vil se på den horisontale eller vertikale naboen, og avgjøre om de er i samme farge.
  • Hvis en kamp er funnet, vil den ringe funksjonen igjen med plasseringen og fargen til den nye blokken, i stedet for den opprinnelige.
  • Dette vil fortsette til det ikke finner noen kamp. På det tidspunktet vil det sjekke hvor mange kamper den fant.
  • Hvis den fant tre eller flere kamper, markerer den alle blokkene det bare så på som matchet med IsMatched instansvariabel vi laget i en av de foregående opplæringsprogrammene; ellers gjør det ingenting.
  • Til slutt, når alle blokkene er blitt sjekket, vil funksjonen ødelegge hver blokk som er merket som en kamp.

Først trenger vi et arrangement som kan lukte gjennom hver Blokkere. Måten jeg bygde systemet på, det iterates faktisk gjennom blokkene to ganger: en gang for å sjekke om vertikale kamper, og en gang for å sjekke om horisontale kamper. Avhengig av hvilken sjekk det gjør, vil det bruke en annen funksjon til å faktisk se etter kampen.

Det aller første vi må gjøre er å lage en Global variabel for å holde oversikt over hvor mange matchende blokker vi har funnet i en gitt iterasjon:

Global Variable Name: "NumMatchesFound" Type = Nummerverdi = 0

La oss nå lage Begivenhet som vil iterere gjennom blokkene:

Hendelse: Funksjon> På funksjon Navn: "FindMatches" Sub-Event: System> For hvert objekt: Block Action: System> Angi verdi NumMatchesFound = 1 Handling: Funksjon> Anropsfunksjon Navn: "CheckMatchesX" Parameter 0: Block.X Parameter 1 : Block.Y Parameter 2: Block.Color Sub-Event: System> Sammenlign Variable NumMatchesFound> = 3 Handling: Blokker> Angi Boolean IsMatched = Ekte delhendelse: System> For hvert objekt: Blokker Handling: System> Angi verdi NumMatchesFound = 1 Funksjon: Funksjon> Anropsfunksjon Navn: "CheckMatchesY" Parameter 0: Block.X Parameter 1: Block.Y Parameter 2: Block.Color Sub-Event: System> Sammenlign Variable NumMatchesFound> = 3 Handling: Blokk> Angi Boolean IsMatched = True Sub-Event: Block> Er Boolean instans variabel satt System> Vent Second> 0.1 Block> Destroy

Koden din skal se slik ut:


I dette tilfelle vierer gjennom hver blokk og sender dem inn CheckMatchesX eller CheckMatchesY, funksjonene som vil sjekke for å se om naboblokken er en kamp.

For å sende blokken inn i funksjonen, sender vi funksjonene tre forskjellige parametere:

  • Parameter 0 er blokkens X-posisjon
  • Parameter 1 er blokkens Y-posisjon
  • Parameter 2 er fargen.

Etter hver blokk er sendt inn i en av funksjonene og funksjonen er ferdig, kontrollerer den NumMatchesFound for å se om det fant tre eller flere matchende blokker, og merker deretter blokkene som matchet hvis det gjorde det.

Til slutt, hver blokk som er merket som værende matchet blir ødelagt etter .1 sekunder går. Dette vente erklæringen er der for å tillate spillet å bytte bilder for blokkene til bildet som indikerer at de er matchet, og å gi spilleren et øyeblikk til å legge merke til denne endringen.

(Mens du kan fjerne vente uttalelse uten å påvirke spillingen negativt, gjør det lettere for spilleren å forstå, og bremser spillet akkurat slik at spilleren lett kan holde rede på hva som skjer.)


2. De to kontrollfunksjonene

Neste må vi lage CheckMatchesX og CheckMatchesY funksjoner. Disse funksjonene fungerer på samme måte som iteratorene ovenfor, idet det vil være en versjon for å sjekke horisontale kamper, CheckMatchesX, og en for vertikale kamper, CheckMatchesY.

Horisontale kontroller

La oss først bygge horisontal sjekkfunksjonen:

Hendelse: Funksjon> På funksjon Navn: "CheckMatchesX" Sub-Event: Tilstand: Blokk> Sammenlign XX = Funksjon.Param (0) + (Blokk.Width + 2) Tilstand: Blokk> Sammenlign YY = Function.Param (1) Tilstand : Blokk> Sammenlign instansvariabel Farge = Funksjon.Param (2) Handling: System> Legg til Variabel = NumBlocks Verdi = 1 Handling: Funksjon> Anropsfunksjon Navn: "CheckMatchesX" Parameter 0: Funksjon.Param (0) + (Blokk. Bredde + 2) Parameter 1: Funksjon.Param (1) Parameter 2: Funksjon.Param (2) Underhendelse: System> Sammenlign Variabel NumMatchesFound> = 3 Handling: Blokk> Angi Boolean IsMatched = True

Koden din skal se slik ut:


Så hva gjør denne funksjonen?

  • Først tester det for å se om en naboblokke selv eksisterer til venstre for blokken vi passerte inn.
  • Når funksjonen bekrefter at det er en blokk i nabolaget, kontrollerer den om det er samme farge som blokken vi passerte inn.
  • Hvis det er, øker det NumMatchesFound av en, og sender den nylig funnet Blokken inn i funksjonen akkurat som den gjorde for originalen.
  • Dette fortsetter til det finner en blokk som ikke har samme farge som originalen. På det tidspunktet sjekker det for å se om det fant nok matchende blokker for å opprette en gruppe, og merker blokkene som samsvarer hvis det gjorde det.

Vertikale sjekker

La oss nå lage en annen versjon av denne funksjonen som vil gjøre det samme for vertikale kamper. Dette kommer til å bli vår CheckMatchesY funksjon. Du kan enten kopiere den opprinnelige funksjonen og foreta de nødvendige endringene, eller bare bygge den på nytt fra begynnelsen. i begge tilfeller, her er hvordan din funksjon skal se når den er ferdig:

Hendelse: Funksjon> På funksjon Navn: "CheckMatchesY" Sub-Event: Tilstand: Blokk> Sammenlign XX = Funksjon.Param (0) Tilstand: Blokk> Sammenlign YY = Function.Param (1) + (Block.Width + 2) Tilstand : Blokk> Sammenlign instansvariabel Farge = Funksjon.Param (2) Handling: System> Legg til Variabel = NumBlocks Verdi = 1 Handling: Funksjon> Anropsfunksjon Navn: "CheckMatchesY" Parameter 0: Funksjon.Param (0) Parameter 1: Funksjon Parameter 2: Funksjon.Param (2) Underhendelse: System> Sammenlign Variabel NumMatchesFound> = 3 Handling: Blokker> Angi Boolean IsMatched = True

Koden din skal se slik ut:



3. Egentlig ser etter sjekker

Til slutt må vi faktisk ringe til FindMatches funksjon. Gå til SwapBlocks funksjon og legg til en ny underhendelse til slutten av funksjonen:

Hendelse: Funksjon> Underhendelse: Handling: Funksjon> Anropsfunksjon Navn: "FindMatches"

Du vil legge merke til at denne underhendelsen faktisk ikke har noen forhold. Hvis du aldri har gjort en underarrangement som dette før, må du bare lage en underhendelse med noen tilstand i det hele tatt, siden det krever at du gir en tilstand når du gjør en underhendelse, og deretter slett tilstanden, men la den gå sub-arrangement. På denne måten sørger du for at underarrangementet alltid kjører.

Din SwapBlocks hendelsen skal nå se slik ut:

Hvis du kjører spillet på dette punktet, vil du se at blokkene blir ødelagt når kampene oppstår. Du vil også legge merke til at noen kamper som er der når spillet begynner, ikke forsvinner før du gjør en bytte av noe slag. Dette skyldes at vi aldri ringer til FindMatches fungere etter at vi har opprettet grensesnittet til blokker.

Årsaken til at vi ikke har lagt til denne koden er fordi i den endelige versjonen vil det være en annen funksjon som forhindrer at kampene blir automatisk generert som dette, så det er egentlig ingen grunn til å bekymre seg for dette problemet i det hele tatt. (Men vær så snill å ringe FindMatches fungere tidligere, hvis du vil.)


4. Konsolidere kontrollene

På dette tidspunktet har vi et ganske sterkt matchende system, men problemet er at vår kode er overflødig. For tiden har vi to forskjellige funksjoner som ser etter om det er en matchende nabo, og den eneste forskjellen mellom dem er at man sjekker vertikalt, og den andre kontrollerer horisontalt.

Siden den frie versjonen av Construct 2 begrenser hvor mange Hendelser vi kan ha, er dette definitivt en sløsing. For å løse dette skal vi lage en ny versjon av funksjonen som kan gjøre begge kontrollene.

Hvis du ser på funksjonen, vil du se den eneste forskjellen mellom de to versjonene er den man legger til Block.Width + 2 til x-posisjonen til blokken, og den andre legger den til y-posisjonen til bocken. Så, hindringen vi må komme forbi for å gjøre dette til en enkelt funksjon, gir funksjonen en måte å legge til Block.Width + 2 til bare X, eller bare Y, uten bruker en Hvis setning eller flere funksjoner, siden de krever flere hendelser som skal utføres.

Min løsning på dette er ikke veldig kompleks, men det blir lettere å forstå om vi kan se det komme sammen, så vi vil implementere det, og jeg vil forklare hvordan det fungerer når vi kan se alt i bruk.

  1. Slett CheckMatchesY begivenhet.
  2. Gi nytt navn til CheckMatchesX hendelse til, rett og slett, CheckMatches.
  3. I funksjonsanropet for CheckMatchesX under FindMatches begivenhet:
    1. Endre funksjonsanropet for å være for CheckMatches i stedet for CheckMatchesX.
    2. Legg til Parameter 3.
      1. Verdi = 1.
    3. Legg til Parameter 4.
      1. Verdi = 0.
  4. I funksjonsanropet for CheckMatchesY under FindMatches begivenhet:
    1. Endre funksjonsanropet for å være for CheckMatches i stedet for CheckMatchesY.
    2. Legg til Parameter 3.
      1. Verdi = 0.
    3. Legg til Parameter 4.
      1. Verdi = 1.

Som jeg vil forklare snart, vil disse ekstra parametrene fortelle CheckMatches enten det er en horisontal kontroll eller en vertikal kontroll. Når vi sender inn 1 til Parameter 3, og 0 til Parameter 4, Det er en horisontal sjekk, og når vi sender inn 0 til Parameter 3, og 1 til Parameter 4, Det er en vertikal kontroll.

Gå nå tilbake til CheckMatches funksjonen, og endre vilkårene og handlingene for å se slik ut:

Hendelse: Funksjon> På funksjon Navn: "CheckMatches" Sub-Event: Tilstand: Blokk> Sammenlign XX = Funksjon.Param (0) + ((Blokk.Width + 2) * Funksjon.Param (3)) Tilstand: Blokk> Sammenlign YY = Function.Param (1) + ((Block.Width + 2) * Function.Param (4)) Tilstand: Blokk> Sammenlign instansvariabel Farge = Funksjon.Param (2) Handling: Blokk> Angi boolsk IsMatched = True Action : Funksjon> Anropsfunksjon Navn: "CheckMatches" Parameter 0: Function.Param (0) + ((Block.Width + 2) * Function.Param (3)) Parameter 1: Funksjon.Param (1) + ((Blokk. Bredde + 2) * Funksjon.Param (4)) Parameter 2: Funksjon.Param (2) Parameter 3: Funksjon.Param (3) Parameter 4: Funksjon.Param (4) Underhendelse: System> Sammenlign Variabel NumMatchesFound> = 3 Handling: Blokk> Angi boolsk IsMatched = True

Dette er hva din FindMatches og CheckMatches koden skal nå se ut som:


Hvordan virker dette?

Så, hva er denne nye versjonen av funksjonen som faktisk gjør?

Vel, når du ringer CheckMatches Du sender nå to parametere, og heller enn å legge til Block.Width + 2 til enten x- eller y-stillingen, legger den til (Block.Width + 2) * Function.Param (3) til x-stillingen, og (Block.Width + 2) * Function.Param (4) til y-posisjonen.

Siden en av de to parametrene vil alltid være 1, og den andre vil alltid være 0, Dette betyr at enten x- eller y-stillingen vil bli endret - aldri begge deler!

For eksempel, hvis vi passerer inn 1 til Parameter 3, og 0 til Parameter 4, da legger det til (Block.Width + 2) * 1, som er rett og slett Block.Width + 2, til x-stillingen, og (Block.Width + 2) * 0, som er 0, til y-posisjonen.

Her er et raskt eksempel for å vise hva jeg mener og hvordan det beregner posisjonen til blokken hvor den vil sjekke for kampen. La oss si at i dette eksemplet er den opprinnelige blokken hos (200, 200), og blokkene har en bredde på 40. Så, hvis vi ønsker å få posisjonen til den nærliggende vertikale blokken, vil formlene trenge slik ut:

  • 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

Hvis vi ønsket å få posisjonen til den nærliggende horisontale blokken, ville formlene virke slik:

  • 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

Hvis du kjører spillet nå, bør du se at kampsystemet fungerer fortsatt som det opprinnelig gjorde, men fra vårt perspektiv er det faktisk et bedre system.


Konklusjon

På dette tidspunktet er vår gjenkjenningsfunksjon fortsatt ufullstendig, men vi har allerede gjort mye i denne opplæringen allerede, og jeg synes det er viktig å la alt dette synke inn før vi legger til noe annet. Med det for øye, skal jeg avslutte denne artikkelen her. Sjekk ut demoen i sin nåværende form.

I neste artikkel vil vi legge til et poengsystem, vi vil forbedre matchende systemet, og vi vil legge til "tyngdekraft" slik at blokkene vil falle når blokker under dem blir eliminert.

Hvis du vil ha en start på neste artikkel, tar du litt tid til å vurdere hvordan du vil oppdage når det er tomt plass under en blokk. Prøv å se på Blokker> Overlapper ved forskyvning Funksjon for inspirasjon!