You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
402 lines
14 KiB
402 lines
14 KiB
<!--
|
|
CO_OP_TRANSLATOR_METADATA:
|
|
{
|
|
"original_hash": "23f088add24f0f1fa51014a9e27ea280",
|
|
"translation_date": "2025-08-28T03:52:30+00:00",
|
|
"source_file": "6-space-game/3-moving-elements-around/README.md",
|
|
"language_code": "sw"
|
|
}
|
|
-->
|
|
# Jenga Mchezo wa Anga Sehemu ya 3: Kuongeza Mwendo
|
|
|
|
## Jaribio la Kabla ya Somo
|
|
|
|
[Jaribio la kabla ya somo](https://ff-quizzes.netlify.app/web/quiz/33)
|
|
|
|
Michezo haiwi ya kufurahisha sana hadi pale unapokuwa na viumbe wa kigeni wakizunguka kwenye skrini! Katika mchezo huu, tutatumia aina mbili za mwendo:
|
|
|
|
- **Mwendo wa Kibodi/Panya**: pale mtumiaji anaposhirikiana na kibodi au panya ili kusogeza kitu kwenye skrini.
|
|
- **Mwendo unaosababishwa na mchezo**: pale mchezo unapohamisha kitu kwa muda maalum.
|
|
|
|
Kwa hiyo, tunahamishaje vitu kwenye skrini? Yote inahusu kuratibu kwa kutumia mfumo wa Cartesian: tunabadilisha eneo (x, y) la kitu na kisha kuchora tena skrini.
|
|
|
|
Kwa kawaida, unahitaji hatua zifuatazo ili kufanikisha *mwendo* kwenye skrini:
|
|
|
|
1. **Weka eneo jipya** la kitu; hii inahitajika ili kuonekana kama kitu kimesogea.
|
|
2. **Futa skrini**, skrini inahitaji kufutwa kati ya michoro. Tunaweza kuifuta kwa kuchora mstatili na kuujaza kwa rangi ya mandharinyuma.
|
|
3. **Chora tena kitu** kwenye eneo jipya. Kwa kufanya hivi, tunafanikisha kusogeza kitu kutoka eneo moja hadi jingine.
|
|
|
|
Hivi ndivyo inavyoweza kuonekana kwenye msimbo:
|
|
|
|
```javascript
|
|
//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);
|
|
```
|
|
|
|
✅ Je, unaweza kufikiria sababu kwa nini kuchora tena shujaa wako mara nyingi kwa sekunde kunaweza kusababisha gharama za utendaji? Soma kuhusu [mbadala wa muundo huu](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas).
|
|
|
|
## Kushughulikia Matukio ya Kibodi
|
|
|
|
Unashughulikia matukio kwa kuambatanisha matukio maalum na msimbo. Matukio ya kibodi huchochewa kwenye dirisha lote wakati matukio ya panya kama `click` yanaweza kuunganishwa na kubofya kipengele maalum. Tutatumia matukio ya kibodi katika mradi huu wote.
|
|
|
|
Ili kushughulikia tukio, unahitaji kutumia njia ya `addEventListener()` ya dirisha na kuipatia vigezo viwili vya pembejeo. Kigezo cha kwanza ni jina la tukio, kwa mfano `keyup`. Kigezo cha pili ni kazi ambayo inapaswa kuitwa kama matokeo ya tukio kutokea.
|
|
|
|
Hapa kuna mfano:
|
|
|
|
```javascript
|
|
window.addEventListener('keyup', (evt) => {
|
|
// `evt.key` = string representation of the key
|
|
if (evt.key === 'ArrowUp') {
|
|
// do something
|
|
}
|
|
})
|
|
```
|
|
|
|
Kwa matukio ya funguo, kuna mali mbili kwenye tukio unazoweza kutumia kuona ni funguo gani ilibonyezwa:
|
|
|
|
- `key`, hii ni uwakilishi wa kamba wa funguo iliyobonyezwa, kwa mfano `ArrowUp`
|
|
- `keyCode`, hii ni uwakilishi wa nambari, kwa mfano `37`, inahusiana na `ArrowLeft`.
|
|
|
|
✅ Udhibiti wa matukio ya funguo ni muhimu hata nje ya maendeleo ya michezo. Je, unaweza kufikiria matumizi mengine ya mbinu hii?
|
|
|
|
### Funguo Maalum: Tahadhari
|
|
|
|
Kuna funguo zingine *maalum* ambazo huathiri dirisha. Hii inamaanisha kuwa ikiwa unasikiliza tukio la `keyup` na unatumia funguo hizi maalum kusogeza shujaa wako, pia itasababisha kurasa kusogea kwa usawa. Kwa sababu hiyo, unaweza kutaka *kuzima* tabia hii ya kawaida ya kivinjari unapojenga mchezo wako. Unahitaji msimbo kama huu:
|
|
|
|
```javascript
|
|
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);
|
|
```
|
|
|
|
Msimbo hapo juu utahakikisha kuwa funguo za mishale na funguo ya nafasi zimezimwa tabia yao ya *kawaida*. Utaratibu wa *kuzima* hutokea tunapopiga `e.preventDefault()`.
|
|
|
|
## Mwendo Unaosababishwa na Mchezo
|
|
|
|
Tunaweza kufanya vitu visogee vyenyewe kwa kutumia vipima muda kama vile kazi ya `setTimeout()` au `setInterval()` ambayo inasasisha eneo la kitu kwa kila muda maalum. Hivi ndivyo inavyoweza kuonekana:
|
|
|
|
```javascript
|
|
let id = setInterval(() => {
|
|
//move the enemy on the y axis
|
|
enemy.y += 10;
|
|
})
|
|
```
|
|
|
|
## Mzunguko wa Mchezo
|
|
|
|
Mzunguko wa mchezo ni dhana ambayo kimsingi ni kazi inayochochewa kwa vipindi vya kawaida. Unaitwa mzunguko wa mchezo kwa sababu kila kitu kinachopaswa kuonekana kwa mtumiaji kinachorwa ndani ya mzunguko. Mzunguko wa mchezo hutumia vitu vyote vya mchezo ambavyo ni sehemu ya mchezo, kuchora vyote isipokuwa kwa sababu fulani havipaswi kuwa sehemu ya mchezo tena. Kwa mfano, ikiwa kitu ni adui aliyepigwa na miale ya laser na kulipuka, hakipo tena katika mzunguko wa sasa wa mchezo (utajifunza zaidi kuhusu hili katika masomo yanayofuata).
|
|
|
|
Hivi ndivyo mzunguko wa mchezo unavyoweza kuonekana, ukiwakilishwa kwa msimbo:
|
|
|
|
```javascript
|
|
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);
|
|
```
|
|
|
|
Mzunguko hapo juu unachochewa kila baada ya milisekunde `200` ili kuchora tena canvas. Una uwezo wa kuchagua muda bora unaofaa kwa mchezo wako.
|
|
|
|
## Kuendelea na Mchezo wa Anga
|
|
|
|
Utachukua msimbo uliopo na kuupanua. Ama anza na msimbo uliokamilisha wakati wa sehemu ya I au tumia msimbo katika [Sehemu ya II - mwanzo](../../../../6-space-game/3-moving-elements-around/your-work).
|
|
|
|
- **Kusogeza shujaa**: utaongeza msimbo kuhakikisha unaweza kusogeza shujaa kwa kutumia funguo za mishale.
|
|
- **Kusogeza maadui**: pia utahitaji kuongeza msimbo kuhakikisha maadui wanasogea kutoka juu kwenda chini kwa kasi fulani.
|
|
|
|
## Hatua Zinazopendekezwa
|
|
|
|
Tafuta faili ambazo zimeundwa kwa ajili yako kwenye folda ndogo ya `your-work`. Inapaswa kuwa na yafuatayo:
|
|
|
|
```bash
|
|
-| assets
|
|
-| enemyShip.png
|
|
-| player.png
|
|
-| index.html
|
|
-| app.js
|
|
-| package.json
|
|
```
|
|
|
|
Anzisha mradi wako kwenye folda ya `your_work` kwa kuandika:
|
|
|
|
```bash
|
|
cd your-work
|
|
npm start
|
|
```
|
|
|
|
Hii itaanzisha Seva ya HTTP kwenye anwani `http://localhost:5000`. Fungua kivinjari na uweke anwani hiyo, kwa sasa inapaswa kuonyesha shujaa na maadui wote; hakuna kinachosogea - bado!
|
|
|
|
### Ongeza Msimbo
|
|
|
|
1. **Ongeza vitu maalum** kwa `hero`, `enemy`, na `game object`, vinapaswa kuwa na mali za `x` na `y`. (Kumbuka sehemu kuhusu [Urithi au muundo](../README.md)).
|
|
|
|
*KIDOKEZO* `game object` inapaswa kuwa na `x` na `y` na uwezo wa kujichora kwenye canvas.
|
|
|
|
>kidokezo: anza kwa kuongeza darasa jipya la GameObject na mjengo wake kama ifuatavyo, kisha ichore kwenye canvas:
|
|
|
|
```javascript
|
|
|
|
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);
|
|
}
|
|
}
|
|
```
|
|
|
|
Sasa, panua GameObject ili kuunda Hero na Enemy.
|
|
|
|
```javascript
|
|
class Hero extends GameObject {
|
|
constructor(x, y) {
|
|
...it needs an x, y, type, and speed
|
|
}
|
|
}
|
|
```
|
|
|
|
```javascript
|
|
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)
|
|
}
|
|
}
|
|
```
|
|
|
|
2. **Ongeza vishughulikia matukio ya funguo** kushughulikia urambazaji wa funguo (sogeza shujaa juu/chini kushoto/kulia).
|
|
|
|
*KUMBUKA* ni mfumo wa Cartesian, kona ya juu-kushoto ni `0,0`. Pia kumbuka kuongeza msimbo wa kusimamisha *tabia ya kawaida*.
|
|
|
|
>kidokezo: unda kazi yako ya onKeyDown na uambatanishe kwenye dirisha:
|
|
|
|
```javascript
|
|
let onKeyDown = function (e) {
|
|
console.log(e.keyCode);
|
|
...add the code from the lesson above to stop default behavior
|
|
}
|
|
};
|
|
|
|
window.addEventListener("keydown", onKeyDown);
|
|
```
|
|
|
|
Angalia koni ya kivinjari chako kwa wakati huu, na uangalie funguo zinazobonyezwa zikiripotiwa.
|
|
|
|
3. **Tekeleza** [Mfumo wa Pub sub](../README.md), hii itahakikisha msimbo wako unasalia safi unapofuata sehemu zinazobaki.
|
|
|
|
Ili kufanya sehemu hii ya mwisho, unaweza:
|
|
|
|
1. **Ongeza msikilizaji wa tukio** kwenye dirisha:
|
|
|
|
```javascript
|
|
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);
|
|
}
|
|
});
|
|
```
|
|
|
|
1. **Unda darasa la EventEmitter** kuchapisha na kujiandikisha kwa ujumbe:
|
|
|
|
```javascript
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
1. **Ongeza vigezo vya kudumu** na usanidi EventEmitter:
|
|
|
|
```javascript
|
|
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();
|
|
```
|
|
|
|
1. **Anzisha mchezo**
|
|
|
|
```javascript
|
|
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;
|
|
});
|
|
}
|
|
```
|
|
|
|
1. **Sanidi mzunguko wa mchezo**
|
|
|
|
Rekebisha kazi ya window.onload ili kuanzisha mchezo na kusanidi mzunguko wa mchezo kwa muda mzuri. Pia utaongeza miale ya laser:
|
|
|
|
```javascript
|
|
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)
|
|
|
|
};
|
|
```
|
|
|
|
5. **Ongeza msimbo** wa kusogeza maadui kwa muda fulani
|
|
|
|
Rekebisha kazi ya `createEnemies()` ili kuunda maadui na kuwasukuma kwenye darasa jipya la gameObjects:
|
|
|
|
```javascript
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
na ongeza kazi ya `createHero()` kufanya mchakato sawa kwa shujaa.
|
|
|
|
```javascript
|
|
function createHero() {
|
|
hero = new Hero(
|
|
canvas.width / 2 - 45,
|
|
canvas.height - canvas.height / 4
|
|
);
|
|
hero.img = heroImg;
|
|
gameObjects.push(hero);
|
|
}
|
|
```
|
|
|
|
na hatimaye, ongeza kazi ya `drawGameObjects()` ili kuanza kuchora:
|
|
|
|
```javascript
|
|
function drawGameObjects(ctx) {
|
|
gameObjects.forEach(go => go.draw(ctx));
|
|
}
|
|
```
|
|
|
|
Maadui wako wanapaswa kuanza kusonga kuelekea kwenye chombo cha anga cha shujaa wako!
|
|
|
|
---
|
|
|
|
## 🚀 Changamoto
|
|
|
|
Kama unavyoona, msimbo wako unaweza kuwa 'msimbo wa tambi' unapoongeza kazi, vigezo, na madarasa. Unawezaje kupanga msimbo wako vizuri zaidi ili uwe rahisi kusoma? Chora mfumo wa kupanga msimbo wako, hata kama bado unakaa kwenye faili moja.
|
|
|
|
## Jaribio la Baada ya Somo
|
|
|
|
[Jaribio la baada ya somo](https://ff-quizzes.netlify.app/web/quiz/34)
|
|
|
|
## Mapitio na Kujisomea
|
|
|
|
Ingawa tunaandika mchezo wetu bila kutumia mifumo, kuna mifumo mingi ya JavaScript inayotegemea canvas kwa ajili ya maendeleo ya michezo. Chukua muda kufanya [usomaji kuhusu hizi](https://github.com/collections/javascript-game-engines).
|
|
|
|
## Kazi
|
|
|
|
[Toa maoni kwenye msimbo wako](assignment.md)
|
|
|
|
---
|
|
|
|
**Kanusho**:
|
|
Hati hii imetafsiriwa kwa kutumia huduma ya kutafsiri ya AI [Co-op Translator](https://github.com/Azure/co-op-translator). Ingawa tunajitahidi kuhakikisha usahihi, tafadhali fahamu kuwa tafsiri za kiotomatiki zinaweza kuwa na makosa au kutokuwa sahihi. Hati ya asili katika lugha yake ya awali inapaswa kuzingatiwa kama chanzo cha mamlaka. Kwa taarifa muhimu, tafsiri ya kitaalamu ya binadamu inapendekezwa. Hatutawajibika kwa kutoelewana au tafsiri zisizo sahihi zinazotokana na matumizi ya tafsiri hii. |