Slik Code Doors and Locks

I spill laget av tilkoblede rom som The Legend of Zelda, er den nyere The Binding of Isaac, eller en hvilken som helst type Roguelike eller Metroidvania-lignende dører, en avgjørende rolle for spillerenes navigasjon og fremgang..

Dører gjør at spilleren kan reise fra ett rom eller et nivå til et annet, og så har et viktig sted i navigasjonen og tilkoblingen til de forskjellige rommene, hver for seg, og ved å definere kartet som en åpen verden eller et dungeon-gulv. De kan også fungere som midlertidige blokkeringer som spilleren må låse opp gjennom en bestemt mekaniker (for eksempel å skaffe en nøkkel eller aktivere en bryter).

I denne opplæringen vil jeg demonstrere flere låsemekanikk, og foreslå måter å implementere dem på i dine spill. Disse er på ingen måte ment å være de eneste eller beste implementeringer; de er praktiske eksempler.

De interaktive demoene i denne opplæringen ble laget med HTML5-spillverktøyverktøyet Construct 2 og bør være kompatibelt med den gratis versjonen. (CAPX-filene er tilgjengelige i nedlasting av kilde.) Denne opplæringen skal imidlertid hjelpe deg med å lære hvordan du implementerer dørene og låser logikken i hvilken motor du vil ha. Når du får ideen bak logikken, er det helt ned til din egen kjennskap til kodingsverktøyet ditt og måten du vil tilpasse det til spillet du for tiden lager.

La oss dykke inn!

Grunnmekanikeren

En dør er i utgangspunktet en blokk av natur som ikke kan passeres, og forhindrer spillerens karakter til å gå gjennom til den er låst opp. Døren kan ha forskjellige tilstander: låst eller ulåst, lukket eller åpen.

Det må være en åpenbar representasjon av sistnevnte; Spilleren må kunne fortelle at døren faktisk er en dør, og om den er i låst eller ulåst tilstand.

I de følgende demonstrasjonene presenteres dørene gjennom to grafikk:


Dette er en lukket dør.

Dette er en åpen dør.

Jeg har også brukt forskjellige farger til å representere de forskjellige materialene som dørene kan bli laget av - men for å være ærlig er det grafiske aspektet opp til deg, ditt spill og dets univers. Den viktigste delen er at døren skal være tydelig identifiserbar som en dør, og det bør være åpenbart om det kommer til å blokkere spillerens progresjon eller åpne og føre til resten av nivået eller verdenen.

Når lukket eller låst, bør døren være en blokk med fast tilstand. Når den er åpnet, skal den faste tilstanden deaktiveres, slik at tegnene kan gå gjennom den. Pass på at uansett hvilken kollisjonsmotor du har, lar du deg endre denne tilstanden ganske enkelt.

Fra et programmeringsperspektiv bør dørobjektet inneholde eller være knyttet til en er låst Boolsk variabel. Avhengig av verdien av denne variabelen, kan du bestemme hvilken sprite som skal vises, og om blokken skal være solid eller ikke.

For å låse opp døren skal tegnet inneholde en has_key Boolsk variabel seg selv når spilleren har hentet en nøkkel: ekte hvis de har det, falsk hvis de ikke gjør det.

I denne grunnmekanikeren fungerer nøkkelen som en del av tegnet av tegnet, og en nøkkel åpnes alle dører. Bruk av nøkkelen på en dør forbruker ikke den; nøkkelen forblir i figurens beholdning.

For å visualisere det, kan vi bare vise et bilde av nøkkelen i HUD for å la spilleren vite at han eier en nøkkel som kan åpne dørene når karakteren har plukket den opp (ved å flytte tegnet over nøkkelen i rommet ).

Vurder følgende grunnleggende eksempel:

Klikk på demoen for å gi den fokus, og kontroller deretter tegnet ved hjelp av piltastene på tastaturet og utfør handlinger med mellomromstasten. (I dette eksemplet er handlingen "åpne en dør".)

Vegger er faste blokker som ikke tillater tegnet å gå gjennom når de kolliderer med dem. Lukkede dører er også solide.

For å åpne en dør, må tegnet være innenfor 64 piksler av døren og ha en nøkkel (det vil si has_key Boolsk variabel som bestemmer om tegnet har nøkkelen i beholdningen må være ekte). 

Med disse forholdene, når spilleren trykker mellomromstasten, endres den tilhørende dørens tilstand. Den boolske variabelen låst er satt til falsk, og dens "faste" tilstand er deaktivert.

I pseudokode vil dette se ut som:

Door.Locked = True Door.AnimationFrame = 0 // Animasjonsrammen som viser døren som låst Door.Solid = Aktivert // Dørens faste tilstand er aktivert Door.Locked = False Door.AnimationFrame = 1 // Animasjonen Ramme som viser døren som den er åpnet Door.Solid = Deaktivert // Dørens faste tilstand er deaktivert Tastaturnøkkel "Mellomrom" er trykket og Avstand (Tegn, Dør) <= 64px and Door.Locked = True and Character.Has_Key = True //The player has a key Door.Locked = False Keyboard Key "Space" is pressed and Distance(Character,Door) <= 64px and Door.Locked = True and Character.Has_Key = False //The player does not have a key Text.text = "You don't have a key for that door" 

Påminnelse: Denne koden representerer ikke et bestemt språk; Du bør kunne implementere det på hvilket som helst språk du vil.

Du kan også merke seg at vi gjør en sjekk for når spilleren gjør det ikke ha den forventede nøkkelen, og vis en tilbakemeldingsmelding som forklarer hvorfor døren ikke var låst opp. Du kan håndtere sjekker som de som passer best for spillet ditt - bare vær oppmerksom på at det alltid er hyggelig å gi tilbakemelding til spilleren at handlingen hans ble registrert, og forklare årsaken til at den ikke var fullført.

Dette er en veldig grunnleggende dør- og låslogikk og hvordan du implementerer den. I resten av opplæringen ser vi på andre låsesystemer som er varianter av dette grunnleggende systemet.

Ulike låsesystemer

Vi har sett det grunnleggende systemet hvor nøkkelen er en hel del av tegningen av tegnet, og en nøkkel åpner alle dører og kan gjenbrukes for å åpne flere dører. La oss bygge videre på dette.

KeyStack eksempel

I det neste eksemplet vil karakteren ha en stable av nøklene i sin beholdning. Selv om det er flere forskjellige farger på døren, er forskjellen strengt grafisk her - dørobjektet er logisk det samme som i det grunnleggende eksemplet, og en type nøkkel kan åpne noen av dem. Men denne gangen, når du bruker en nøkkel til å åpne en dør, fjernes nøkkelen fra stakken.


Når det gjelder koding, er denne endringen hovedsakelig på tegns nivå. I stedet for å ha a has_key Boolsk variabel, vil du ha en numerisk variabel som vil holde antall nøkler karakteren har "på lager".

Hver gang karakteren henter en nøkkel, legger du til 1 til denne variabelen for å representere stabelen som går opp. Hver gang tegnet åpner en dør, trekker du av 1 fra denne variabelen for å representere bruk av en nøkkel. (I videospill land blir nøklene ødelagt så snart de brukes en gang.)

En annen modifikasjon er når spilleren trykker mellomromstasten: i stedet for å kontrollere at a has_key Boolsk variabel er ekte, vi vil faktisk sjekke at verdien av KeyStack er mer enn null, slik at vi kan forbruke en nøkkel når du har åpnet døren.

I pseudokode ser dette ut som:

Dører mekanikk = samme som i det grunnleggende eksemplet ovenfor. Tastaturnøkkel "Mellomrom" er trykket og Character.KeyStack> 0 og Avstand (tegn, dør) <= 64 and Door.Locked = True Character.KeyStack = Character.KeyStack - 1 Door.Locked = False 

WhichKey Eksempel

I dette nye eksemplet vil vi vurdere et scenario der ulike typer dører krever forskjellige typer nøkler som skal låses opp.

Her, som i det første grunnleggende eksemplet, vil nøklene være en del av tegnetes beholdning. Vi vil gå tilbake til å bruke boolske variabler for å avgjøre om tegnet har hentet de nødvendige nøklene. Og siden vi vil ha forskjellige nøkler, vil vi også ha forskjellige typer dører (svart dør, rød dør, gulldør) som også vil kreve en passende nøkkel for å tillate dem å åpnes (svart nøkkel, rød nøkkel, gull nøkkel ).

Dørobjektene vil bruke forskjellige sprites til å vise materialet deres, og vil inneholde en numerisk variabel som heter WhichKey som vil indikere hvilken type nøkkel som forventes, samt hvilken type grafikk den skal vise. De forskjellige nøkkelverdiene finnes som konstante variabler, for bedre lesbarhet.


I pseudokode:

CONSTANT BLACK_KEY = 0 CONSTANT RED_KEY = 1 CONSTANT GOLD_KEY = 2 Dørmekanikk er det samme som i det grunnleggende eksemplet. Tastaturnøkkel "Mellomrom" er trykket // Døren krever en svart nøkkel, men tegnet har ikke en Hvis Door.Locked = True and Door.WhichKey = BLACK_KEY og Character.Has_Black_Key = False og Distance (Door, Character) <= 64 Text.text="You need a black key for this door" //The door requires a red key but the character doesn't have one Else If Door.Locked = True and Door.WhichKey = RED_KEY and Character.Has_Red_Key = False and Distance(Door,Character) <= 64 Text.text="You need a red key for this door" //The door requires a gold key but the character doesn't have one Else If Door.Locked = True and Door.WhichKey = GOLD_KEY and Character.Has_Gold_Key = False and Distance(Door,Character) <= 64 Text.text="You need a red key for this door" //The door requires a black key and the character has one Else If Door.Locked = True and Door.WhichKey = BLACK_KEY and Character.Has_Black_Key = True and Distance(Door,Character) <= 64 Door.Locked = False //The door requires a red key and the character has one Else If Door.Locked = True and Door.WhichKey = RED_KEY and Character.Has_Red_Key = True and Distance(Door,Character) <= 64 Door.Locked = False //The door requires a gold key and the character has one Else If Door.Locked = True and Door.WhichKey = GOLD_KEY and Character.Has_Gold_Key = True and Distance(Door,Character) <= 64 Door.Locked = False

Dette er en variasjon på det grunnleggende eksemplet som tillater flere typer nøkler og dører, og det forbruker ikke nøkler for å åpne dører. Når du har nøkkelen, er det en del av lagerbeholdningen din - en del av tegnets "statistikk".

Bytt eksempel

Denne gangen, i stedet for å handle direkte på dører, må spilleren aktivere en bestemt bryter for å åpne eller lukke en bestemt dør.

Dørene her er egentlig det samme som i det grunnleggende eksemplet. De kan vise forskjellig grafikk, men logikken til objektet er fortsatt den samme. Det er et tillegg skjønt: vi legger til to numeriske variabler DoorID og SwitchID, som vi bruker til å vite hvilken bryter som er knyttet til hvilken dør.

brytere er en ny type objekter som jeg har valgt å lage solid (men du trenger ikke). De inneholder en boolsk variabel, aktivert, og numeriske variabler DoorID og SwitchID som, som du kan gjette, bruker vi til å bestemme hvilken bryter som er bundet til hvilken dør.

Så når en bryter har Aktivert: True, Den "koblede" døren er satt til å ha Låst: Falsk. Vår handling med mellomromstasten vil da skje når ved siden av a bytte om, heller enn ved siden av en dør. Legg merke til mangel på en nøkkel i dette eksemplet, siden bryterne fungerer som nøkler:

Vi kunne bare bruke enkel kode som sjekker om dørbryterenes koblinger i samme rom (siden dette eksempelet viser tre dører og brytere i samme rom), men senere ser vi at vi kan ha brytere som virker på dører som er i en annen rom, og så vil deres handling ikke skje på det nøyaktige tidspunktet da spilleren aktiverer bryteren; Det vil skje senere, når det nye rommet er lastet.

Av denne grunn trenger vi standhaftighet. Et alternativ for dette er å bruke arrays for å holde oversikt over data som tilstanden til bryterne (det vil si om hver bryter er aktivert eller ikke).

I pseudokode:

CONSTANT SWITCH_DOORID = 0 CONSTANT SWITCH_ACTIVATION = 1 // Disse konstantene tillater oss å holde en lesbar påminnelse om array koordinatene 
// Definer noe array // X-koordinatet til arrayet vil svare til SwitchID-verdien // Y-0-koordinatet blir DoorID // Y-1-koordinatet vil være aktiveringsstatus aSwitch (antall brytere, 2) // 2 er høyden (Y) -nummeret, ofte 0-basert.
Kjør noen tilknytning til SwitchID med DoorIDs Dørmekaniker er fortsatt den samme som i det grunnleggende eksemplet. // Viser den riktige bryteren grafisk i henhold til aktiveringsstatus Switch.Activated = True Vis animasjonsrammen Switch_ON Switch.Activated = False Vis animasjonsrammen Switch_OFF Tastaturnøkkel "Mellomrom" er trykket og Avstand (tegn, bryter) <= 64 Switch.Toggle(Activated) //A function that will set the value to either True or False) aSwitch(Switch.SwitchID,SWITCH_ACTIVATION) = Switch.Activated //It can depend on your coding language, but the idea is to set the value in the array where X is the SwitchID and where Y is the state of activation of the switch. The value itself is supposed to be the equivalent of the Switch.Activated boolean value. Door.DoorID = aSwitch(Switch.SwitchID,SWITCH.DOORID) //Allows us to make sure we're applying/selecting the correct door instance //Now according to the activation value, we lock or unlock the door aSwitch(Switch.SwitchID,SWITCH.ACTIVATION) = True Door.Locked = False aSwitch(Switch.SwitchID,SWITCH.ACTIVATION) = False Door.Locked = True

For dette spesifikke eksempelet, hvor bryterne er i samme rom som dørene de er koblet til, er det ved hjelp av array-teknikken overkill. Hvis spillet ditt er opprettet på en slik måte at hver bryter som virker på en dør, skal plasseres i samme rom, så går du for den enklere metoden, slipper av matrisen og kontrollerer objekter som er på bare skjermen.

Plate-Switch Eksempel

Plate-brytere ligner brytere, i den forstand at de er enten aktivert eller ikke, og at vi kan koble dem til dører for å låse eller låse dem opp. Forskjellen ligger i hvordan en platebryter er aktivert, som er gjennom trykk.

I dette eksempelet med topp ned-visning, vil platebryteren aktiveres når tegnet overlapper det. Du kan trykke på mellomromstasten for å slippe en stein på platebryteren, slik at den aktiveres selv når tegnet ikke står på den.

Gjennomføringen av dette ligner det forrige eksempelet, med to små endringer:

  • Du må aktivere platebryteren når et tegn eller en stein er på toppen av den.
  • Du må gjøre mellomromstasten slippe en stein (fra lageret) til platebryteren.
// Det meste av implementeringen er det samme som forrige eksempel, erstatt Switch-objektet med PlateSwitch-objektet. // Plate-Switch Mechanic Character ELLER Rock er IKKE overlappende PlateSwitch PlateSwitch.Activated = False aSwitch (PlateSwitch.SwitchID, SWITCH_ACTIVATION) = PlateSwitch.Activated Door.DoorID = aSwitch (PlateSwitch.SwitchID, SWITCH.DOORID) // Tillater oss å sørge for at vi søker / velger riktig dørinstans. Door.Locked = True Character ELLER Rock er overlappende PlateSwitch PlateSwitch.Activated = True aSwitch (PlateSwitch .SwitchID, SWITCH_ACTIVATION) = PlateSwitch.Activated Door.DoorID = aSwitch (PlateSwitch.SwitchID, SWITCH.DOORID) // Tillater oss å sørge for at vi søker / velger riktig døreksempel. Door.Locked = False Keyboard Key "Space" er presset og tegn er overlappende PlateSwitch Spawn Rock på PlateSwitch.position 

Mobs Eksempel

En annen mulig låsemekaniker er å kreve spilleren å kvitte seg med alle fiender (også kjent som mobs) i et rom eller område for å utløse opplåsing av dørene.

For dette eksempelet har jeg laget noen få områder i et enkeltrom; hvert område har en dør og flere mobber (selv om disse fiender ikke beveger seg og ikke tar vare på skade).
Hvert område har sin egen farge.

Mellomromstasten vil gjøre tegnet brann noen prosjektiler; tre prosjektiler vil drepe en mob.

Denne typen mekaniker brukes i The Legend of Zelda og The Binding of Isaac, og dreier seg om en funksjon som kontrollerer antall levende fiender i rommet eller området. I dette eksemplet har hvert fargede område et antall av de levende mobs, initiert når rommet laster, og er bundet til døren. Hver mobs død dras av 1 fra denne disken; en gang det faller til 0, dørene Låst Staten er endret til Falsk.

// Ved start av spillet For hvert område For hvert Mob overlappende område Area.AliveMobs = Area.AliveMobs + 1 
Dørmekaniker er det samme som i det grunnleggende eksemplet
Tastaturnøkkel "Mellomrom" er presset Krymp et prosjektil fra Karakter posisjon Projektil kolliderer med Mob Mob.HP = Mob.HP - 1 Destroy Projectile Mob.HP <=0 //Mob is dead and Mob is overlapping Area Destroy Mob Area.AliveMobs = Area.AliveMobs - 1 Area.AliveMobs <= 0 and Door is linked to Area //By means of an ID, a pointer or whatever Door.Locked = False

I dette eksemplet an Område er en farget sprite med tilhørende numerisk variabel, AliveMobs, som teller antall mobs som overlapper området. Når alle mobbe i et område er beseiret, er den tilsvarende døren ulåst (bruker samme mekaniker som vi har sett siden det grunnleggende eksempelet).

Navigasjonseksempel

Som jeg nevnte i innledningen, kan dører fungere som blokkerende hindringer, men kan også brukes til å tillate spillerens karakter å navigere fra et rom til et annet.

I dette eksemplet vil dørene bli låst opp som standard siden vi er mer interessert i navigasjonsaspektet.

Mekanikeren avhenger mye av spillet du lager, samt måten du håndterer datastrukturen på gulvene dine. Jeg vil ikke gå inn i detaljene om hvordan implementeringen min fungerer her, da den er veldig spesifikk for Construct 2, men du kan finne den i kildefilene hvis du ønsker det.

Konklusjon

Gjennom denne artikkelen har vi sett hvordan dører er midlertidige hindringer som krever nøkler eller låser opp mekanikk som brytere, platebrytere eller til og med mobs død. Vi har også sett hvordan de kan fungere som "broer", slik at du kan navigere gjennom forskjellige områder av spillets verden.

Som en rask påminnelse, her er noen mulige låsemekanikk:

  • En nøkkel for alle dører, som en del av lageret.
  • Forbruksnøkler: Hver gang du åpner en dør, blir en nøkkel fjernet fra nøkkelen.
  • Ulike dører krever forskjellige nøkler.
  • Brytere eller platebrytere, hvor du ikke opptrer direkte på døren for å låse opp den, men gjennom en separat, koblet enhet.
  • Å drepe alle mobben i et område åpner automatisk en dør.

Hvis du blandet alle disse mekanikkene i et spill, kan du ende opp med noe slikt:

Her har vi et fint utvalg av forskjellige dør- og låsemekanikk, som krever at spilleren går gjennom flere rom for å låse opp de ulike dørene. For læringsformål, vil du kanskje gjenskape dette i ditt eget programmeringsmiljø, ved hjelp av alle tidligere implementeringer vi har gjennomgått.

Jeg håper du likte denne artikkelen, og at den var nyttig for deg, og jeg vil gjerne minne om at du kan finne kilden til alle demoene på Github. Du kan åpne og redigere dem i Construct 2s gratisversjon (versjon r164.2 eller høyere).

referanser

  • Forhåndsvis bilde: Lås designet av João Miranda fra Noun-prosjektet