Grunnleggende 2D Platformer Fysikk, Del 1

Denne serien vil dekke hvordan man lager et enkelt og robust fysikksystem for et plattformspill. I denne delen ser vi på karakterkollisjonsdata.

Tegnkollisjoner

OK, så ser premissen ut slik: Vi ønsker å lage en 2D-plattform med enkel, robust, responsiv, nøyaktig og forutsigbar fysikk. Vi ønsker ikke å bruke en stor 2D fysikkmotor i dette tilfellet, og det er noen grunner til dette:

  • uforutsigbare kollisionsresponser
  • vanskelig å sette opp nøyaktig og robust tegnbevegelse
  • mye mer komplisert å jobbe med
  • tar mye mer prosessorkraft enn enkel fysikk

Selvfølgelig er det også mange fordeler med å bruke en fysisk motor uten fysisk form, for eksempel å kunne sette opp komplekse fysikkinteraksjoner ganske enkelt, men det er ikke det vi trenger for spillet vårt.

En egendefinert fysikkmotor hjelper spillet å ha en tilpasset følelse for det, og det er veldig viktig! Selv om du skal begynne med et relativt grunnleggende oppsett, vil måten i hvilken ting beveger seg og samhandle med hverandre alltid påvirkes av dine egne regler, i stedet for noen andres. La oss komme til det!

Karakter Bounds

La oss begynne med å definere hva slags figurer vi skal bruke i vår fysikk. En av de mest grunnleggende formene vi kan bruke til å representere et fysisk objekt i et spill, er en akselbundet grenseboks (AABB). AABB er i utgangspunktet et urotert rektangel.

I mange plattformspill er AABBer nok til å tilnærme kroppen til hvert objekt i spillet. De er svært effektive, fordi det er veldig enkelt å beregne en overlapping mellom AABB og krever svært lite data - for å beskrive en AABB, er det nok å kjenne sentrum og størrelse.

Uten videre, la oss lage en struktur for vårt AABB.

offentlig struktur AABB 

Som nevnt tidligere, er alt vi trenger her så langt data angår to vektorer; Den første blir AABBs senter, og den andre er den halve størrelsen. Hvorfor halv størrelse? Mesteparten av tiden for beregninger trenger vi den halve størrelsen uansett, så i stedet for å beregne det hver gang vi bare vil huske det i stedet for full størrelse.

offentlig struktur AABB offentlig Vector2 senter; offentlig Vector2 halfSize; 

La oss begynne med å legge til en konstruktør, så det er mulig å lage strukturen med egendefinerte parametere.

offentlig AABB (Vector2 senter, Vector2 halfSize) this.center = center; this.halfSize = halfSize; 

Med dette kan vi opprette kollisjonskontrollfunksjonene. Først, la oss gjøre en enkel sjekk om to AABBer kolliderer med hverandre. Dette er veldig enkelt - vi trenger bare å se om avstanden mellom sentrene på hver akse er mindre enn summen av halvstørrelser.

offentlig bool Overlapper (AABB andre) if (Mathf.Abs (center.x - other.center.x)> halfSize.x + other.halfSize.x) return false; hvis (Mathf.Abs (center.y - other.center.y)> halfSize.y + other.halfSize.y) returnerer false; returnere sant; 

Her er et bilde som demonstrerer denne sjekken på x-aksen; y-aksen er merket på samme måte.

Som du kan se, hvis summen av halvstørrelser skulle være mindre enn avstanden mellom sentrene, ville det ikke være mulig å overlappe. Legg merke til at i koden ovenfor kan vi unnslippe kollisjonskontrollen tidlig hvis vi finner at objektene ikke overlapper på den første aksen. Overlappingen må eksistere på begge akse, hvis AABBene skal kollidere i 2D-plass.

Flyttende objekt

La oss begynne med å lage en klasse for en gjenstand som påvirkes av spillets fysikk. Senere bruker vi dette som en basis for en faktisk spillerobjekt. La oss ringe denne klassen MovingObject.

offentlig klasse MovingObject 

La oss nå fylle denne klassen med dataene. Vi trenger ganske mye informasjon for dette objektet:

  • Posisjon og forrige rammes posisjon
  • hastighet og forrige rammens hastighet
  • skala
  • AABB og en kompensasjon for det (slik at vi kan justere det med en sprite)
  • er objekt på bakken og om det var på bakken sistramme
  • er objekt ved siden av veggen til venstre og om det var ved siden av den siste rammen
  • er objekt ved siden av veggen til høyre og om den var ved siden av den siste rammen
  • er objekt i taket og om det var i taket siste ramme

Posisjon, fart og skala er 2D vektorer.

offentlig klasse MovingObject public Vector2 mOldPosition; offentlig Vector2 mPosition; offentlig Vector2 mOldSpeed; offentlig Vector2 mSpeed; offentlig Vector2 mScale; 

La oss nå legge til AABB og offset. Forskjellen er nødvendig, så vi kan fritt tilpasse AABB til objektets sprite.

offentlig AABB mAABB; offentlig Vector2 mAABBOffset;

Og til slutt, la oss deklarere variablene som indikerer objektets posisjonstilstand, enten det er på bakken, ved siden av en vegg eller ved taket. Disse er svært viktige fordi de vil fortelle oss om vi kan hoppe eller, for eksempel, trenger å spille en lyd etter å ha bumpet inn i en vegg.

offentlig bool mPushedRightWall; offentlig bool mPushesRightWall; offentlig bool mPushedLeftWall; offentlig bool mPushesLeftWall; offentlig bool mWasOnGround; offentlig bool mOnGround; offentlig bool mWasAtCeiling; offentlig bool mAtCeiling;

Dette er grunnleggende. La oss nå lage en funksjon som vil oppdatere objektet. For nå vil vi ikke sette opp alt, men bare nok, så vi kan begynne å lage grunnleggende karakterkontroller.

offentlig ugyldig UpdatePhysics () 

Det første vi vil gjøre her er å lagre de forrige rammens data til de riktige variablene.

offentlig tomgang UpdatePhysics () mOldPosition = mPosition; mOldSpeed ​​= mSpeed; mWasOnGround = mOnGround; mPushedRightWall = mPushesRightWall; mPushedLeftWall = mPushesLeftWall; mWasAtCeiling = mAtCeiling; 

La oss nå oppdatere posisjonen ved hjelp av gjeldende hastighet.

mPosisjon + = mSpeed ​​* Time.deltaTime;

Og bare for nå, la oss gjøre det slik at hvis den vertikale posisjonen er mindre enn null, antar vi tegnet på bakken. Dette er bare for nå, så vi kan sette opp tegnets kontroller. Senere vil vi kollisjonere med en tilkart.

hvis (mPosition.y < 0.0f)  mPosition.y = 0.0f; mOnGround = true;  else mOnGround = false;

Etter dette må vi også oppdatere AABBs senter, slik at det faktisk stemmer overens med den nye posisjonen.

mAABB.center = mPosition + mAABBOffset;

For demo-prosjektet bruker jeg Unity, og for å oppdatere posisjonen til objektet som det må brukes på transformasjonskomponenten, så la oss også gjøre det. Det samme må gjøres for skalaen.

mTransform.position = ny Vector3 (Mathf.Round (mPosition.x), Mathf.Round (mPosition.y), - 1.0f); mTransform.localScale = ny Vector3 (mScale.x, mScale.y, 1.0f);

Som du ser, er den gjengitte posisjonen avrundet. Dette er for å sikre at det gjengitte tegnet alltid knyttes til en piksel.

Tegnkontroller

Data

Nå som vi har gjort vår grunnleggende MovingObject-klasse, kan vi begynne å spille med tegnebevegelsen. Det er jo en veldig viktig del av spillet, og kan gjøres ganske mye med en gang - det er ikke nødvendig å dykke for dypt inn i spillsystemet enda, og det vil være klart når vi må teste vår karakter- kartkollisjoner.

La oss først lage en karakter klasse og avlede den fra MovingObject-klassen.

offentlig klasse Karakter: MovingObject 

Vi må håndtere noen ting her. Først av alt, inngangene-la oss lage en enum som vil dekke alle kontrollene for tegnet. La oss lage den i en annen fil og kalle den KeyInput. 

Public Enum KeyInput GoLeft = 0, GoRight, GoDown, Jump, Count

Som du kan se, kan vår karakter bevege seg til venstre, høyre, ned og hoppe opp. Flytting vil bare fungere på enveisplattformer, når vi ønsker å falle gjennom dem.

La oss nå erklære to arrays i Character-klassen, en for gjeldende rammeinnganger og en annen for den forrige rammen. Avhengig av et spill, kan dette oppsettet mer eller mindre fornuftig. Vanligvis, i stedet for å lagre nøkkelstaten til en matrise, kontrolleres den etter behov ved hjelp av en motors eller rammeverkets spesifikke funksjoner. Å ha en matrise som ikke er strengt bundet til ekte inngang kan imidlertid være nyttig, hvis vi for eksempel vil simulere nøkkelpresser.

beskyttet bool [] mInputs; beskyttet bool [] mPrevInputs;

Disse arrays vil bli indeksert av KeyInput enum. For å enkelt bruke disse arrays, la oss lage noen få funksjoner som vil hjelpe oss å sjekke for en bestemt nøkkel.

beskyttet bool Utgitt (KeyInput-nøkkel) return (! mInputs [(int) -tast] && mPrevInputs [(int) -tast]);  beskyttet bool KeyState (KeyInput-tast) retur (mInputs [(int) -tast]);  beskyttet bool Trykkes (KeyInput-tast) retur (mInputs [(int) -tast] &&! mPrevInputs [(int) -tast]); 

Ikke noe spesielt her - vi vil kunne se om en nøkkel bare ble trykket, bare utgitt, eller om den er på eller av.

La oss skape en annen enum som vil holde alle tegnets mulige tilstander.

offentlig karakter CharacterState Stand, Walk, Jump, GrabLedge,;

Som du kan se, kan vår karakter enten stå stille, gå, hoppe eller ta en hylle. Nå som dette er gjort, må vi legge til variabler som hopphastighet, gåhastighet og nåværende tilstand.

offentlig CharacterState mCurrentState = CharacterState.Stand; offentlig float mJumpSpeed; offentlig float mWalkSpeed;

Selvfølgelig er det noen flere data som trengs her, for eksempel karaktersprite, men hvordan dette ser ut, avhenger mye av hvilken type motor du skal bruke. Siden jeg bruker Unity, bruker jeg en referanse til en animator for å sikre at sprite spiller animasjon for en passende tilstand.

Oppdater Loop

OK, nå kan vi starte arbeidet med oppdateringssløyfen. Hva vi skal gjøre her, vil avhenge av den nåværende tilstanden til tegnet.

offentlig ugyldig CharacterUpdate () switch (mCurrentState) tilfelle CharacterState.Stand: break; tilfelle CharacterState.Walk: break; tilfelle CharacterState.Jump: break; tilfelle CharacterState.GrabLedge: break; 

Stand State

La oss begynne med å fylle opp hva som skal gjøres når tegnet ikke beveger seg i stand-state. Først av alt bør hastigheten settes til null.

tilfelle CharacterState.Stand: mSpeed ​​= Vector2.zero; gå i stykker;

Vi ønsker også å vise den aktuelle sprite for staten.

tilfelle CharacterState.Stand: mSpeed ​​= Vector2.zero; mAnimator.Play ( "Stand"); gå i stykker;

Nå, hvis tegnet ikke er på bakken, kan det ikke lenger stå, så vi må endre tilstanden for å hoppe.

tilfelle CharacterState.Stand: mSpeed ​​= Vector2.zero; mAnimator.Play ( "Stand"); hvis (! mOnGround) mCurrentState = CharacterState.Jump; gå i stykker;  gå i stykker;

Hvis du trykker GoLeft eller GoRight-tasten, må vi endre vår tilstand for å gå.

tilfelle CharacterState.Stand: mSpeed ​​= Vector2.zero; mAnimator.Play ( "Stand"); hvis (! mOnGround) mCurrentState = CharacterState.Jump; gå i stykker;  hvis (KeyState (KeyInput.GoRight)! = KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Walk; pause pause;

Hvis du trykker på Jump-tasten, vil vi sette den vertikale hastigheten til hopphastigheten og endre tilstanden for å hoppe.

hvis (KeyState (KeyInput.GoRight)! = KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Walk; gå i stykker;  annet hvis (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; mCurrentState = CharacterState.Jump; gå i stykker; 

Det kommer til å være det for denne tilstanden, i hvert fall for nå. 

Walk State

La oss nå lage en logikk for å flytte på bakken, og umiddelbart begynner å spille gangav animasjonen.

tilfelle CharacterState.Walk: mAnimator.Play ("Walk"); gå i stykker;

Her, hvis vi ikke trykker på venstre eller høyre knapp, eller begge er trykket, vil vi gå tilbake til stående stillstand.

hvis (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Stand; mSpeed ​​= Vector2.zero; gå i stykker; 

Hvis du trykker på GoRight-tasten, må vi sette den horisontale hastigheten til mWalkSpeed ​​og sørge for at sprite er skalert riktig - den horisontale skalaen må endres hvis vi vil vende spritet horisontalt. 

Vi bør også flytte bare hvis det ikke er noen hindring fremover, så hvis mPushesRightWall er satt til sant, må den horisontale hastigheten settes til null hvis vi beveger oss til høyre.

hvis (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Stand; mSpeed ​​= Vector2.zero; gå i stykker;  annet hvis (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; ellers mSpeed.x = mWalkSpeed; mScale.x = Mathf.Abs (mScale.x);  annet hvis (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; ellers mSpeed.x = -mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x); 

Vi må også håndtere venstre side på samme måte.

Som vi gjorde for stående tilstand, må vi se om en hoppeknapp er trykket og sett vertikal hastighet hvis det er slik.

hvis (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; mAudioSource.PlayOneShot (mJumpSfx, 1.0f); mCurrentState = CharacterState.Jump; gå i stykker; 

Ellers, hvis tegnet ikke er på bakken, må det endres til å hoppe også, men uten tillegg av vertikal hastighet, så faller det bare ned.

hvis (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; mAudioSource.PlayOneShot (mJumpSfx, 1.0f); mCurrentState = CharacterState.Jump; gå i stykker;  annet hvis (! mOnGround) mCurrentState = CharacterState.Jump; gå i stykker; 

Det er det for turgåing. La oss flytte til hoppestaten.

Jump State

La oss starte med å sette en passende animasjon for sprite.

mAnimator.Play ( "Jump");

I Jump-tilstanden må vi legge tyngdekraften til karakterens hastighet, så den går raskere og raskere mot bakken.

mSpeed.y + = Constants.cGravity * Time.deltaTime;

Men det ville være fornuftig å legge til en grense, slik at tegnet ikke kan falle for fort.

mSpeed.y = Mathf.Max (mSpeed.y, Constants.cMaxFallingSpeed);

I mange spill, når tegnet er i luften, reduseres manøvredigheten, men vi vil gå for noen svært enkle og nøyaktige kontroller som gir full fleksibilitet når du er i luften. Så hvis vi trykker på GoLeft eller GoRight-tasten, beveger tegnet i retningen mens du hopper så fort som det ville hvis det var på bakken. I dette tilfellet kan vi bare kopiere bevegelseslogikken fra gående tilstand.

hvis (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mSpeed.x = 0.0f;  annet hvis (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; ellers mSpeed.x = mWalkSpeed; mScale.x = Mathf.Abs (mScale.x);  annet hvis (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; ellers mSpeed.x = -mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x); 

Endelig skal vi hoppe høyere hvis hoppeknappen trykkes lenger. For å gjøre dette, vil vi faktisk gjøre hoppet lavere hvis hoppeknappen ikke trykkes. 

hvis (! KeyState (KeyInput.Jump) && mSpeed.y> 0.0f) mSpeed.y = Mathf.Min (mSpeed.y, Constants.cMinJumpSpeed); 

Som du kan se, hvis hoppeknappen ikke er trykket og den vertikale hastigheten er positiv, klemmer du hastigheten til den maksimale verdien av cMinJumpSpeed (200 piksler per sekund). Dette betyr at hvis vi bare skulle trykke på hoppeknappen, er hoppens hastighet i stedet for å være lik mJumpSpeed (410 som standard), blir senket til 200, og derfor blir hoppet kortere.

Siden vi ikke har noen nivå geometri ennå, bør vi hoppe over GrabLedge implementeringen for nå.

Oppdater de tidligere inngangene

Når rammen er ferdig, kan vi oppdatere tidligere innganger. La oss lage en ny funksjon for dette. Alt vi trenger å gjøre her er å flytte nøkkelstatusverdiene fra mInputs array til mPrevInputs matrise.

offentlig ugyldig UpdatePrevInputs () var count = (byte) KeyInput.Count; for (byte i = 0; i < count; ++i) mPrevInputs[i] = mInputs[i]; 

På slutten av CharacterUpdate-funksjonen må vi fortsatt gjøre et par ting. Den første er å oppdatere fysikken.

UpdatePhysics (); 

Nå som fysikken er oppdatert, kan vi se om vi skulle spille noen lyd. Vi ønsker å spille en lyd når karakteren støter på en overflate, men akkurat nå kan den bare slå bakken fordi kollisjonen med tilemap ikke er implementert ennå. 

La oss se om karakteren nettopp har falt på bakken. Det er veldig enkelt å gjøre det med det nåværende oppsettet - vi må bare se opp hvis tegnet er på bakken akkurat nå, men ikke i forrige ramme.

hvis (mOnGround &&! mWasOnGround) mAudioSource.PlayOneShot (mHitWallSfx, 0,5f);

Til slutt, la oss oppdatere tidligere innganger.

UpdatePrevInputs ();

Alt i alt, dette er hvordan CharacterUpdate-funksjonen skal se nå, med mindre forskjeller avhengig av hvilken type motor eller ramme du bruker.

offentlig ugyldig CharacterUpdate () switch (mCurrentState) tilfelle CharacterState.Stand: mWalkSfxTimer = cWalkSfxTime; mAnimator.Play ( "Stand"); mSpeed ​​= Vector2.zero; hvis (! mOnGround) mCurrentState = CharacterState.Jump; gå i stykker;  // hvis venstre eller høyre tast er trykket, men ikke begge hvis (KeyState (KeyInput.GoRight)! = KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Walk; gå i stykker;  annet hvis (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; mAudioSource.PlayOneShot (mJumpSfx); mCurrentState = CharacterState.Jump; gå i stykker;  gå i stykker; tilfelle CharacterState.Walk: mAnimator.Play ("Walk"); mWalkSfxTimer + = Time.deltaTime; hvis (mWalkSfxTimer> cWalkSfxTime) mWalkSfxTimer = 0.0f; mAudioSource.PlayOneShot (mWalkSfx);  / / Hvis begge eller ikke venstre eller høyre tastene trykkes, stopp deretter å gå og stå hvis (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Stand; mSpeed ​​= Vector2.zero; gå i stykker;  annet hvis (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; ellers mSpeed.x = mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x);  annet hvis (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; ellers mSpeed.x = -mWalkSpeed; mScale.x = Mathf.Abs (mScale.x);  // hvis det ikke er flis å gå på, fall hvis (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; mAudioSource.PlayOneShot (mJumpSfx, 1.0f); mCurrentState = CharacterState.Jump; gå i stykker;  annet hvis (! mOnGround) mCurrentState = CharacterState.Jump; gå i stykker;  gå i stykker; tilfelle CharacterState.Jump: mWalkSfxTimer = cWalkSfxTime; mAnimator.Play ( "Jump"); mSpeed.y + = Constants.cGravity * Time.deltaTime; mSpeed.y = Mathf.Max (mSpeed.y, Constants.cMaxFallingSpeed); hvis (! KeyState (KeyInput.Jump) && mSpeed.y> 0.0f) mSpeed.y = Mathf.Min (mSpeed.y, 200.0f);  hvis (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mSpeed.x = 0.0f;  annet hvis (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; ellers mSpeed.x = mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x);  annet hvis (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; ellers mSpeed.x = -mWalkSpeed; mScale.x = Mathf.Abs (mScale.x);  // hvis vi treffer bakken hvis (mOnGround) // hvis det ikke er noen bevegelseskifte tilstand til stående hvis (mInputs [(int) KeyInput.GoRight] == ​​mInputs [(int) KeyInput.GoLeft]) mCurrentState = CharacterState .Stå; mSpeed ​​= Vector2.zero; mAudioSource.PlayOneShot (mHitWallSfx, 0,5f);  else // enten gå til høyre eller gå til venstre, trykkes så vi endrer tilstanden for å gå mCurrentState = CharacterState.Walk; mSpeed.y = 0.0f; mAudioSource.PlayOneShot (mHitWallSfx, 0,5f);   gå i stykker; tilfelle CharacterState.GrabLedge: break;  UpdateFysics (); hvis (()! (! mWasOnGround && mOnGround) || (! mWasAtCeiling && mAtCeiling) || (! mPushedLeftWall && mPushesLeftWall) || (! mPushedRightWall && mPushesRightWall)) mAudioSource.PlayOneShot (mHitWallSfx, 0,5f); UpdatePrevInputs (); 

Skriv inn tegnet

La oss skrive en Init-funksjon for tegnet. Denne funksjonen tar innspillene som parametere. Vi vil levere disse fra lederklassen senere. Annet enn dette, må vi gjøre ting som:

  • tilordne skalaen
  • tilordne hopphastigheten
  • tilordne gåhastigheten
  • sett inn startposisjonen
  • sett AABB
offentlig ugyldig CharacterInit (bool [] innganger, bool [] prevInputs) 

Vi bruker noen av de definerte konstantene her.

offentlig const float cWalkSpeed ​​= 160.0f; offentlig const float cJumpSpeed ​​= 410.0f; offentlig const float cMinJumpSpeed ​​= 200.0f; offentlig const float cHalfSizeY = 20,0f; offentlig const float cHalfSizeX = 6.0f;

Når det gjelder demo, kan vi sette startposisjonen til stillingen i redigeringsprogrammet.

offentlig tomrom CharacterInit (bool [] innganger, bool [] prevInputs) mPosition = transform.position; 

For AABB må vi sette forskyvning og halv størrelse. Forskjellen i tilfelle av demoens sprite må bare være halv størrelse.

offentlig tomrom CharacterInit (bool [] innganger, bool [] prevInputs) mPosition = transform.position; mAABB.halfSize = ny Vector2 (Constants.cHalfSizeX, Constants.cHalfSizeY); mAABBOffset.y = mAABB.halfSize.y; 

Nå kan vi ta vare på resten av variablene.

offentlig tomrom CharacterInit (bool [] innganger, bool [] prevInputs) mPosition = transform.position; mAABB.halfSize = ny Vector2 (Constants.cHalfSizeX, Constants.cHalfSizeY); mAABBOffset.y = mAABB.halfSize.y; mInputs = innganger; mPrevInputs = prevInputs; mJumpSpeed ​​= Constants.cJumpSpeed; mWalkSpeed ​​= Constants.cWalkSpeed; mScale = Vector2.one; 

Vi må ringe denne funksjonen fra spilllederen. Lederen kan settes opp på mange måter, alt avhengig av verktøyene du bruker, men generelt er ideen det samme. I lederens init må vi opprette inngangsarrayer, opprette en spiller og starte den.

offentlig klasse spill offentlig karakter mPlayer; bool [] mInputs; bool [] mPrevInputs; void Start () inputs = new bool [(int) KeyInput.Count]; prevInputs = ny bool [(int) KeyInput.Count]; player.CharacterInit (innganger, prevInputs); 

I tillegg, i lederens oppdatering, må vi oppdatere spilleren og spillerens innganger.

ugyldig oppdatering () innganger [(int) KeyInput.GoRight] = Input.GetKey (goRightKey); innganger [(int) KeyInput.GoLeft] = Input.GetKey (goLeftKey); innganger [(int) KeyInput.GoDown] = Input.GetKey (goDownKey); innganger [(int) KeyInput.Jump] = Input.GetKey (goJumpKey);  void FixedUpdate () player.CharacterUpdate (); 

Legg merke til at vi oppdaterer tegnets fysikk i den faste oppdateringen. Dette vil sørge for at hoppene alltid vil være i samme høyde, uansett hvilken bildefrekvens vårt spill jobber med. Det er en utmerket artikkel av Glenn Fiedler om hvordan du løser timestep, hvis du ikke bruker Unity.

Test tegnkontrollen

På dette punktet kan vi teste tegningens bevegelse og se hvordan det føles. Hvis vi ikke liker det, kan vi alltid endre parametrene eller måten hastigheten endres på tastetrykk.

Sammendrag

Tegnkontrollene kan virke veldig vektløse og ikke så hyggelige som en momentumbasert bevegelse for noen, men dette handler bare om hva slags kontroller som passer best for ditt spill. Heldigvis er det bare ganske enkelt å endre måten tegnet beveger seg på. Det er nok å endre hvordan hastighetsverdien endres i tur- og hoppestatistikkene.

Det er det for første del av serien. Vi har endte opp med en enkel tegnbevegelsesplan, men ikke mye mer. Det viktigste er at vi legger veien for neste del, hvor vi får karakteren til å virke sammen med en tilkart.