Această serie va acoperi modul de a crea un sistem simplu și robust de fizică pentru un joc cu platformă. În această parte, vom examina datele de coliziune a caracterului.
Bine, astfel încât premisa arată astfel: vrem să facem un platformer 2D cu fizică simplă, robustă, receptivă, precisă și predictibilă. Nu vrem să folosim în acest caz un mare motor de fizică 2D și există câteva motive pentru acest lucru:
Desigur, există și mulți profesioniști care folosesc un motor fizic off-the-shelf, cum ar fi posibilitatea de a înființa interacțiuni fizice complexe destul de ușor, dar asta nu este ceea ce avem nevoie pentru jocul nostru.
Un motor fizic personalizat ajută jocul să aibă un simț personalizat față de el, și asta e cu adevărat important! Chiar dacă veți începe cu o configurație relativ de bază, modul în care lucrurile se vor mișca și interacționa unul cu celălalt va fi întotdeauna influențat numai de propriile reguli, mai degrabă decât de alții. Hai să ajungem la asta!
Să începem prin definirea tipurilor de forme pe care le vom folosi în fizica noastră. Una dintre formele cele mai de bază pe care le putem folosi pentru a reprezenta un obiect fizic într-un joc este o cutie de legare Axis Aligned (AABB). AABB este, în principiu, un dreptunghi netratat.
În multe jocuri de platformă, AABB-urile sunt suficiente pentru a aproxima corpul fiecărui obiect din joc. Ele sunt extrem de eficiente, deoarece este foarte ușor să se calculeze o suprapunere între AABB-uri și necesită foarte puține date - pentru a descrie un AABB, este suficient să cunoaștem centrul și dimensiunea.
Fără alte idei, să creăm un struct pentru AABB.
public struct AABB
Așa cum am menționat mai devreme, tot ceea ce avem nevoie aici, în ceea ce privește datele, sunt doi vectori; primul va fi centrul AABB, iar al doilea jumătate. De ce jumătate? De cele mai multe ori pentru calcule vom avea nevoie de jumătate de mărime oricum, așa că în loc de a calcula de fiecare dată când vom pur și simplu să-l memoreze în loc de dimensiunea completă.
public struct AABB public Vector2 centru; public Vector2 halfSize;
Să începem prin adăugarea unui constructor, deci este posibil să creați structul cu parametri personalizați.
public AABB (Centrul Vector2, Vector2 halfSize) this.center = center; this.halfSize = halfSize;
Cu aceasta putem crea funcțiile de verificare a coliziunii. Mai întâi, să facem o simplă verificare dacă două AABB-uri se ciocnesc unul cu celălalt. Acest lucru este foarte simplu - trebuie doar să vedem dacă distanța dintre centrele de pe fiecare axă este mai mică decât suma jumătăților de dimensiuni.
suprapuneri publice bool (alt AABB) if (Mathf.Abs (center.x - other.center.x)> halfSize.x + other.halfSize.x) return false; dacă (Mathf.Abs (center.y - other.center.y)> halfSize.y + other.halfSize.y) returnează false; return true;
Iată o imagine care demonstrează această verificare pe axa x; axa y este verificată în același mod.
După cum puteți vedea, dacă suma jumătăților de dimensiuni ar fi mai mică decât distanța dintre centre, nu ar fi posibilă o suprapunere. Observați că, în codul de mai sus, putem scăpa mai devreme de verificarea coliziunii dacă descoperim că obiectele nu se suprapun pe prima axă. Suprapunerea trebuie să existe pe ambele axe, în cazul în care AABB-urile trebuie să se ciocnească în spațiul 2D.
Să începem prin crearea unei clase pentru un obiect care este influențat de fizica jocului. Mai târziu, vom folosi acest lucru ca bază pentru un obiect de jucător real. Să numim această clasă MovingObject.
clasa publică MovingObject
Acum, să ocupăm această clasă cu datele. Vom avea nevoie de o mulțime de informații pentru acest obiect:
Poziția, viteza și scala sunt vectori 2D.
clasa publică MovingObject public Vector2 mOldPosition; public Vector2 mPoziție; public Vector2 mOldSpeed; public Vector2 mSpeed; public Vector2 mScale;
Acum, să adăugăm AABB și offsetul. Este necesar un offset pentru a putea potrivi liber AABB cu sprite-ul obiectului.
public AABB mAABB; public Vector2 mAABBOset;
În cele din urmă, să declarăm variabilele care indică starea poziției obiectului, indiferent dacă este pe pământ, lângă un perete sau la tavan. Acestea sunt foarte importante pentru că ne vor spune dacă putem să sară sau, de exemplu, trebuie să jucăm un sunet după ce ne-am îmbolnăvit într-un zid.
bool public mPushedRightWall; bool public mPushesRightWall; public bool mPushedLeftWall; bool public mPushesLeftWall; bool public mWasOnGround; bool public mOnGround; boola publică mWasAtCeiling; public bool mAtCeiling;
Acestea sunt elementele de bază. Acum, să creăm o funcție care să actualizeze obiectul. Deocamdată nu vom seta totul, ci doar suficient pentru a putea începe să creăm controale de bază ale caracterelor.
public void UpdatePhysics ()
Primul lucru pe care vom dori să-l facem este să salvați datele cadrului precedent la variabilele corespunzătoare.
public void UpdatePhysics () mOldPosition = mPoziție; mOldSpeed = mSpeed; mWasOnGround = mOnGround; mPushedRightWall = mPushesRightWall; mPushedLeftWall = mPushesLeftWall; mWasAtCeiling = mAtCeiling;
Acum, să actualizăm poziția folosind viteza actuală.
mPoziția + = mSpeed * Time.deltaTime;
Și pentru moment, să facem ca astfel, dacă poziția verticală este mai mică decât zero, vom presupune că personajul este pe teren. Asta e doar pentru moment, așa că putem configura controalele personajului. Mai târziu, vom face o coliziune cu o tabelă.
dacă (mPosition.y < 0.0f) mPosition.y = 0.0f; mOnGround = true; else mOnGround = false;
După aceasta, trebuie să actualizăm și centrul AABB, așa că se potrivește de fapt noii poziții.
mAABB.center = mPoziție + mAABBOset;
Pentru proiectul demo, folosesc Unitatea și pentru a actualiza poziția obiectului care trebuie aplicat componentei de transformare, deci facem și asta. Același lucru trebuie făcut pentru scară.
mTransform.position = Vector3 nou (Mathf.Round (mPosition.x), Mathf.Round (mPosition.y), - 1.0f); mTransform.localScale = Vector3 nou (mScale.x, mScale.y, 1.0f);
După cum puteți vedea, poziția rendered este rotunjită. Acest lucru este pentru a vă asigura că caracterul randat este întotdeauna cuplat la un pixel.
Acum, când avem o clasă MovingObject de bază, putem începe să jucăm cu mișcarea de caractere. Este o parte foarte importantă a jocului, la urma urmei, și se poate face destul de mult imediat - nu este nevoie să vă plictisiți prea mult în sistemele de jocuri, și va fi gata atunci când va trebui să încercăm caracterul nostru - coliziuni de hărți.
Mai întâi, să creăm o clasă de caractere și să o derivăm din clasa MovingObject.
clasa publica Caracter: MovingObject
Va trebui să rezolvăm câteva lucruri aici. Mai întâi de toate, intrările - să facem un enum care să acopere toate comenzile personajului. Să îl creăm într-un alt fișier și să îl numim KeyInput.
public keyInput GoLeft = 0, GoRight, GoDown, Jump, Count)
După cum puteți vedea, personajul nostru se poate mișca la stânga, la dreapta, în jos și sări în sus. Deplasarea în jos va funcționa numai pe platforme unidirecționale, când vrem să cădem prin ele.
Acum, să declarăm două matrice în clasa Character, unul pentru intrările cadrului curent și altul pentru rama anterioară. În funcție de joc, această configurare poate face mai mult sau mai puțin sens. De obicei, în loc să salveze starea cheie într-o matrice, este verificată la cerere utilizând funcțiile specifice ale unui motor sau ale cadrului. Cu toate acestea, având o matrice care nu este strict legată de intrarea reală poate fi benefică, dacă, de exemplu, vrem să simulam presele de taste.
bool protejat [] mInputs; bool protejat [] mPrevInputs;
Aceste matrice vor fi indexate de enumerarea KeyInput. Pentru a utiliza cu ușurință acele matrice, să creăm câteva funcții care ne vor ajuta să verificăm o anumită cheie.
bool protejat eliberat (tasta KeyInput) retur (! mInputs [(int) cheie] && mPrevInputs [(int) cheie]); boot protejat KeyState (cheie keyInput) return (mInputs [(int) cheie]); bool protejat Apăsat (tasta KeyInput) retur (tasta mInputs [(int)] &&! mPrevInputs [(int)]];
Nimic special aici - vrem să vedem dacă o cheie a fost doar presată, eliberată, sau dacă este activată sau dezactivată.
Acum, să creăm un alt enum care să dețină toate stările posibile ale personajului.
public enum CharacterState Stand, Walk, Jump, GrabLedge,;
După cum puteți vedea, personajul nostru poate fie să stea nemișcat, să se plimbe, să sară sau să apucă o margine. Acum că acest lucru este făcut, trebuie să adăugăm variabile cum ar fi viteza de salt, viteza de mers și starea actuală.
public CharacterState mCurrentState = CharacterState.Stand; public float mJumpSpeed; public float mWalkSpeed;
Desigur, sunt necesare mai multe date, cum ar fi sprite de caractere, dar modul în care aceasta va arăta depinde foarte mult de tipul de motor pe care îl veți folosi. Deoarece folosesc Unitatea, voi folosi o referință la un Animator pentru a vă asigura că sprite rulează animația pentru o stare adecvată.
Bine, acum putem începe lucrul la buclă de actualizare. Ce vom face aici va depinde de starea actuală a personajului.
public void CharacterUpdate () comutare (mCurrentState) caz CharacterState.Stand: break; caz CharacterState.Walk: break; caz CharacterState.Jump: break; caz CharacterState.GrabLedge: break;
Să începem prin completarea a ceea ce trebuie făcut atunci când personajul nu se mișcă - în starea de stand. În primul rând, viteza trebuie setată la zero.
caz CharacterState.Stand: mSpeed = Vector2.zero; pauză;
De asemenea, dorim să arătăm sprite-ul corespunzător pentru stat.
caz CharacterState.Stand: mSpeed = Vector2.zero; mAnimator.Play ( "Stand"); pauză;
Acum, dacă personajul nu este pe teren, nu mai poate sta, așa că trebuie să schimbăm statul să sară.
caz CharacterState.Stand: mSpeed = Vector2.zero; mAnimator.Play ( "Stand"); dacă (! mOnGround) mCurrentState = CharacterState.Jump; pauză; pauză;
Dacă este apăsată tasta GoLeft sau GoRight, atunci va trebui să ne schimbăm starea pentru a merge.
caz CharacterState.Stand: mSpeed = Vector2.zero; mAnimator.Play ( "Stand"); dacă (! mOnGround) mCurrentState = CharacterState.Jump; pauză; dacă (KeyState (KeyInput.GoRight)! = KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Walk; break break;
În cazul în care tasta Jump este apăsată, dorim să setăm viteza verticală la viteza de salt și să schimbăm starea pentru a sari.
dacă (KeyState (KeyInput.GoRight)! = KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Walk; pauză; altfel dacă (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; mCurrentState = CharacterState.Jump; pauză;
Asta va fi pentru acest stat, cel puțin pentru moment.
Acum, să creați o logică pentru a vă deplasa pe teren și imediat începeți să jucați animația de mers pe jos.
caz CharacterState.Walk: mAnimator.Play ("Walk"); pauză;
Dacă nu apăsăm butonul din stânga sau din dreapta sau ambele sunt apăsate, vrem să ne întoarcem la starea staționară.
dacă (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Stand; mSpeed = Vector2.zero; pauză;
Dacă apăsați tasta GoRight, trebuie să setați viteza orizontală la mWalkSpeed și să vă asigurați că scutul este scalat în mod corespunzător - scala orizontală trebuie schimbată dacă vrem să răsturnăm sprite orizontal.
De asemenea, ar trebui să ne mișcăm numai dacă nu există un obstacol în față, deci dacă mPushesRightWall este setat la adevărat, atunci viteza orizontală ar trebui să fie setată la zero dacă ne mișcăm bine.
dacă (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Stand; mSpeed = Vector2.zero; pauză; altfel dacă (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; altfel mSpeed.x = mWalkSpeed; mScale.x = Mathf.Abs (mScale.x); altfel dacă (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; altfel mSpeed.x = -mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x);
De asemenea, trebuie să ne ocupăm și de partea stângă în același mod.
Așa cum am făcut pentru starea în picioare, trebuie să vedem dacă este apăsat un buton de salt și să setăm viteza verticală dacă este așa.
dacă (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; MudioSource.PlayOneShot (mJumpSfx, 1.0f); mCurrentState = CharacterState.Jump; pauză;
În caz contrar, dacă personajul nu este pe teren, atunci trebuie să schimbe starea pentru a sari, dar fără o adăugare de viteză verticală, așa că pur și simplu cade.
dacă (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; MudioSource.PlayOneShot (mJumpSfx, 1.0f); mCurrentState = CharacterState.Jump; pauză; altfel dacă (! mOnGround) mCurrentState = CharacterState.Jump; pauză;
Asta e pentru mersul pe jos. Să trecem la starea de salt.
Să începem prin stabilirea unei animații adecvate pentru sprite.
mAnimator.Play ( "Salt");
În starea Jump, trebuie să adăugăm gravitația la viteza personajului, astfel că merge mai repede și mai repede spre sol.
mSpeed.y + = Constants.cGravity * Time.deltaTime;
Dar ar fi sensibil să adăugăm o limită, astfel încât personajul să nu poată cădea prea repede.
mSpeed.y = Mathf.Max (mSpeed.y, Constants.cMaxFallingSpeed);
În multe jocuri, când caracterul este în aer, manevrabilitatea scade, dar vom merge pentru niște controale foarte simple și exacte, care să permită o flexibilitate maximă în aer. Deci, dacă apăsăm pe tasta GoLeft sau GoRight, personajul se mișcă în direcție în timp ce sărind cât de repede ar fi dacă ar fi fost la pământ. În acest caz putem copia pur și simplu logica mișcării din starea de mers.
dacă (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mSpeed.x = 0.0f; altfel dacă (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; altfel mSpeed.x = mWalkSpeed; mScale.x = Mathf.Abs (mScale.x); altfel dacă (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; altfel mSpeed.x = -mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x);
În cele din urmă, vom face saltul mai mare dacă butonul de salt este apăsat mai mult. Pentru a face acest lucru, ceea ce vom face efectiv este să faceți saltul mai jos dacă butonul de salt nu este apăsat.
dacă (! KeyState (KeyInput.Jump) && mSpeed.y> 0.0f) mSpeed.y = Mathf.Min (mSpeed.y, Constants.cMinJumpSpeed);
După cum puteți vedea, dacă tasta de salt nu este apăsată și viteza verticală este pozitivă, atunci strângem viteza la valoarea maximă a cMinJumpSpeed
(200 pixeli pe secundă). Aceasta înseamnă că dacă am apăsa butonul de salt, viteza saltului, în loc să fie egală cu mJumpSpeed
(410 în mod implicit), va scădea la 200 și, prin urmare, saltul va fi mai scurt.
Deoarece nu avem încă nicio geometrie a nivelului, ar trebui să ignorăm acum implementarea programului GrabLedge.
După finalizarea cadrului, putem actualiza intrările anterioare. Să creăm o nouă funcție pentru acest lucru. Tot ce trebuie să facem aici este să mutați valorile de stare cheie din mInputs
array la mPrevInputs
mulțime.
public void UpdatePrevInputs () var count = (octet) KeyInput.Count; pentru (octetul i = 0; i < count; ++i) mPrevInputs[i] = mInputs[i];
La sfârșitul funcției CharacterUpdate, trebuie încă să facem câteva lucruri. Primul este actualizarea fizicii.
UpdatePhysics ();
Acum, că fizica este actualizată, putem vedea dacă ar trebui să jucăm un sunet. Vrem să cântăm un sunet când caracterul se lovește de orice suprafață, dar acum poate atinge doar pământul, deoarece ciocnirea cu tilemap nu este încă implementată.
Să verificăm dacă personajul tocmai a căzut pe pământ. Este foarte ușor să faceți acest lucru cu configurația actuală - trebuie doar să privim dacă caracterul este pe teren chiar acum, dar nu a fost în cadrul anterior.
dacă (mOnGround &&! mWasOnGround) mAudioSource.PlayOneShot (mHitWallSfx, 0.5f);
În cele din urmă, să actualizăm intrările anterioare.
UpdatePrevInputs ();
În general, așa ar trebui să se uite acum funcția CharacterUpdate, cu diferențe minore în funcție de tipul de motor sau de cadrul pe care îl utilizați.
public void CharacterUpdate () comutare (mCurrentState) caz CharacterState.Stand: mWalkSfxTimer = cWalkSfxTime; mAnimator.Play ( "Stand"); mSpeed = Vector2.zero; dacă (! mOnGround) mCurrentState = CharacterState.Jump; pauză; // dacă este apăsată tasta stânga sau dreapta, dar nu ambele dacă (KeyState (KeyInput.GoRight)! = KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Walk; pauză; altfel dacă (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; mAudioSource.PlayOneShot (mJumpSfx); mCurrentState = CharacterState.Jump; pauză; pauză; caz CharacterState.Walk: mAnimator.Play ("Walk"); mWalkSfxTimer + = Timp.deltaTime; dacă (mWalkSfxTimer> cWalkSfxTime) mWalkSfxTimer = 0.0f; mAudioSource.PlayOneShot (mWalkSfx); // dacă ambele taste sau stânga sau dreapta nu sunt apăsate, apoi opriți mersul și stați dacă (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mCurrentState = CharacterState.Stand; mSpeed = Vector2.zero; pauză; altfel dacă (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; altfel mSpeed.x = mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x); altfel dacă (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; altfel mSpeed.x = -mWalkSpeed; mScale.x = Mathf.Abs (mScale.x); // dacă nu există țiglă pentru a merge pe, cad în cazul în care (KeyState (KeyInput.Jump)) mSpeed.y = mJumpSpeed; MudioSource.PlayOneShot (mJumpSfx, 1.0f); mCurrentState = CharacterState.Jump; pauză; altfel dacă (! mOnGround) mCurrentState = CharacterState.Jump; pauză; pauză; caz CharacterState.Jump: mWalkSfxTimer = cWalkSfxTime; mAnimator.Play ( "Salt"); mSpeed.y + = Constants.cGravity * Time.deltaTime; mSpeed.y = Mathf.Max (mSpeed.y, Constants.cMaxFallingSpeed); dacă (! KeyState (KeyInput.Jump) && mSpeed.y> 0.0f) mSpeed.y = Mathf.Min (mSpeed.y, 200.0f); dacă (KeyState (KeyInput.GoRight) == KeyState (KeyInput.GoLeft)) mSpeed.x = 0.0f; altfel dacă (KeyState (KeyInput.GoRight)) if (mPushesRightWall) mSpeed.x = 0.0f; altfel mSpeed.x = mWalkSpeed; mScale.x = -Mathf.Abs (mScale.x); altfel dacă (KeyState (KeyInput.GoLeft)) if (mPushesLeftWall) mSpeed.x = 0.0f; altfel mSpeed.x = -mWalkSpeed; mScale.x = Mathf.Abs (mScale.x); // daca am lovit pamantul daca (mOnGround) // daca nu exista nici o stare de schimbare a miscarii in picioare daca (mInputs [(int) KeyInput.GoRight] == mInputs [(int) KeyInput.GoLeft]) mCurrentState = CharacterState .Stand; mSpeed = Vector2.zero; mAudioSource.PlayOneShot (mHitWallSfx, 0.5f); else // fie mergeți la dreapta, fie mergeți la stânga sunt apăsate pentru a schimba starea pentru a merge mCurrentState = CharacterState.Walk; mSpeed.y = 0.0f; mAudioSource.PlayOneShot (mHitWallSfx, 0.5f); pauză; caz CharacterState.GrabLedge: break; UpdatePhysics (); dacă (mWasOnGround && mOnGround) || (! mWasAtCeiling && mAtCeiling) || (! mPushedLeftWall && mPushesLeftWall) || (! mPushedRightWall && mPushesRightWall)) mAudioSource.PlayOneShot (mHitWallSfx, 0.5f); UpdatePrevInputs ();
Să scriem o funcție Init pentru caracter. Această funcție va lua matricele de intrare ca parametri. Vom furniza aceste informații de la clasa managerului mai târziu. În afară de aceasta, trebuie să facem lucruri precum:
public void CharacterInit (bool [] intrări, bool [] prevInputs)
Vom folosi câteva constante definite aici.
public const float cWalkSpeed = 160.0f; public const float cJumpSpeed = 410.0f; public const float cMinJumpSpeed = 200.0f; public const float cHalfSizeY = 20.0f; public const float cHalfSizeX = 6.0f;
În cazul demonstrației, putem seta poziția inițială în poziția din editor.
public void CharacterInit (bool [] intrări, bool [] prevInputs) mPosition = transform.position;
Pentru AABB, trebuie să setăm offsetul și jumătate. Offsetul în cazul sprite-ului demo trebuie să fie doar jumătate.
public void CharacterInit (bool [] intrări, bool [] prevInputs) mPosition = transform.position; mAABB.halfSize = nou Vector2 (Constants.cHalfSizeX, Constants.cHalfSizeY); mAABBOffset.y = mAABB.halfSize.y;
Acum putem avea grijă de restul variabilelor.
public void CharacterInit (bool [] intrări, bool [] prevInputs) mPosition = transform.position; mAABB.halfSize = nou Vector2 (Constants.cHalfSizeX, Constants.cHalfSizeY); mAABBOffset.y = mAABB.halfSize.y; mInputs = intrări; mPrevInputs = prevInputs; mJumpSpeed = Constants.cJumpSpeed; mWalkSpeed = Constants.cWalkSpeed; mScale = Vector2.one;
Trebuie să numim această funcție de la managerul jocurilor. Managerul poate fi configurat în mai multe moduri, toate în funcție de instrumentele pe care le utilizați, dar, în general, ideea este aceeași. În initul managerului, trebuie să creăm matricele de intrare, să creăm un jucător și să îl inițializăm.
joc de clasă publică public Character mPlayer; bool [] mInput-uri; bool [] mPrevInputs; void Start () intrări = bool nou [(int) KeyInput.Count]; prevInputs = bool nou [(int) KeyInput.Count]; player.CharacterInit (intrări, prevInputs);
În plus, în actualizarea managerului, trebuie să actualizăm datele playerului și ale jucătorului.
void Actualizare () intrări [(int) KeyInput.GoRight] = Input.GetKey (goRightKey); intrări [(int) KeyInput.GoLeft] = Input.GetKey (goLeftKey); intrări [(int) KeyInput.GoDown] = Input.GetKey (goDownKey); intrări [(int) KeyInput.Jump] = Input.GetKey (goJumpKey); void FixedUpdate () player.CharacterUpdate ();
Rețineți că actualizăm fizica caracterului în actualizarea fixă. Acest lucru vă va face să vă asigurați că salturile vor fi întotdeauna la aceeași înălțime, indiferent de ce rată de cadre funcționează jocul nostru. Există un articol excelent al lui Glenn Fiedler cu privire la modul de remediere a timestepului, în cazul în care nu utilizați Unity.
În acest moment putem testa mișcarea personajului și putem vedea cum se simte. Dacă nu ne place, putem schimba întotdeauna parametrii sau modul în care viteza este schimbată la apăsarea tastelor.
Controlul caracterelor poate părea foarte greu și nu este atât de plăcut ca o mișcare bazată pe impuls pentru unii, dar este vorba doar de ce fel de controale ar potrivi cel mai bine jocului tău. Din fericire, schimbarea modului în care personajul se mișcă este destul de ușor; este suficient să modificați modul în care se modifică valoarea vitezei în stările de mers și salt.
Asta e pentru prima parte a seriei. Am ajuns cu o schemă de mișcare a caracterului simplu, dar nu cu mult mai mult. Cel mai important lucru este că am stabilit calea pentru următoarea parte, în care vom face ca personajul să interacționeze cu o tabelă.