|
|
1 month ago | |
|---|---|---|
| .. | ||
| solution | 3 months ago | |
| your-work | 3 months ago | |
| README.md | 1 month ago | |
| assignment.md | 1 month ago | |
README.md
Construiește un Joc Spațial Partea 6: Final și Restart
Fiecare joc grozav are nevoie de condiții clare de finalizare și un mecanism de restart fluid. Ai construit un joc spațial impresionant cu mișcare, luptă și punctaj - acum este momentul să adaugi piesele finale care îl fac să pară complet.
Jocul tău rulează în prezent la nesfârșit, la fel ca sondele Voyager lansate de NASA în 1977 - încă călătorind prin spațiu după decenii. Deși acest lucru este în regulă pentru explorarea spațială, jocurile au nevoie de puncte finale definite pentru a crea experiențe satisfăcătoare.
Astăzi, vom implementa condiții corecte de câștig/pierdere și un sistem de restart. Până la sfârșitul acestei lecții, vei avea un joc finisat pe care jucătorii îl pot finaliza și relua, la fel ca jocurile arcade clasice care au definit acest mediu.
Test înainte de lecție
Înțelegerea condițiilor de finalizare ale jocului
Când ar trebui să se termine jocul tău? Această întrebare fundamentală a modelat designul jocurilor încă din era arcade. Pac-Man se termină când ești prins de fantome sau când cureți toate punctele, în timp ce Space Invaders se termină când extratereștrii ajung jos sau când îi distrugi pe toți.
Ca creator al jocului, tu definești condițiile de victorie și înfrângere. Pentru jocul nostru spațial, iată câteva abordări dovedite care creează un gameplay captivant:
Nnave inamice au fost distruse: Este destul de comun ca, dacă împarți un joc în diferite niveluri, să fie necesar să distrugiNnave inamice pentru a finaliza un nivel.- Nava ta a fost distrusă: Există cu siguranță jocuri în care pierzi dacă nava ta este distrusă. O altă abordare comună este conceptul de vieți. De fiecare dată când nava ta este distrusă, pierzi o viață. Odată ce toate viețile au fost pierdute, pierzi jocul.
- Ai colectat
Npuncte: O altă condiție comună de finalizare este să colectezi puncte. Modul în care obții punctele depinde de tine, dar este destul de comun să atribui puncte diverselor activități, cum ar fi distrugerea unei nave inamice sau colectarea obiectelor pe care acestea le lasă când sunt distruse. - Finalizarea unui nivel: Acest lucru poate implica mai multe condiții, cum ar fi distrugerea a
Xnave inamice, colectarea aYpuncte sau poate colectarea unui obiect specific.
Implementarea funcționalității de restart a jocului
Jocurile bune încurajează rejucabilitatea prin mecanisme de restart fluide. Când jucătorii finalizează un joc (sau sunt înfrânți), de multe ori vor să încerce din nou imediat - fie pentru a-și depăși scorul, fie pentru a-și îmbunătăți performanța.
Tetris exemplifică perfect acest lucru: când blocurile tale ajung în partea de sus, poți începe instantaneu un nou joc fără a naviga prin meniuri complicate. Vom construi un sistem de restart similar care resetează curat starea jocului și îi readuce pe jucători în acțiune rapid.
✅ Reflecție: Gândește-te la jocurile pe care le-ai jucat. În ce condiții se termină și cum ești îndemnat să le reîncepi? Ce face ca experiența de restart să fie fluidă versus frustrantă?
Ce vei construi
Vei implementa funcțiile finale care transformă proiectul tău într-o experiență completă de joc. Aceste elemente disting jocurile finisate de prototipurile de bază.
Iată ce adăugăm astăzi:
- Condiție de victorie: Distruge toți inamicii și bucură-te de o celebrare pe măsură (merită!)
- Condiție de înfrângere: Rămâi fără vieți și confruntă-te cu un ecran de înfrângere
- Mecanism de restart: Apasă Enter pentru a începe din nou - pentru că un joc nu este niciodată suficient
- Gestionarea stării: Totul de la zero de fiecare dată - fără inamici rămași sau erori ciudate din jocul anterior
Începem
Să pregătim mediul de dezvoltare. Ar trebui să ai toate fișierele jocului spațial din lecțiile anterioare pregătite.
Proiectul tău ar trebui să arate cam așa:
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
Pornește serverul de dezvoltare:
cd your-work
npm start
Această comandă:
- Rulează un server local pe
http://localhost:5000 - Servește fișierele corect
- Se reîmprospătează automat când faci modificări
Deschide http://localhost:5000 în browserul tău și verifică dacă jocul funcționează. Ar trebui să poți să te miști, să tragi și să interacționezi cu inamicii. Odată confirmat, putem continua cu implementarea.
💡 Sfat util: Pentru a evita avertismentele în Visual Studio Code, declară
gameLoopIdîn partea de sus a fișierului calet gameLoopId;în loc să-l declari în funcțiawindow.onload. Acest lucru urmează cele mai bune practici de declarare a variabilelor în JavaScript modern.
Pași de implementare
Pasul 1: Creează funcții de urmărire a condițiilor de finalizare
Avem nevoie de funcții care să monitorizeze când ar trebui să se termine jocul. La fel ca senzorii de pe Stația Spațială Internațională care monitorizează constant sistemele critice, aceste funcții vor verifica continuu starea jocului.
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
Ce se întâmplă în spatele scenei:
- Verifică dacă eroul nostru a rămas fără vieți (au!)
- Numără câți inamici sunt încă activi
- Returnează
truecând câmpul de luptă este liber de inamici - Folosește logică simplă de tip adevărat/fals pentru a păstra lucrurile clare
- Filtrează toate obiectele din joc pentru a găsi supraviețuitorii
Pasul 2: Actualizează gestionarea evenimentelor pentru condițiile de finalizare
Acum vom conecta aceste verificări ale condițiilor la sistemul de evenimente al jocului. De fiecare dată când are loc o coliziune, jocul va evalua dacă aceasta declanșează o condiție de finalizare. Acest lucru creează un feedback imediat pentru evenimentele critice ale jocului.
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
Ce se întâmplă aici:
- Laserul lovește inamicul: Ambele dispar, primești puncte și verificăm dacă ai câștigat
- Inamicul te lovește: Pierzi o viață și verificăm dacă mai ești în joc
- Ordine inteligentă: Verificăm mai întâi înfrângerea (nimeni nu vrea să câștige și să piardă în același timp!)
- Reacții instantanee: De îndată ce se întâmplă ceva important, jocul știe despre asta
Pasul 3: Adaugă noi constante pentru mesaje
Va trebui să adaugi noi tipuri de mesaje în obiectul constant Messages. Aceste constante ajută la menținerea consistenței și prevenirea greșelilor de scriere în sistemul de evenimente.
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
În cele de mai sus, am:
- Adăugat constante pentru evenimentele de finalizare a jocului pentru a menține consistența
- Folosit nume descriptive care indică clar scopul evenimentului
- Urmat convenția de denumire existentă pentru tipurile de mesaje
Pasul 4: Implementarea controalelor de restart
Acum vei adăuga controale de tastatură care permit jucătorilor să reînceapă jocul. Tasta Enter este o alegere naturală, deoarece este asociată frecvent cu confirmarea acțiunilor și începerea unor jocuri noi.
Adaugă detectarea tastei Enter la listener-ul existent pentru evenimentele de apăsare a tastelor:
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
Adaugă noua constantă de mesaj:
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
Ce trebuie să știi:
- Extinde sistemul existent de gestionare a evenimentelor de tastatură
- Folosește tasta Enter ca declanșator pentru restart, pentru o experiență intuitivă
- Emite un eveniment personalizat pe care alte părți ale jocului îl pot asculta
- Menține același model ca celelalte controale de tastatură
Pasul 5: Creează sistemul de afișare a mesajelor
Jocul tău trebuie să comunice clar rezultatele jucătorilor. Vom crea un sistem de mesaje care afișează stările de victorie și înfrângere folosind text colorat, similar interfețelor terminalelor primelor sisteme de calcul, unde verde indica succesul și roșu semnala erorile.
Creează funcția displayMessage():
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
Pas cu pas, iată ce se întâmplă:
- Setează dimensiunea și familia fontului pentru un text clar și ușor de citit
- Aplică un parametru de culoare cu "roșu" ca implicit pentru avertismente
- Centrează textul orizontal și vertical pe canvas
- Folosește parametrii impliciți moderni din JavaScript pentru opțiuni flexibile de culoare
- Utilizează contextul 2D al canvas-ului pentru redarea directă a textului
Creează funcția endGame():
function endGame(win) {
clearInterval(gameLoopId);
// Set a delay to ensure any pending renders complete
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
Ce face această funcție:
- Îngheață totul - fără nave sau lasere în mișcare
- Ia o mică pauză (200ms) pentru a permite ultimului cadru să se finalizeze
- Curăță ecranul și îl colorează în negru pentru un efect dramatic
- Afișează mesaje diferite pentru câștigători și învinși
- Codifică mesajele în culori - verde pentru bine, roșu pentru... ei bine, nu prea bine
- Spune jucătorilor exact cum să reînceapă
Pasul 6: Implementarea funcționalității de resetare a jocului
Sistemul de resetare trebuie să curețe complet starea actuală a jocului și să inițializeze o sesiune nouă. Acest lucru asigură că jucătorii au un început curat, fără date rămase din jocul anterior.
Creează funcția resetGame():
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
Să înțelegem fiecare parte:
- Verifică dacă un loop de joc rulează în prezent înainte de resetare
- Curăță loop-ul existent pentru a opri toate activitățile curente ale jocului
- Elimină toate listener-ele de evenimente pentru a preveni scurgerile de memorie
- Reinițializează starea jocului cu obiecte și variabile noi
- Pornește un nou loop de joc cu toate funcțiile esențiale ale jocului
- Menține același interval de 100ms pentru o performanță constantă a jocului
Adaugă handler-ul pentru tasta Enter în funcția initGame():
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
Adaugă metoda clear() în clasa EventEmitter:
clear() {
this.listeners = {};
}
Puncte cheie de reținut:
- Conectează apăsarea tastei Enter la funcționalitatea de resetare a jocului
- Înregistrează acest listener de evenimente în timpul inițializării jocului
- Oferă o modalitate curată de a elimina toate listener-ele de evenimente între jocuri
- Previne scurgerile de memorie prin curățarea handler-elor de evenimente între jocuri
- Resetează obiectul listener-elor la o stare goală pentru o inițializare proaspătă
Felicitări! 🎉
👽 💥 🚀 Ai reușit să construiești un joc complet de la zero. La fel ca programatorii care au creat primele jocuri video în anii '70, ai transformat linii de cod într-o experiență interactivă cu mecanici de joc și feedback pentru utilizatori. 🚀 💥 👽
Ai realizat:
- Implementarea condițiilor complete de câștig și pierdere cu feedback pentru utilizatori
- Crearea unui sistem de restart fluid pentru o experiență continuă de joc
- Proiectarea unei comunicări vizuale clare pentru stările jocului
- Gestionarea tranzițiilor complexe ale stării jocului și curățarea acestuia
- Asamblarea tuturor componentelor într-un joc coerent și jucabil
Provocarea Agentului GitHub Copilot 🚀
Folosește modul Agent pentru a finaliza următoarea provocare:
Descriere: Îmbunătățește jocul spațial prin implementarea unui sistem de progresie pe niveluri cu dificultate crescută și funcții bonus.
Prompt: Creează un sistem de joc pe mai multe niveluri, în care fiecare nivel are mai multe nave inamice cu viteză și sănătate crescute. Adaugă un multiplicator de punctaj care crește cu fiecare nivel și implementează power-up-uri (cum ar fi foc rapid sau scut) care apar aleatoriu când inamicii sunt distruși. Include un bonus pentru finalizarea nivelului și afișează nivelul curent pe ecran alături de scorul și viețile existente.
Află mai multe despre modul agent aici.
🚀 Provocare opțională de îmbunătățire
Adaugă sunet jocului tău: Îmbunătățește experiența de joc prin implementarea efectelor sonore! Ia în considerare adăugarea de sunet pentru:
- Focurile de laser când jucătorul trage
- Distrugerea inamicilor când navele sunt lovite
- Deteriorarea eroului când jucătorul este lovit
- Muzică de victorie când jocul este câștigat
- Sunet de înfrângere când jocul este pierdut
Exemplu de implementare audio:
// Create audio objects
const laserSound = new Audio('assets/laser.wav');
const explosionSound = new Audio('assets/explosion.wav');
// Play sounds during game events
function playLaserSound() {
laserSound.currentTime = 0; // Reset to beginning
laserSound.play();
}
Ce trebuie să știi:
- Creează obiecte Audio pentru diferite efecte sonore
- Resetează
currentTimepentru a permite efecte sonore rapide - Gestionează politicile de autoplay ale browserului prin declanșarea sunetelor din interacțiuni ale utilizatorului
- Administrează volumul și sincronizarea audio pentru o experiență mai bună de joc
💡 Resursă de învățare: Explorează acest sandbox audio pentru a învăța mai multe despre implementarea audio în jocurile JavaScript.
Test după lecție
Recapitulare & Studiu individual
Tema ta este să creezi un joc nou, așa că explorează câteva dintre jocurile interesante existente pentru a vedea ce tip de joc ai putea construi.
Temă
Declinare de responsabilitate:
Acest document a fost tradus folosind serviciul de traducere AI Co-op Translator. Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de oameni. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.