|
3 weeks ago | |
---|---|---|
.. | ||
README.md | 3 weeks ago | |
assignment.md | 3 weeks ago |
README.md
Gumawa ng Space Game Part 3: Pagdaragdag ng Galaw
Pre-Lecture Quiz
Hindi masyadong masaya ang mga laro kung walang mga alien na gumagalaw sa screen! Sa larong ito, gagamit tayo ng dalawang uri ng galaw:
- Galaw gamit ang Keyboard/Mouse: kapag ang user ay nakikipag-ugnayan gamit ang keyboard o mouse upang ilipat ang isang bagay sa screen.
- Galaw na dulot ng laro: kapag ang laro mismo ang gumagalaw sa isang bagay sa isang tiyak na agwat ng oras.
Paano nga ba natin pinapagalaw ang mga bagay sa screen? Ang lahat ay tungkol sa cartesian coordinates: binabago natin ang lokasyon (x,y) ng bagay at pagkatapos ay nire-redraw ang screen.
Karaniwan, kailangan mo ang mga sumusunod na hakbang upang makamit ang galaw sa screen:
- Itakda ang bagong lokasyon ng isang bagay; ito ay kinakailangan upang makita na ang bagay ay gumalaw.
- I-clear ang screen, kailangang i-clear ang screen sa pagitan ng mga pag-draw. Maaari natin itong i-clear sa pamamagitan ng pag-draw ng isang rectangle na pinupuno ng background color.
- I-redraw ang bagay sa bagong lokasyon. Sa pamamagitan nito, sa wakas ay makakamit natin ang paggalaw ng bagay mula sa isang lokasyon patungo sa iba.
Ganito ang itsura nito sa code:
//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);
✅ Maiisip mo ba ang dahilan kung bakit ang pag-redraw ng iyong hero ng maraming frames kada segundo ay maaaring magdulot ng performance costs? Basahin ang tungkol sa mga alternatibo sa pattern na ito.
Pag-handle ng keyboard events
Hinahandle ang mga events sa pamamagitan ng pag-attach ng mga partikular na events sa code. Ang keyboard events ay na-trigger sa buong window samantalang ang mouse events tulad ng click
ay maaaring i-connect sa pag-click sa isang partikular na elemento. Gagamit tayo ng keyboard events sa buong proyekto na ito.
Upang i-handle ang isang event, kailangan mong gamitin ang addEventListener()
method ng window at magbigay ng dalawang input parameters. Ang unang parameter ay ang pangalan ng event, halimbawa keyup
. Ang pangalawang parameter ay ang function na dapat ma-invoke bilang resulta ng event na naganap.
Narito ang isang halimbawa:
window.addEventListener('keyup', (evt) => {
// `evt.key` = string representation of the key
if (evt.key === 'ArrowUp') {
// do something
}
})
Para sa key events, mayroong dalawang properties sa event na maaari mong gamitin upang makita kung anong key ang na-press:
key
, ito ay string representation ng na-press na key, halimbawaArrowUp
keyCode
, ito ay number representation, halimbawa37
, na tumutugma saArrowLeft
.
✅ Ang key event manipulation ay kapaki-pakinabang hindi lamang sa game development. Ano pang ibang gamit ang maiisip mo para sa teknik na ito?
Mga espesyal na key: isang babala
May ilang espesyal na key na nakakaapekto sa window. Ibig sabihin, kung nakikinig ka sa isang keyup
event at ginamit mo ang mga espesyal na key na ito upang ilipat ang iyong hero, maaari rin itong magdulot ng horizontal scrolling. Dahil dito, maaaring gusto mong i-shut-off ang built-in na browser behavior habang binubuo mo ang iyong laro. Kailangan mo ng code tulad nito:
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);
Ang code sa itaas ay titiyakin na ang arrow-keys at ang space key ay may default behavior na naka-shut off. Ang shut-off mechanism ay nangyayari kapag tinawag natin ang e.preventDefault()
.
Galaw na dulot ng laro
Maaari nating pagalawin ang mga bagay nang mag-isa sa pamamagitan ng paggamit ng timers tulad ng setTimeout()
o setInterval()
function na nag-a-update sa lokasyon ng bagay sa bawat tick, o agwat ng oras. Ganito ang itsura nito:
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
})
Ang game loop
Ang game loop ay isang konsepto na mahalagang isang function na na-invoke sa regular na agwat. Tinatawag itong game loop dahil lahat ng dapat makita ng user ay na-draw sa loop. Ang game loop ay gumagamit ng lahat ng game objects na bahagi ng laro, dinodrawing ang lahat ng mga ito maliban na lang kung sa ilang kadahilanan ay hindi na dapat maging bahagi ng laro. Halimbawa, kung ang isang bagay ay isang kalaban na tinamaan ng laser at sumabog, hindi na ito bahagi ng kasalukuyang game loop (malalaman mo pa ang tungkol dito sa mga susunod na leksyon).
Ganito ang karaniwang itsura ng isang game loop, na ipinapahayag sa code:
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);
Ang loop sa itaas ay na-invoke tuwing 200
milliseconds upang i-redraw ang canvas. May kakayahan kang pumili ng pinakamahusay na interval na may katuturan para sa iyong laro.
Pagpapatuloy ng Space Game
Kukunin mo ang umiiral na code at palalawakin ito. Maaaring magsimula sa code na natapos mo noong Part I o gamitin ang code sa Part II- starter.
- Paggalaw ng hero: magdadagdag ka ng code upang matiyak na maaari mong ilipat ang hero gamit ang arrow keys.
- Paggalaw ng mga kalaban: magdadagdag ka rin ng code upang matiyak na ang mga kalaban ay gumagalaw mula itaas pababa sa isang tiyak na bilis.
Mga Inirerekomendang Hakbang
Hanapin ang mga file na ginawa para sa iyo sa sub-folder na your-work
. Dapat itong maglaman ng sumusunod:
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
Simulan ang iyong proyekto sa folder na your_work
sa pamamagitan ng pag-type:
cd your-work
npm start
Ang nasa itaas ay magsisimula ng HTTP Server sa address na http://localhost:5000
. Buksan ang browser at ilagay ang address na iyon, sa ngayon dapat itong mag-render ng hero at lahat ng mga kalaban; wala pang gumagalaw - sa ngayon!
Magdagdag ng code
-
Magdagdag ng mga dedicated objects para sa
hero
,enemy
, atgame object
, dapat silang magkaroon ngx
aty
properties. (Tandaan ang bahagi tungkol sa Inheritance o composition).HINT Ang
game object
ang dapat magkaroon ngx
aty
at kakayahang i-draw ang sarili nito sa canvas.tip: magsimula sa pamamagitan ng pagdagdag ng bagong GameObject class na may constructor na tulad ng nasa ibaba, at pagkatapos ay i-draw ito sa canvas:
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); } }
Ngayon, i-extend ang GameObject upang lumikha ng Hero at Enemy.
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) } }
-
Magdagdag ng key-event handlers upang i-handle ang key navigation (ilipat ang hero pataas/pababa kaliwa/kanan)
TANDAAN ito ay isang cartesian system, ang top-left ay
0,0
. Tandaan din na magdagdag ng code upang ihinto ang default behavior.tip: gumawa ng iyong onKeyDown function at i-attach ito sa window:
let onKeyDown = function (e) { console.log(e.keyCode); ...add the code from the lesson above to stop default behavior } }; window.addEventListener("keydown", onKeyDown);
Tingnan ang console ng iyong browser sa puntong ito, at panoorin ang mga keystrokes na na-log.
-
Ipatupad ang Pub sub pattern, ito ay magpapanatili ng kalinisan ng iyong code habang sinusundan ang natitirang mga bahagi.
Para gawin ang huling bahagi, maaari mong:
-
Magdagdag ng event listener sa window:
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); } });
-
Gumawa ng EventEmitter class upang mag-publish at mag-subscribe sa mga mensahe:
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)); } } }
-
Magdagdag ng constants at i-set up ang 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();
-
I-initialize ang laro
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; }); }
-
-
I-set up ang game loop
I-refactor ang window.onload function upang i-initialize ang laro at i-set up ang game loop sa tamang interval. Magdadagdag ka rin ng laser beam:
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) };
-
Magdagdag ng code upang pagalawin ang mga kalaban sa isang tiyak na interval
I-refactor ang
createEnemies()
function upang lumikha ng mga kalaban at i-push ang mga ito sa bagong gameObjects class: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); } } }
at magdagdag ng
createHero()
function upang gawin ang katulad na proseso para sa hero.function createHero() { hero = new Hero( canvas.width / 2 - 45, canvas.height - canvas.height / 4 ); hero.img = heroImg; gameObjects.push(hero); }
at sa wakas, magdagdag ng
drawGameObjects()
function upang simulan ang pag-draw:function drawGameObjects(ctx) { gameObjects.forEach(go => go.draw(ctx)); }
Ang iyong mga kalaban ay dapat magsimulang umabante sa iyong hero spaceship!
🚀 Hamon
Tulad ng nakikita mo, ang iyong code ay maaaring maging 'spaghetti code' kapag nagsimula kang magdagdag ng mga function, variable, at klase. Paano mo mas maayos na ma-oorganisa ang iyong code upang mas maging nababasa ito? Gumuhit ng sistema upang ma-organisa ang iyong code, kahit na ito ay nananatili sa isang file.
Post-Lecture Quiz
Review & Self Study
Habang sinusulat natin ang ating laro nang walang paggamit ng frameworks, maraming JavaScript-based canvas frameworks para sa game development. Maglaan ng oras upang magbasa tungkol sa mga ito.
Assignment
Paunawa:
Ang dokumentong ito ay isinalin gamit ang AI translation service na Co-op Translator. Bagama't sinisikap naming maging tumpak, pakitandaan na ang mga awtomatikong pagsasalin ay maaaring maglaman ng mga pagkakamali o hindi pagkakatugma. Ang orihinal na dokumento sa kanyang katutubong wika ang dapat ituring na opisyal na sanggunian. Para sa mahalagang impormasyon, inirerekomenda ang propesyonal na pagsasalin ng tao. Hindi kami mananagot sa anumang hindi pagkakaunawaan o maling interpretasyon na maaaring magmula sa paggamit ng pagsasaling ito.