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.
Web-Dev-For-Beginners/translations/sl/6-space-game/3-moving-elements-around/README.md

402 lines
14 KiB

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a9a161871de7706cb0e23b1bd0c74559",
"translation_date": "2025-08-29T12:49:55+00:00",
"source_file": "6-space-game/3-moving-elements-around/README.md",
"language_code": "sl"
}
-->
# Ustvarjanje vesoljske igre, 3. del: Dodajanje gibanja
## Pred-predavanje kviz
[Pred-predavanje kviz](https://ff-quizzes.netlify.app/web/quiz/33)
Igre niso prav zabavne, dokler se po zaslonu ne začnejo premikati vesoljci! V tej igri bomo uporabili dve vrsti gibanja:
- **Gibanje s tipkovnico/miško**: ko uporabnik z interakcijo s tipkovnico ali miško premakne objekt na zaslonu.
- **Gibanje, ki ga sproži igra**: ko igra premakne objekt v določenih časovnih intervalih.
Kako torej premikamo stvari na zaslonu? Vse temelji na kartezičnih koordinatah: spremenimo lokacijo (x, y) objekta in nato ponovno narišemo zaslon.
Običajno potrebujete naslednje korake, da dosežete *gibanje* na zaslonu:
1. **Nastavite novo lokacijo** za objekt; to je potrebno, da se zdi, da se je objekt premaknil.
2. **Počistite zaslon**, zaslon je treba počistiti med posameznimi risanji. To lahko storimo tako, da narišemo pravokotnik, ki ga zapolnimo z barvo ozadja.
3. **Ponovno narišite objekt** na novi lokaciji. S tem končno dosežemo premik objekta z ene lokacije na drugo.
Tako lahko to izgleda v kodi:
```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);
```
✅ Ali lahko pomislite na razlog, zakaj bi lahko risanje vašega junaka večkrat na sekundo povzročilo težave z zmogljivostjo? Preberite več o [alternativah temu vzorcu](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas).
## Obdelava dogodkov tipkovnice
Dogodke obdelujete tako, da določene dogodke povežete s kodo. Dogodki tipkovnice se sprožijo na celotnem oknu, medtem ko so dogodki miške, kot je `click`, lahko povezani s klikom na določen element. V tem projektu bomo uporabljali dogodke tipkovnice.
Za obdelavo dogodka morate uporabiti metodo `addEventListener()` okna in ji podati dva vhodna parametra. Prvi parameter je ime dogodka, na primer `keyup`. Drugi parameter je funkcija, ki naj se izvede, ko se dogodek zgodi.
Tukaj je primer:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = string representation of the key
if (evt.key === 'ArrowUp') {
// do something
}
})
```
Za dogodke tipk obstajata dve lastnosti dogodka, ki ju lahko uporabite za ugotavljanje, katera tipka je bila pritisnjena:
- `key`, to je nizovna predstavitev pritisnjene tipke, na primer `ArrowUp`.
- `keyCode`, to je številčna predstavitev, na primer `37`, kar ustreza `ArrowLeft`.
✅ Manipulacija dogodkov tipk je uporabna tudi zunaj razvoja iger. Za katere druge namene bi lahko uporabili to tehniko?
### Posebne tipke: opozorilo
Obstajajo nekatere *posebne* tipke, ki vplivajo na okno. To pomeni, da če poslušate dogodek `keyup` in uporabite te posebne tipke za premikanje junaka, bo to povzročilo tudi horizontalno pomikanje. Zaradi tega boste morda želeli *izklopiti* to privzeto vedenje brskalnika, ko gradite svojo igro. Potrebujete kodo, kot je ta:
```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);
```
Zgornja koda bo zagotovila, da bodo puščične tipke in preslednica imele svoje *privzeto* vedenje izklopljeno. Mehanizem *izklopa* se zgodi, ko pokličemo `e.preventDefault()`.
## Gibanje, ki ga sproži igra
Stvari lahko premikamo same od sebe z uporabo časovnikov, kot sta funkciji `setTimeout()` ali `setInterval()`, ki posodabljata lokacijo objekta ob vsakem intervalu. Tako lahko to izgleda:
```javascript
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
})
```
## Igralna zanka
Igralna zanka je koncept, ki je v bistvu funkcija, ki se izvaja v rednih intervalih. Imenuje se igralna zanka, ker se v njej nariše vse, kar naj bo vidno uporabniku. Igralna zanka uporablja vse igralne objekte, ki so del igre, in jih nariše, razen če iz kakšnega razloga ne bi smeli biti več del igre. Na primer, če je objekt sovražnik, ki ga je zadela laserska žarka in eksplodira, ni več del trenutne igralne zanke (o tem boste izvedeli več v naslednjih lekcijah).
Tako lahko igralna zanka običajno izgleda, izražena v kodi:
```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);
```
Zgornja zanka se izvaja vsakih `200` milisekund za ponovno risanje platna. Sami lahko izberete interval, ki najbolj ustreza vaši igri.
## Nadaljevanje vesoljske igre
Obstoječo kodo boste razširili. Lahko začnete s kodo, ki ste jo dokončali v prvem delu, ali uporabite kodo iz [2. dela - začetna koda](../../../../6-space-game/3-moving-elements-around/your-work).
- **Premikanje junaka**: dodali boste kodo, ki omogoča premikanje junaka s puščičnimi tipkami.
- **Premikanje sovražnikov**: dodali boste tudi kodo, ki omogoča premikanje sovražnikov od zgoraj navzdol z določeno hitrostjo.
## Priporočeni koraki
Poiščite datoteke, ki so bile ustvarjene za vas v podmapi `your-work`. Vsebujejo naj naslednje:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Svoj projekt začnete v mapi `your_work` z vnosom:
```bash
cd your-work
npm start
```
Zgornji ukaz bo zagnal HTTP strežnik na naslovu `http://localhost:5000`. Odprite brskalnik in vnesite ta naslov. Trenutno bi morali videti junaka in vse sovražnike; nič se še ne premika!
### Dodajte kodo
1. **Dodajte namenski objekt** za `hero`, `enemy` in `game object`, ki naj imajo lastnosti `x` in `y`. (Spomnite se dela o [Dedovanju ali kompoziciji](../README.md)).
*NAMIG*: `game object` naj bo tisti z lastnostma `x` in `y` ter zmožnostjo, da se nariše na platno.
>nasvet: začnite z dodajanjem novega razreda GameObject s konstruktorjem, kot je spodaj, in ga nato narišite na platno:
```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);
}
}
```
Zdaj razširite ta GameObject, da ustvarite Hero in 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. **Dodajte obdelovalce dogodkov tipk**, da omogočite navigacijo s tipkami (premikanje junaka gor/dol, levo/desno).
*SPOMNITE SE*: gre za kartezični sistem, zgoraj levo je `0,0`. Prav tako ne pozabite dodati kode za zaustavitev *privzetega vedenja*.
>nasvet: ustvarite svojo funkcijo onKeyDown in jo povežite z oknom:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
Na tej točki preverite konzolo brskalnika in opazujte, kako se beležijo pritiski tipk.
3. **Implementirajte** [Vzorec Pub-Sub](../README.md), da bo vaša koda ostala čista, ko boste sledili preostalim delom.
Za izvedbo tega zadnjega dela lahko:
1. **Dodate poslušalca dogodkov** na okno:
```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. **Ustvarite razred EventEmitter**, da objavljate in naročate sporočila:
```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. **Dodate konstante** in nastavite 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. **Inicializirate igro**
```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. **Nastavite igralno zanko**
Preoblikujte funkcijo window.onload, da inicializira igro in nastavi igralno zanko na ustrezen interval. Dodali boste tudi laserski žarek:
```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. **Dodajte kodo** za premikanje sovražnikov v določenih intervalih
Preoblikujte funkcijo `createEnemies()`, da ustvari sovražnike in jih doda v nov razred 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);
}
}
}
```
in dodajte funkcijo `createHero()`, da naredite podoben postopek za junaka.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
in na koncu dodajte funkcijo `drawGameObjects()`, da začnete risanje:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
Vaši sovražniki bi morali začeti napadati vaš vesoljski ladijski junak!
---
## 🚀 Izziv
Kot lahko vidite, se vaša koda lahko spremeni v pagetno kodo', ko začnete dodajati funkcije, spremenljivke in razrede. Kako lahko bolje organizirate svojo kodo, da bo bolj berljiva? Narišite sistem za organizacijo svoje kode, tudi če še vedno ostane v eni datoteki.
## Po-predavanje kviz
[Po-predavanje kviz](https://ff-quizzes.netlify.app/web/quiz/34)
## Pregled in samostojno učenje
Čeprav svojo igro pišemo brez uporabe ogrodij, obstaja veliko ogrodij za razvoj iger na osnovi JavaScript platna. Vzemite si čas za [branje o teh](https://github.com/collections/javascript-game-engines).
## Naloga
[Komentirajte svojo kodo](assignment.md)
---
**Omejitev odgovornosti**:
Ta dokument je bil preveden z uporabo storitve za prevajanje z umetno inteligenco [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem maternem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.