Creați un joc de hochei AI Folosind comportamentul de direcție Mecanica jocurilor

În ultimele postări din această serie, ne-am concentrat pe concepte în spatele inteligenței artificiale despre care am învățat. În această parte, vom împacheta toată implementarea într-un joc de hochei complet jucat. Veți învăța cum să adăugați piesele lipsă necesare pentru a transforma acest lucru într-un joc, cum ar fi scorul, power-up-urile și un pic de design de joc.

Rezultat final

Mai jos este jocul care va fi implementat folosind toate elementele descrise în acest tutorial.

Gândirea joc de design

Partea anterioară a acestei serii sa axat pe explicarea modului în care funcționează jocul AI. Fiecare parte detaliază un aspect particular al jocului, cum ar fi modul în care sportivii se mișcă și cum sunt implementate atacurile și apărarea. Ele s-au bazat pe concepte precum comportamentele de direcție și mașinile de stat finite bazate pe stack.

Cu toate acestea, pentru a face un joc pe deplin jucabil, toate aceste aspecte trebuie să fie înfășurate într-un nucleu joc mecanic. Cea mai evidentă alegere ar fi punerea în aplicare a tuturor regulilor oficiale ale unui meci oficial de hochei, însă acest lucru ar necesita multă muncă și timp. Să luăm în schimb o abordare mai simplă.

Toate regulile de hochei vor fi înlocuite cu una singură: dacă purtați pucul și sunteți atins de un adversar, îngheți și distrugeți-vă într-un milion de piese! Acesta va face jocul mai simplu de jucat și distractiv pentru ambii jucători: cel care poartă pucul și cel care încearcă să-l recupereze.

Pentru a îmbunătăți acest mecanic, vom adăuga câteva upgrade-uri. Ei vor ajuta jucătorul să înscrie și să facă jocul un pic mai dinamic.

Adăugarea abilității de marcare

Să începem cu sistemul de punctaj, responsabil pentru a determina cine câștigă sau pierde. O echipă înscrie de fiecare dată când pucul intră în jocul adversarului.

Cea mai ușoară modalitate de a implementa acest lucru este folosirea a două dreptunghiuri suprapuse:

Suprapuse dreptunghiuri care descriu zona țintă. Dacă pucul se ciocnește cu dreptunghiul roșu, echipa scoruriază.

Dreptunghiul verde reprezintă zona ocupată de structura țintă (cadrul și plasa). Funcționează ca un bloc solid, astfel încât pucul și sportivii nu vor putea să treacă prin el; ei vor sări înapoi.

Dreptunghiul roșu reprezintă "zona de scor". Dacă pucul se suprapune peste acest dreptunghi, înseamnă că o echipă tocmai a marcat.

Dreptul dreptunghi roșu este mai mic decât cel verde și plasat în fața acestuia, astfel încât dacă pucul atinge țintă din orice parte, ci din față, acesta va sări înapoi și nu se va adăuga un scor:

Câteva exemple despre modul în care pucul se va comporta dacă atinge dreptunghiurile în timp ce se mișcă.

Organizarea tuturor după cineva scoruri

După ce o echipă scoruri, toți sportivii trebuie să se întoarcă în poziția inițială, iar pucul trebuie să fie plasat din nou la centrul de patinoar. După acest proces, meciul poate continua.

Mutarea sportivilor la poziția lor inițială

După cum sa explicat în prima parte a acestei serii, toți sportivii au un stat AI numit prepareForMatch care le va muta în poziția inițială și le va face să se oprească fără probleme.

Când pucul se suprapune peste una din "zonele de scor", orice stare activă AI a tuturor sportivilor este eliminată și prepareForMatch este împins în creier. Oriunde sunt sportivii, se vor întoarce la poziția inițială după câteva secunde:

Deplasarea camerei spre centrul de patinoar

Deoarece aparatul foto urmărește întotdeauna pucul, dacă este teleportat direct la centrul de patinoar după ce cineva scoruri, vizualizarea curentă se va schimba brusc, ceea ce ar fi urât și confuz.

O modalitate mai bună de a face acest lucru este de a muta pucul fără probleme spre centrul de patinoar; deoarece aparatul foto urmărește pucul, acest lucru va aluneca cu grijă vederea de la poartă spre centrul pantofului. 

Acest lucru poate fi obținut prin schimbarea vectorului vitezei pucului după ce acesta lovește orice zonă de țintă. Noul vector de viteză trebuie să "împingă" pucul către centrul de mers, astfel încât acesta poate fi calculat ca:

var c: Vector3D = getRinkCenter (); var p: Vector3D = pozitie puc; var v: Vector3D = c - p; v = normaliza (v) * 100; puck.velocity = v;

Prin scăderea poziției centrului pantalonului de la poziția actuală a pucului, este posibil să se calculeze un vector care indică direct spre centrul.

După normalizarea acestui vector, acesta poate fi scalat de orice valoare, cum ar fi 100, care controlează cât de repede pucul se deplasează spre centrul pantofului.

Mai jos este o imagine cu o reprezentare a vectorului de viteză nou:

Calcularea unui nou vector de viteză care va muta pucul spre centrul de mers pe jos.

Acest vector V este folosit ca vector de viteză a pucului, astfel încât pucul se va deplasa spre centrul pantofului conform destinației.

Pentru a preveni orice comportament ciudat în timp ce pucul se îndreaptă spre centrul pantofului, cum ar fi o interacțiune cu un atlet, pucul este dezactivat în timpul procesului. În consecință, se oprește interacțiunea cu atleții și este marcată ca invizibilă. Jucătorul nu va vedea pucul în mișcare, dar camera va continua să o urmeze.

Pentru a decide dacă pucul este deja în poziție, distanța dintre acesta și centrul de mers se calculează în timpul mișcării. Dacă este mai mică decât 10, de exemplu, puck-ul este suficient de aproape pentru a fi plasat direct în centrul pantofului și a fost reactivat astfel încât meciul să poată continua.

Adăugarea de Power-Ups

Ideea din spatele puterii este să ajute jucătorul să atingă obiectivul principal al jocului, care este să înscrie prin transportul pucului spre obiectivul adversarului.

De dragul scopului, jocul nostru va avea doar două power-up-uri: Ghost Ajutor și Te temi de puck. Primul adaugă un timp sportiv trei sportivi suplimentari echipei de jucători, în timp ce ultimul face adversarii să părăsească pucul pentru câteva secunde.

Power-up-uri sunt adăugate la ambele echipe atunci când cineva scoruri.

Implementarea ajutorului "Ghost Help"

Deoarece toți sportivii adăugați de Ghost Ajutor de putere-up sunt temporare, Atlet clasa trebuie modificată pentru a permite unui atlet să fie marcat ca "fantomă". Dacă un atlet este o fantomă, se va elimina din joc după câteva secunde.

Mai jos este Atlet clasă, subliniind doar adăugările făcute pentru a acomoda funcționalitatea fantomă:

clasa publica Athlete // (...) private var mGhost: Boolean; // spune dacă atletul este o fantomă (un powerup care adaugă noi sportivi pentru a ajuta la furarea puckului). privat var mGhostCounter: număr; // numără momentul în care o fantomă va rămâne activă Funcționarea publică a atletului (ThePosX: Number, ThePosY: Number ,TotalMass: Number) // (...) mGhost = false; mGhostCounter = 0; // (...) funcția publică setGhost (Status: Boolean ,Durația: Număr): void mGhost = theStatus; mGhostCounter =Durația;  funcția publică amIAGhost (): Boolean return mGhost;  public function update (): void // (...) // Actualizați numărătoarele powerup și lucrurile updatePowerups (); // (...) funcția publică updatePowerups (): void // TODO. 

Proprietatea mGhost este un boolean care spune dacă atletul este o fantomă sau nu, în timp ce mGhostCounter conține numărul de secunde pe care sportivul trebuie să îl aștepte înainte de a se elimina din joc.

Aceste două proprietăți sunt utilizate de către updatePowerups () metodă:

funcția privată updatePowerups (): void // Dacă atletul este o fantomă, are un contor care controlează // când trebuie eliminat. dacă (amIAGhost ()) mGhostCounter - = time_elapsed; dacă (mGhostCounter <= 2)  // Make athlete flicker when it is about to be removed. flicker(0.5);  if (mGhostCounter <= 0)  // Time to leave this world! (again) kill();   

updatePowerups () metodă, numită în cadrul atletului Actualizați() de rutină, se va ocupa de toate procesele de procesare a puterii în atlet. Chiar acum tot ce face este să verifici dacă atletul actual este o fantomă sau nu. Dacă este, atunci mGhostCounter proprietatea este diminuată cu timpul scurs de la ultima actualizare.

Atunci când valoarea mGhostCounter atinge zero, înseamnă că sportivul temporar a fost activ timp de mult timp, așa că trebuie să se elimine din joc. Pentru a face jucătorul conștient de acest lucru, sportivul va începe să clipească în ultimele două secunde înainte de a dispărea.

În cele din urmă, este timpul să punem în aplicare procesul de adăugare a sportivilor temporari atunci când activarea este activată. Acest lucru este efectuat în powerupGhostHelp () metoda, disponibil în logica principală a jocului:

funcția privată powerupGhostHelp (): void var aAlete: atlet; pentru (var i: int = 0; i < 3; i++)  // Add the new athlete to the list of athletes aAthlete = addAthlete(RINK_WIDTH / 2, RINK_HEIGHT - 100); // Mark the athlete as a ghost which will be removed after 10 seconds. aAthlete.setGhost(true, 10);  

Această metodă se repetă peste o buclă care corespunde cu cantitatea de atleți temporari care se adaugă. Fiecare atlet nou este adăugat la fundul patinoarului și marcat ca o fantomă. 

După cum sa descris anterior, sportivii fantomă se vor elimina din joc.

Implementarea funcției "Fear The Puck"

Te temi de puck Power-up face ca toti adversarii sa fuga de puc pentru cateva secunde. 

La fel ca și Ghost Ajutor putere-up, Atlet clasa trebuie să fie modificată pentru a acomoda această funcționalitate:

clasa publica Athlete // (...) privat var mFearCounter: Număr; // numără timpul în care sportivul trebuie să se sustragă de la puc (atunci când frica este activă). funcția publică funcțională atlet (numărul de poștă: numărul, numărul de poștă, numărul total, numărul total) // (...) mFearCounter = 0; // (...) frica funcției publicePuck (TheDuration: Number = 2): void mFearCounter =Durația;  // Returnează true dacă mFearCounter are o valoare și atletul // nu este inactiv sau nu se pregătește pentru o potrivire. funcția privată shouldIEvadeFromPuck (): Boolean retur mFearCounter> 0 && mBrain.getCurrentState ()! = idle && mBrain.getCurrentState ()! = prepareForMatch;  funcția privată updatePowerups (): void if (mFearCounter> 0) mFearCounter - = elapsed_time;  // (...) funcția publică funcțională (): void // (...) // Actualizați contoarele powerup și lucrurile updatePowerups (); // Dacă atletul este un adversar controlat de AI dacă (amIAnAiControlledOpponent ()) // Verificați dacă este activă "teama de puc". Dacă este adevărat, evitați din puc. dacă (ifIEvadeFromPuck ()) evadeFromPuck ();  // (()) funcția publică evadeFromPuck (): void // TODO

Mai întâi updatePowerups () metoda se schimba pentru a descrește mFearCounter proprietate, care conține timpul în care sportivul ar trebui să evite pucul. mFearCounter proprietatea se schimbă de fiecare dată când se aplică metoda fearPuck () se numește.

În Atlet„s Actualizați() , se adaugă un test pentru a verifica dacă trebuie să se efectueze pornirea. Dacă atletul este un adversar controlat de AI (amIAnAiControlledOpponent () se intoarce Adevărat), iar sportivul ar trebui să se sustragă pucului (shouldIEvadeFromPuck () se intoarce Adevărat precum și) evadeFromPuck () metoda este invocată.

evadeFromPuck () metoda folosește comportamentul de evadare, ceea ce face ca o entitate să evite orice obiect și traiectoria sa cu totul:

funcția privată evadeFromPuck (): void mBoid.steering = mBoid.steering + mBoid.evade (getPuck (). getBoid ()); 

Toate evadeFromPuck () metoda este de a adăuga o forță de evadare forței de direcție a atletului. Îl face să evite pucul fără a ignora forțele de direcție deja adăugate, cum ar fi cele create de statul AI activ în prezent.

Pentru a fi evadabil, pucul trebuie să se comporte ca un băț, așa cum fac toți sportivii (mai multe informații despre asta în prima parte a seriei). În consecință, trebuie adăugată o proprietate boid, care conține poziția curentă și viteza puckului Pucul clasă:

clasa Puck // (...) privat var mBoid: Boid; // (...) funcția publică funcțională () // (...) mBoid.update ();  funcția publică getBoid (): Boid return mBoid;  // (...)

În cele din urmă, actualizăm principala logică a jocului pentru a face adversarii să se teamă de puck atunci când activarea este activată:

funcția privată powerupFearPuck (): void var i: uint, atleți: Array = rightTeam.members, size: uint = athletes.length; pentru (i = 0; i < size; i++)  if (athletes[i] != null)  // Make athlete fear the puck for 3 seconds. athletes[i].fearPuck(3);   

Metoda se repetă asupra tuturor sportivilor adversari (echipa potrivită, în acest caz), care apelează fearkPuck () metoda fiecăruia dintre ei. Acest lucru va declanșa logica care îi face pe sportivi să se teamă de puck în câteva secunde, după cum sa explicat anterior.

Înghețarea și sfărâmarea

Ultima adăugare a jocului este partea care îngheață și sfărâmă. Se efectuează în logica jocului principal, unde o rutină verifică dacă sportivii din echipa stângă se suprapun cu sportivii echipei drepte.

Această verificare a suprapunerii este efectuată automat de către motorul de joc Flixel, care invocă un apel inversat de fiecare dată când se găsește o suprapunere:

sportivii de activitate privatOverlapped (theLeftAlelete: Athlete, TheRightAllete: Athlete): void // Pucul are un proprietar? dacă (mPuck.owner! = null) // Da, da. dacă (mPuck.owner ==LeftAthlete) // proprietarul lui Puck este atletul stânga theLeftAthlete.shatter (); mPuck.setOwner (theRightAthlete);  altfel dacă (mPuck.owner ==RightAthlete) // Proprietarul lui Puck este atletul drept theRightAthlete.shatter (); mPuck.setOwner (theLeftAthlete); 

Acest callback primește ca parametri atleții fiecărei echipe care s-au suprapus. Un test verifică dacă proprietarul pucului nu este nul, ceea ce înseamnă că este purtat de cineva.

În acest caz, proprietarul pucului este comparat cu sportivii care s-au suprapus. Dacă unul dintre aceștia poartă pucul (așa că el este proprietarul pucului), el este spulberat și proprietatea pucului trece la celălalt atlet.

sfărâma() metodă în Atlet clasa va marca atletul ca inactiv și îl va plasa în partea de jos a patinoarului după câteva secunde. De asemenea, va emite mai multe particule reprezentând bucăți de gheață, dar acest subiect va fi acoperit într-un alt post.

Concluzie

În acest tutorial, am implementat câteva elemente necesare pentru a transforma prototipul nostru de hochei într-un joc pe deplin redat. În mod intenționat am pus accentul pe conceptele din spatele fiecăruia dintre ele, în loc de modul în care le-am implementat efectiv în motorul de joc X sau Y.

Metoda de înghețare și de spargere folosită pentru joc ar putea părea prea fantastic, dar ajută la menținerea proiectului gestionabil. Regulile sportive sunt foarte specifice, iar implementarea lor poate fi dificilă.

Prin adăugarea câtorva ecrane și a unor elemente HUD, vă puteți crea propriul joc de hochei complet din acest demo!

Referințe

  • Rink: Stadionul de hochei pe GraphicRiver
  • Sprites: Jucătorii de hochei de Taylor J Glidden
  • Icoane: Joc-Icoane de Lorc
  • Cursorul mouse-ului: Cursorul lui Iwan Gabovitch
  • Instrucțiuni: Tastatura Pack de Nicolae Berbece
  • Crosshair: Crosshairs Pack de Bryan
  • SFX / Muzica: spargerea lui Michel Baradari, lovitura si bucuria puiului de gr8sfx, muzica de DanoSongs.com