|
|
2 months ago | |
|---|---|---|
| .. | ||
| README.md | 2 months ago | |
| assignment.md | 2 months ago | |
README.md
Kosmosemängu loomine, 3. osa: Liikumise lisamine
Eelloengu viktoriin
Mängud ei ole eriti lõbusad, kuni ekraanil hakkavad tulnukad ringi liikuma! Selles mängus kasutame kahte tüüpi liikumist:
- Klaviatuuri/hiire liikumine: kui kasutaja suhtleb klaviatuuri või hiirega, et liigutada objekti ekraanil.
- Mängu poolt tekitatud liikumine: kui mäng liigutab objekti kindla ajavahemiku järel.
Kuidas siis asju ekraanil liigutada? Kõik põhineb ristkoordinaatidel: me muudame objekti asukohta (x, y) ja joonistame ekraani uuesti.
Tavaliselt on liikumise saavutamiseks ekraanil vaja järgmisi samme:
- Määra objektile uus asukoht; see on vajalik, et tajuda objekti liikumist.
- Puhasta ekraan, ekraan tuleb joonistuste vahel puhastada. Seda saab teha, joonistades ristküliku, mille täidame taustavärviga.
- Joonista objekt uuesti uues asukohas. Sellega saavutame lõpuks objekti liikumise ühest asukohast teise.
Näiteks võib see koodis välja näha järgmiselt:
//set the hero's location
hero.x += 5;
// clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
✅ Kas oskad mõelda põhjusele, miks kangelase korduv joonistamine mitmel kaadril sekundis võib tekitada jõudluskulusid? Loe alternatiive sellele mustrile.
Klaviatuuri sündmuste käsitlemine
Sündmusi käsitletakse, sidudes konkreetseid sündmusi koodiga. Klaviatuuri sündmused käivitatakse kogu aknas, samas kui hiire sündmused, nagu click, saab siduda konkreetse elemendi klõpsamisega. Kasutame kogu projekti vältel klaviatuuri sündmusi.
Sündmuse käsitlemiseks tuleb kasutada akna addEventListener() meetodit ja anda sellele kaks sisendparameetrit. Esimene parameeter on sündmuse nimi, näiteks keyup. Teine parameeter on funktsioon, mida tuleks sündmuse toimumise tulemusena käivitada.
Näide:
window.addEventListener('keyup', (evt) => {
// `evt.key` = string representation of the key
if (evt.key === 'ArrowUp') {
// do something
}
})
Klaviatuuri sündmuste puhul on sündmusel kaks omadust, mida saab kasutada, et näha, millist klahvi vajutati:
key, see on vajutatud klahvi stringiline esitus, näiteksArrowUpkeyCode, see on numbriline esitus, näiteks37, mis vastabArrowLeft.
✅ Klaviatuuri sündmuste manipuleerimine on kasulik ka väljaspool mänguarendust. Milliseid muid kasutusviise oskad sellele tehnikale mõelda?
Erilised klahvid: hoiatus
On mõned erilised klahvid, mis mõjutavad akent. See tähendab, et kui kuulad keyup sündmust ja kasutad neid erilisi klahve kangelase liigutamiseks, toimub ka horisontaalne kerimine. Seetõttu võib olla vajalik väljalülitada see sisseehitatud brauseri käitumine, kui oma mängu arendad. Selleks on vaja sellist koodi:
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Arrow keys
case 32:
e.preventDefault();
break; // Space
default:
break; // do not block other keys
}
};
window.addEventListener('keydown', onKeyDown);
Ülaltoodud kood tagab, et nooleklahvid ja tühikuklahv ei täida oma vaikimisi käitumist. Väljalülitamise mehhanism toimub, kui kutsume e.preventDefault().
Mängu poolt tekitatud liikumine
Saame asju ise liikuma panna, kasutades taimerit, näiteks setTimeout() või setInterval() funktsiooni, mis uuendab objekti asukohta iga ajavahemiku järel. Näiteks võib see välja näha järgmiselt:
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
})
Mängu tsükkel
Mängu tsükkel on kontseptsioon, mis sisuliselt tähendab funktsiooni, mida kutsutakse regulaarselt. Seda nimetatakse mängu tsükliks, kuna kõik, mis peaks olema kasutajale nähtav, joonistatakse tsüklisse. Mängu tsükkel kasutab kõiki mängu objekte, mis on mängu osa, joonistades need kõik, välja arvatud juhul, kui mingil põhjusel ei peaks need enam mängu osa olema. Näiteks kui objekt on vaenlane, keda laseriga tabati ja kes plahvatab, ei ole see enam praeguse mängu tsükli osa (õpid sellest rohkem järgnevates tundides).
Näiteks võib mängu tsükkel koodis välja näha järgmiselt:
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
Ülaltoodud tsüklit kutsutakse iga 200 millisekundi järel, et lõuend uuesti joonistada. Sul on võimalus valida parim intervall, mis sobib sinu mängule.
Kosmosemängu jätkamine
Võta olemasolev kood ja laienda seda. Alusta kas koodiga, mille lõpetasid esimeses osas, või kasuta II osa algkoodi.
- Kangelase liigutamine: lisad koodi, et saaksid kangelast nooleklahvide abil liigutada.
- Vaenlaste liikumine: lisad koodi, et vaenlased liiguksid ülalt alla kindla kiirusega.
Soovitatud sammud
Leia failid, mis on sinu jaoks loodud kaustas your-work. See peaks sisaldama järgmist:
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
Alusta oma projekti kaustas your_work, sisestades:
cd your-work
npm start
Ülaltoodud käivitab HTTP serveri aadressil http://localhost:5000. Ava brauser ja sisesta see aadress, praegu peaks see kuvama kangelase ja kõik vaenlased; miski ei liigu - veel!
Lisa kood
-
Lisa spetsiaalsed objektid
kangelase,vaenlasejamängu objektijaoks, neil peaks olemaxjayomadused. (Tuleta meelde osa Pärand või kompositsioon).VIHJE
mängu objektpeaks olema see, millel onxjayning võime end lõuendile joonistada.vihje: alusta uue GameObject klassi lisamisega, mille konstruktor on määratletud allpool, ja joonista see lõuendile:
class GameObject { constructor(x, y) { this.x = x; this.y = y; this.dead = false; this.type = ""; this.width = 0; this.height = 0; this.img = undefined; } draw(ctx) { ctx.drawImage(this.img, this.x, this.y, this.width, this.height); } }Nüüd laienda seda GameObjecti, et luua kangelane ja vaenlane.
class Hero extends GameObject { constructor(x, y) { ...it needs an x, y, type, and speed } }class Enemy extends GameObject { constructor(x, y) { super(x, y); (this.width = 98), (this.height = 50); this.type = "Enemy"; let id = setInterval(() => { if (this.y < canvas.height - this.height) { this.y += 5; } else { console.log('Stopped at', this.y) clearInterval(id); } }, 300) } } -
Lisa klaviatuuri sündmuste käsitlejad, et käsitleda kangelase liikumist (liiguta kangelast üles/alla vasakule/paremale).
MEENUTA see on ristkoordinaatide süsteem, vasak ülemine nurk on
0,0. Samuti ära unusta lisada koodi, et peatada vaikimisi käitumine.vihje: loo oma onKeyDown funktsioon ja kinnita see aknale:
let onKeyDown = function (e) { console.log(e.keyCode); ...add the code from the lesson above to stop default behavior } }; window.addEventListener("keydown", onKeyDown);Kontrolli oma brauseri konsooli selles punktis ja vaata, kuidas klahvivajutused logitakse.
-
Rakenda Pub sub muster, see hoiab su koodi puhtana, kui järgmistest osadest edasi liigud.
Selle viimase osa tegemiseks saad:
-
Lisa sündmuste kuulaja aknale:
window.addEventListener("keyup", (evt) => { if (evt.key === "ArrowUp") { eventEmitter.emit(Messages.KEY_EVENT_UP); } else if (evt.key === "ArrowDown") { eventEmitter.emit(Messages.KEY_EVENT_DOWN); } else if (evt.key === "ArrowLeft") { eventEmitter.emit(Messages.KEY_EVENT_LEFT); } else if (evt.key === "ArrowRight") { eventEmitter.emit(Messages.KEY_EVENT_RIGHT); } }); -
Loo EventEmitter klass, et sõnumeid avaldada ja tellida:
class EventEmitter { constructor() { this.listeners = {}; } on(message, listener) { if (!this.listeners[message]) { this.listeners[message] = []; } this.listeners[message].push(listener); } emit(message, payload = null) { if (this.listeners[message]) { this.listeners[message].forEach((l) => l(message, payload)); } } } -
Lisa konstandid ja seadista EventEmitter:
const Messages = { KEY_EVENT_UP: "KEY_EVENT_UP", KEY_EVENT_DOWN: "KEY_EVENT_DOWN", KEY_EVENT_LEFT: "KEY_EVENT_LEFT", KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT", }; let heroImg, enemyImg, laserImg, canvas, ctx, gameObjects = [], hero, eventEmitter = new EventEmitter(); -
Initsialiseeri mäng
function initGame() { gameObjects = []; createEnemies(); createHero(); eventEmitter.on(Messages.KEY_EVENT_UP, () => { hero.y -=5 ; }) eventEmitter.on(Messages.KEY_EVENT_DOWN, () => { hero.y += 5; }); eventEmitter.on(Messages.KEY_EVENT_LEFT, () => { hero.x -= 5; }); eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => { hero.x += 5; }); } -
-
Seadista mängu tsükkel
Refaktoreeri window.onload funktsioon, et initsialiseerida mäng ja seadistada mängu tsükkel sobiva intervalliga. Lisad ka laserkiire:
window.onload = async () => { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); heroImg = await loadTexture("assets/player.png"); enemyImg = await loadTexture("assets/enemyShip.png"); laserImg = await loadTexture("assets/laserRed.png"); initGame(); let gameLoopId = setInterval(() => { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); drawGameObjects(ctx); }, 100) }; -
Lisa kood, et vaenlased liiguksid kindla intervalliga
Refaktoreeri
createEnemies()funktsioon, et luua vaenlased ja lisada need uude gameObjects klassi:function createEnemies() { const MONSTER_TOTAL = 5; const MONSTER_WIDTH = MONSTER_TOTAL * 98; const START_X = (canvas.width - MONSTER_WIDTH) / 2; const STOP_X = START_X + MONSTER_WIDTH; for (let x = START_X; x < STOP_X; x += 98) { for (let y = 0; y < 50 * 5; y += 50) { const enemy = new Enemy(x, y); enemy.img = enemyImg; gameObjects.push(enemy); } } }ja lisa
createHero()funktsioon, et teha sarnane protsess kangelase jaoks.function createHero() { hero = new Hero( canvas.width / 2 - 45, canvas.height - canvas.height / 4 ); hero.img = heroImg; gameObjects.push(hero); }ja lõpuks lisa
drawGameObjects()funktsioon, et alustada joonistamist:function drawGameObjects(ctx) { gameObjects.forEach(go => go.draw(ctx)); }Sinu vaenlased peaksid hakkama sinu kangelase kosmoselaeva poole liikuma!
🚀 Väljakutse
Nagu näha, võib su kood muutuda "spagetikoodiks", kui hakkad lisama funktsioone, muutujaid ja klasse. Kuidas saaksid oma koodi paremini organiseerida, et see oleks loetavam? Joonista süsteem, kuidas oma koodi organiseerida, isegi kui see jääb ühte faili.
Järelloengu viktoriin
Ülevaade ja iseseisev õppimine
Kuigi kirjutame oma mängu ilma raamistikke kasutamata, on palju JavaScripti-põhiseid lõuendi raamistikke mänguarenduseks. Võta aega, et lugeda nende kohta.
Ülesanne
Lahtiütlus:
See dokument on tõlgitud AI tõlketeenuse Co-op Translator abil. Kuigi püüame tagada täpsust, palume arvestada, et automaatsed tõlked võivad sisaldada vigu või ebatäpsusi. Algne dokument selle algses keeles tuleks pidada autoriteetseks allikaks. Olulise teabe puhul soovitame kasutada professionaalset inimtõlget. Me ei vastuta selle tõlke kasutamisest tulenevate arusaamatuste või valesti tõlgenduste eest.