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/hu/6-space-game/3-moving-elements-around
leestott c0ca49b2cc
🌐 Update translations via Co-op Translator
3 weeks ago
..
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

Űrjáték készítése 3. rész: Mozgás hozzáadása

Előadás előtti kvíz

Előadás előtti kvíz

A játékok nem igazán szórakoztatóak, amíg nem látunk ide-oda mozgó idegeneket a képernyőn! Ebben a játékban kétféle mozgást fogunk használni:

  • Billentyűzet/egér mozgás: amikor a felhasználó a billentyűzettel vagy egérrel mozgat egy objektumot a képernyőn.
  • Játék által generált mozgás: amikor a játék bizonyos időközönként mozgat egy objektumot.

De hogyan mozgatunk dolgokat a képernyőn? Az egész a derékszögű koordinátákról szól: megváltoztatjuk az objektum helyét (x, y), majd újrarajzoljuk a képernyőt.

Általában a következő lépések szükségesek a mozgás megvalósításához a képernyőn:

  1. Új hely meghatározása egy objektum számára; ez szükséges ahhoz, hogy az objektum mozgását érzékeljük.
  2. Képernyő törlése, a képernyőt minden rajzolás között törölni kell. Ezt úgy tehetjük meg, hogy egy téglalapot rajzolunk, amelyet kitöltünk egy háttérszínnel.
  3. Objektum újrarajzolása az új helyen. Ezzel végül elérjük, hogy az objektum egyik helyről a másikra mozogjon.

Így nézhet ki ez a kódban:

//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);

Gondolkozz el azon, hogy miért okozhat teljesítményproblémákat, ha a hősödet másodpercenként sokszor újrarajzolod? Olvass utána az alternatív megoldásoknak.

Billentyűesemények kezelése

Az eseményeket úgy kezeljük, hogy specifikus eseményeket kapcsolunk kódhoz. A billentyűesemények az egész ablakra vonatkoznak, míg az egér események, például a click, egy adott elemhez kapcsolhatók. Ebben a projektben billentyűeseményeket fogunk használni.

Egy esemény kezeléséhez az ablak addEventListener() metódusát kell használnod, és két bemeneti paramétert kell megadnod. Az első paraméter az esemény neve, például keyup. A második paraméter az a függvény, amelyet az esemény bekövetkezésekor meg kell hívni.

Íme egy példa:

window.addEventListener('keyup', (evt) => {
  // `evt.key` = string representation of the key
  if (evt.key === 'ArrowUp') {
    // do something
  }
})

A billentyűeseményekhez két tulajdonságot használhatsz az eseményen belül, hogy megtudd, melyik billentyűt nyomták meg:

  • key: ez a megnyomott billentyű szöveges ábrázolása, például ArrowUp.
  • keyCode: ez a számérték, például 37, amely az ArrowLeft-nek felel meg.

A billentyűesemények kezelése a játékfejlesztésen kívül is hasznos lehet. Milyen más felhasználási módokat tudsz elképzelni ehhez a technikához?

Speciális billentyűk: egy figyelmeztetés

Vannak bizonyos speciális billentyűk, amelyek hatással vannak az ablakra. Ez azt jelenti, hogy ha például egy keyup eseményt figyelsz, és ezeket a speciális billentyűket használod a hős mozgatására, akkor vízszintes görgetés is történhet. Emiatt érdemes lehet kikapcsolni ezt a beépített böngészői viselkedést, miközben a játékodat építed. Ehhez ilyen kódra van szükséged:

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);

A fenti kód biztosítja, hogy a nyílbillentyűk és a szóköz billentyű alapértelmezett viselkedése ki legyen kapcsolva. A kikapcsolás a e.preventDefault() hívásával történik.

Játék által generált mozgás

Az objektumokat maguktól is mozgathatjuk időzítők, például a setTimeout() vagy setInterval() függvények segítségével, amelyek minden időintervallumban frissítik az objektum helyét. Így nézhet ki ez:

let id = setInterval(() => {
  //move the enemy on the y axis
  enemy.y += 10;
})

A játékciklus

A játékciklus egy olyan koncepció, amely lényegében egy rendszeresen meghívott függvény. Azért hívják játékciklusnak, mert minden, amit a felhasználónak látnia kell, ebben a ciklusban kerül kirajzolásra. A játékciklus az összes játékobjektumot használja, amelyek a játék részét képezik, és mindet kirajzolja, kivéve, ha valamilyen okból már nem részei a játéknak. Például, ha egy objektum egy ellenség, amelyet egy lézer eltalált és felrobbant, akkor az már nem része az aktuális játékciklusnak (erről többet tanulsz a következő leckékben).

Így nézhet ki egy játékciklus kódban kifejezve:

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);

A fenti ciklus minden 200 milliszekundumban meghívódik, hogy újrarajzolja a vásznat. Te döntheted el, hogy mi a legjobb időköz a játékod számára.

Az Űrjáték folytatása

A meglévő kódot fogod bővíteni. Vagy az első részben elkészült kóddal kezdj, vagy használd a II. rész - kezdő kódját.

  • Hős mozgatása: kódot fogsz hozzáadni, hogy a hőst a nyílbillentyűkkel lehessen mozgatni.
  • Ellenségek mozgatása: kódot kell hozzáadnod, hogy az ellenségek fentről lefelé mozogjanak egy adott sebességgel.

Ajánlott lépések

Keresd meg azokat a fájlokat, amelyeket a your-work almappában hoztak létre számodra. Ezeknek a következőket kell tartalmazniuk:

-| assets
  -| enemyShip.png
  -| player.png
-| index.html
-| app.js
-| package.json

A projektedet a your_work mappában indíthatod el az alábbi parancs beírásával:

cd your-work
npm start

A fenti parancs egy HTTP szervert indít a http://localhost:5000 címen. Nyiss meg egy böngészőt, és írd be ezt a címet. Jelenleg a hős és az összes ellenség megjelenik; még semmi sem mozog!

Kód hozzáadása

  1. Hozz létre dedikált objektumokat a hero, enemy és game object számára, ezeknek legyen x és y tulajdonságuk. (Emlékezz az Öröklődés vagy kompozíció részre).

    TIPP: A game object legyen az, amelyik rendelkezik x és y tulajdonságokkal, és képes önmagát kirajzolni a vászonra.

    tipp: hozz létre egy új GameObject osztályt az alábbi konstruktorral, majd rajzold ki a vászonra:

    
    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);
      }
    }
    

    Most bővítsd ki ezt a GameObject-et, hogy létrehozd a Hero-t és az Enemy-t.

    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)
      }
    }
    
  2. Adj hozzá billentyűesemény-kezelőket, hogy kezeld a hős navigációját (mozgatás fel/le, balra/jobbra).

    EMLÉKEZZ: Ez egy derékszögű rendszer, a bal felső sarok a 0,0. Ne felejts el kódot hozzáadni az alapértelmezett viselkedés leállításához.

    tipp: hozd létre az onKeyDown függvényedet, és csatold az ablakhoz:

     let onKeyDown = function (e) {
           console.log(e.keyCode);
             ...add the code from the lesson above to stop default behavior
           }
     };
    
     window.addEventListener("keydown", onKeyDown);
    

    Nézd meg a böngésződ konzolját, és figyeld, ahogy a billentyűleütések naplózásra kerülnek.

  3. Valósítsd meg a Pub-sub mintát, hogy a kódod tiszta maradjon a további részek során.

    Ehhez az utolsó részhez:

    1. Adj hozzá egy eseményfigyelőt az ablakhoz:

       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);
         }
       });
      
    2. Hozz létre egy EventEmitter osztályt, hogy üzeneteket publikálj és iratkozz fel rájuk:

      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));
          }
        }
      }
      
    3. Adj hozzá konstansokat, és állítsd be az EventEmitter-t:

      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();
      
    4. Inicializáld a játékot

    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;
      });
    }
    
  4. Állítsd be a játékciklust

    Refaktoráld az window.onload függvényt, hogy inicializálja a játékot, és állíts be egy játékciklust megfelelő időközönként. Adj hozzá egy lézersugarat is:

    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. Adj hozzá kódot, hogy az ellenségek bizonyos időközönként mozogjanak.

    Refaktoráld a createEnemies() függvényt, hogy létrehozza az ellenségeket, és hozzáadja őket az új gameObjects osztályhoz:

    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);
        }
      }
    }
    

    és adj hozzá egy createHero() függvényt, hogy hasonló folyamatot végezzen a hős számára.

    function createHero() {
      hero = new Hero(
        canvas.width / 2 - 45,
        canvas.height - canvas.height / 4
      );
      hero.img = heroImg;
      gameObjects.push(hero);
    }
    

    végül adj hozzá egy drawGameObjects() függvényt, hogy elindítsd a rajzolást:

    function drawGameObjects(ctx) {
      gameObjects.forEach(go => go.draw(ctx));
    }
    

    Az ellenségeid elindulnak a hős űrhajód felé!


🚀 Kihívás

Ahogy láthatod, a kódod könnyen "spagetti kóddá" válhat, amikor elkezdesz funkciókat, változókat és osztályokat hozzáadni. Hogyan tudnád jobban megszervezni a kódodat, hogy olvashatóbb legyen? Vázolj fel egy rendszert a kódod megszervezésére, még akkor is, ha az egy fájlban marad.

Előadás utáni kvíz

Előadás utáni kvíz

Áttekintés és önálló tanulás

Bár a játékunkat keretrendszerek használata nélkül írjuk, számos JavaScript-alapú vászon keretrendszer létezik játékfejlesztéshez. Szánj időt arra, hogy olvass ezekről.

Feladat

Kommentáld a kódodat


Felelősségkizárás:
Ez a dokumentum az Co-op Translator AI fordítási szolgáltatás segítségével készült. Bár törekszünk a pontosságra, kérjük, vegye figyelembe, hogy az automatikus fordítások hibákat vagy pontatlanságokat tartalmazhatnak. Az eredeti dokumentum az eredeti nyelvén tekintendő hiteles forrásnak. Kritikus információk esetén javasolt a professzionális, emberi fordítás igénybevétele. Nem vállalunk felelősséget a fordítás használatából eredő félreértésekért vagy téves értelmezésekért.