Felhasználói eszközök

Eszközök a webhelyen


tanszek:oktatas:jatek_prototipusok:jatek_prototipusok:rpg_tutorial

Különbségek

A kiválasztott változat és az aktuális verzió közötti különbségek a következők.

Összehasonlító nézet linkje

Következő változat
Előző változat
tanszek:oktatas:jatek_prototipusok:jatek_prototipusok:rpg_tutorial [2022/10/08 14:44]
kissa létrehozva
tanszek:oktatas:jatek_prototipusok:jatek_prototipusok:rpg_tutorial [2022/10/10 08:38] (aktuális)
kissa
Sor 1: Sor 1:
 ====== RPG játék ====== ====== RPG játék ======
  
-===== Tiled map editor ​===== +===== Térkép felépítése ​===== 
 +Tiled map editor segítségével:​ 
 +  - térkép létrehozása 
 +  - tileset betöltése 
 +  - tile layer-ek létrehozása:​ Egyszerű képi információkat tartalmaznak. 
 +  - object layer-ek létrehozása:​ A felhasználó számára közvetlenül nem látható objektumokat tartalmaznak,​ melyeknek saját tulajdonságai lehetnek. Az objektumokat a játékprogram fogja feldolgozni (pl. spawner-ek, portálok, effektek, savepoint-ok létrehozása céljából). 
 +  - térkép exportálása 
 +    - az exportált .tmx fájlt később lehet szerkeszteni Tiled-ban 
 +    - az exportált .json fájlt a Phaser közvetlenül be tudja tölteni
 ===== Térkép betöltése,​ játékos mozgatása ===== ===== Térkép betöltése,​ játékos mozgatása =====
 +  - asset-ek betöltése 
 +  - tilemap létrehozása 
 +  - tileset betöltése 
 +  - layer-ek létrehozása,​ ütközések beállítása 
 +  - térkép metaadatok kinyerése (spawner objektumok) 
 +  - játékos követése a kamerával
 ===== Spawner osztály ===== ===== Spawner osztály =====
 +Hozzunk létre egy új fájlt a ''​js/​classes''​ mappában, ''​spawner.js''​ néven, a következő tartalommal:​
 +
 +<sxh js>
 +class Spawner {
 +    constructor(config,​ clock, spawnLocations,​ addObject, deleteObject) {
 +        this.id = config.id;
 +        this.spawnInterval = config.spawnInterval;​
 +        this.limit = config.limit;​
 +        this.objectType = config.objectType;​
 +        this.clock = clock;
 +        this.spawnLocations = spawnLocations;​
 +        this.addObject = addObject;
 +        this.deleteObject = deleteObject;​
 +
 +        this.objectsCreated = [];
 +
 +        this.objectId = 1;
 +    }
 +}
 +</​sxh>​
 +
 +Ez az osztály fogja szabályozni a spawner-ek működését. Az osztály konstruktora a következő adatokat fogadja:
 +  * ''​config''​ objektum:
 +    * ''​id'':​ a spawner azonosítója,​
 +    * ''​spawnInterval'':​ objektum spawn-olás időköze,
 +    * ''​limit'':​ aktív objektumok maximális száma,
 +    * ''​objectType'':​ objektum típusa (''​MONSTER''​ vagy ''​CHEST''​)
 +  * ''​clock'':​ a megfelelő scene-hez tartozó Clock objektum (''​this.time''​)
 +  * ''​spawnLocations'':​ az objektumok lehetséges pozíciói,
 +  * ''​addObject'',​ ''​deleteObject'':​ callback függvények,​ melyeket valamely objektum hozzáadása,​ illetve törlése esetén fog meghívni a ''​Spawner''​ osztály.
 +
 +Az ''​objectsCreated''​ tömb a spawner által létrehozott aktív objektumokat tartalmazza,​ az ''​objectId''​ pedig az első létrehozandó objektum egyedi azonosítóját.
 +
 +Az osztályon belül hozzunk létre egy ''​start''​ metódust, mely az objektumok spawn-olását fogja időzíteni:​
 +
 +<sxh js>
 +start() {
 +        this.interval = this.clock.addEvent({
 +            delay: this.spawnInterval,​
 +            loop: true,
 +            callback: () => {
 +                if (this.objectsCreated.length < this.limit) {
 +                    this.spawnObject();​
 +                }
 +            }
 +        });
 +    }
 +</​sxh>​
 +
 +A konstruktorban,​ az adattagok inicializálását követően hívjuk is meg a létrehozott metódust: ''​this.start();''​
 +
 +A ''​spawnObject''​ metódus az ''​objectType''​ adattagnak megfelelő objektumot fog létrehozni (egyelőre a ''​Spawner''​ osztály csak ládák létrehozását támogatja):​
 +
 +<sxh js>
 +spawnObject() {
 +        if (this.objectType === '​CHEST'​) {
 +            this.spawnChest();​
 +        }
 +    }
 +</​sxh>​
 +
 +A ''​spawnChest''​ metódus kisorsolja, hogy a spawner melyik lokációra spawnolja a ládát, majd létrehozza a láda adatait. Minden láda egyedi azonosítót kap, mely a spawner id-ból, és egy generált objektum azonosítóból áll. Ezen kívül minden ládához tartozik egy ''​coins''​ érték. A létrehozott ládát a Spawner osztály ''​objectsCreated''​ tömbjében eltároljuk,​ majd meghívjuk a konstruktorban kapott ''​addObject''​ callback függvényt (ez a ''​GameScene''​ osztályban lesz később implementálva,​ és a láda tényleges megjelenítéséért fog felelni).
 +
 +<sxh js>
 +spawnChest() {
 +        const location = this.pickRandomLocation();​
 +        const newChest = {
 +            id: `${this.id}-${this.getNewObjectId()}`,​
 +            spawnerId: this.id,
 +            x: location[0],​
 +            y: location[1],​
 +            coins: Phaser.Math.RND.between(10,​ 20)
 +        };
 +        this.objectsCreated.push(newChest);​
 +        this.addObject(newChest);​
 +    }
 +</​sxh>​
 +
 +A lehetséges lokációk közül a ''​pickRandomLocation''​ metódus fog választani. Amennyiben a létrehozott objektumok között már található olyan, ami a kisorsolt lokáción található ([[https://​developer.mozilla.org/​en-US/​docs/​Web/​JavaScript/​Reference/​Global_Objects/​Array/​some|Array.prototype.some()]] függvény működése),​ akkor újabb pozíció sorsolódik rekurzív módon, addig, amíg egy szabad helyet találunk:
 +
 +<sxh js>
 +pickRandomLocation() {
 +        const location = Phaser.Math.RND.pick(this.spawnLocations);​
 +        const invalidLocation = this.objectsCreated.some((obj) => obj.x === location[0] && obj.y === location[1]);​
 +        return invalidLocation ? this.pickRandomLocation() : location;
 +    }
 +</​sxh>​
 +
 +A ''​getNewObjectId''​ metódus eggyel növeli az ''​objectId''​-t,​ majd visszaadja az értéket, így egyedi azonosítót generál:
 +
 +<sxh js>
 +getNewObjectId() {
 +        return ++this.objectId;​
 +    }
 +</​sxh>​
 +
 +Ezen kívül szükségünk lesz a későbbiekben a spawnolt objektumok törlésére (pl. ha a játékos kinyitotta a ládát), ehhez is létrehozunk egy metódust, mely az objektum azonosítója alapján törli a megfelelő objektumot.
 +
 +<sxh js>
 +removeObject(id) {
 +        this.objectsCreated = this.objectsCreated.filter(obj => obj.id !== id);
 +    }
 +</​sxh>​
 +
 +Ahhoz, hogy a ''​Spawner''​ osztályt ténylegesen használni tudjuk, hivatkozzunk rá az ''​index.html''​ fájlban, még a scene-k betöltése előtt:
 +
 +<sxh html>
 +<script src="​js/​classes/​spawner.js"></​script>​
 +</​sxh>​
  
 ==== Ládák spawn-olása ==== ==== Ládák spawn-olása ====
 +A ládák spawnolása a ''​Spawner''​ osztályban most már megtörténik,​ viszont itt még csak a ládákhoz tartozó adatok (id, pozíció, tárolt coin-ok száma) kerülnek létrehozásra. A ládák tényleges megjelenítését a ''​GameScene''​ fogja végezni.
 +
 +A ''​scenes/​game.scene.js''​ fájlban szeretnénk tárolni a létrehozott spawnereket,​ ezért az ''​init''​ metódusban hozzunk létre számukra egy új objektumot:
 +
 +<sxh js>
 +this.spawners = {};
 +</​sxh>​
 +
 +Ezen kívül egy ''​setupSpawners''​ metódust is létre fogunk hozni, mely a láda lokációk alapján hozza létre a spawner-eket ([[https://​developer.mozilla.org/​en-US/​docs/​Web/​JavaScript/​Reference/​Global_Objects/​Object/​keys|Object.keys() függvény működése]]):​
 +
 +<sxh js>
 +gameScene.setupSpawners = function () {
 +  Object.keys(this.chestLocations).forEach(id => {
 +    const config = {
 +      id: `chest-${id}`,​
 +      spawnInterval:​ 3000,
 +      limit: 3,
 +      objectType: '​CHEST'​
 +    };
 +    const spawner = new Spawner(
 +      config,
 +      this.time,
 +      this.chestLocations[id],​
 +      (chest) => this.addChest(chest),​
 +      (id) => this.deleteChest(id)
 +    );
 +    this.spawners[spawner.id] = spawner;
 +  });
 +};
 +</​sxh>​
 +
 +A ''​setupSpawner''​ metódust hívjuk meg a ''​create''​ metódusban,​ közvetlenül a player spawn-olása után! Emellett létre kell hoznunk egy Physics Group-ot is, melyben a láda sprite-okat fogjuk tárolni:
 +
 +<sxh js>
 +this.setupSpawners();​
 +
 +this.chests = this.physics.add.group();​
 +</​sxh>​
 +
 +A spawner-ek létrehozásakor hivatkoztunk az ''​addChest''​ és ''​deleteChest''​ metódusokra is, hozzuk létre ezeket is:
 +
 +<sxh js>
 +gameScene.addChest = function (chestData) {
 +  console.log('​Spawning',​ chestData);
 +  this.chests[chestData.id] = chestData;
 +  const chest = this.add.sprite(chestData.x * this.scale, chestData.y * this.scale, '​items',​ 0);
 +  chest.config = {
 +    coins: chestData.coins,​
 +    id: chestData.id
 +  };
 +  chest.setScale(this.scale);​
 +  this.chests.add(chest,​ true);
 +};
 +
 +gameScene.deleteChest = function (chestId) {
 +  delete this.chests[chestId];​
 +};
 +</​sxh>​
 +
 +A ''​chest''​ sprite létrehozásakor egy ''​config''​ objektumot rendeltünk a sprite-hoz, mely a láda azonosítóját és a benne tárolt coinok számát tárolja.
 +
 +Akkor dolgoztunk jól, ha 3 másodperc elteltével a konzolon látjuk, hogy különböző id-kkal új ládák kerültek létrehozásra,​ és a pályán ezek meg is jelentek.
 +
 +{{:​tanszek:​oktatas:​jatek_prototipusok:​jatek_prototipusok:​chest-spawn.png?​400|}}
 +
 +==== Ládák begyűjtése ====
 +Következő lépésként,​ amikor a láda és a játékos átfedésbe kerül, akkor szeretnénk a ládában tárolt pontokat jóváírni,​ a ládát eltüntetni,​ majd helyette új ládát spawnolni. Ennek vizsgálatához adjuk hozzá a következő sort a ''​create''​ metódus megfelelő részéhez:
 +
 +<sxh js>
 +this.physics.add.overlap(this.player,​ this.chests,​ (player, chest) => this.collectChest(player,​ chest));
 +</​sxh>​
 +
 +A ''​collectChest''​ metódust a következőképpen implementáljuk. Ha a hivatkozott láda ténylegesen létezik, akkor a hozzá tartozó adatokat (ezt a ''​Spawner''​ osztály tárolja) és sprite-ot is töröljük!
 +
 +<sxh js>
 +gameScene.collectChest = function (player, chest) {
 +  this.score += chest.config.coins;​
 +  uiScene.updateScore(this.score);​
 +
 +  if (this.chests[chest.config.id]) {
 +    this.spawners[this.chests[chest.config.id].spawnerId].removeObject(chest.config.id);​
 +    chest.destroy();​
 +  }
 +};
 +</​sxh>​
 +
 +A láda begyűjtése után a coin-ok jóváíródnak,​ a láda eltűnik, és helyette új láda spawn-olódik egy véletlenszerűen kiválasztott pozíción (utóbbi a konzolon ellenőrizhető).
 +
 +==== Szörnyek spawn-olása ====
 +A szörnyek spawn-olása a ládákkal analóg módon fog történni. Először a ''​Spawner''​ osztályt kell módosítanunk,​ a ''​spawnObject''​ metódus egy ''​else if''​ ággal egészül ki:
 +
 +<sxh js>
 +spawnObject() {
 +        if (this.objectType === '​CHEST'​) {
 +            this.spawnChest();​
 +        } else if (this.objectType === '​MONSTER'​) {
 +            this.spawnMonster();​
 +        }
 +    }
 +</​sxh>​
 +
 +Ezen kívül az új ''​spawnMonster''​ metódust is implementálnunk kell. A szörnyek a ládák tulajdonságai mellett ''​frame''​ (melyik szörny legyen megjelenítve a spritesheet-ről),​ ''​health''​ (életerő),​ ''​attack''​ (sebzés) értékekkel is rendelkeznek.
 +
 +<sxh js>
 +spawnMonster() {
 +        const location = this.pickRandomLocation();​
 +        const newMonster = {
 +            id: `${this.id}-${this.getNewObjectId()}`,​
 +            spawnerId: this.id,
 +            x: location[0],​
 +            y: location[1],​
 +            coins: Phaser.Math.RND.between(10,​ 20),
 +            frame: Phaser.Math.RND.between(0,​ 19),
 +            health: Phaser.Math.RND.between(3,​ 5),
 +            attack: 1
 +        };
 +        this.objectsCreated.push(newMonster);​
 +        this.addObject(newMonster);​
 +    }
 +</​sxh>​
 +
 +A spawnerek példányosítását és a spawnolt szörnyek tényleges megjelenítését a ''​GameScene''​ fogja végezni. Itt a szörnyeket egy új Physics Group-ban fogjuk tárolni, melyet a ''​create''​ metódusban hozunk létre:
 +
 +<sxh js>
 +this.monsters = this.physics.add.group();​
 +</​sxh>​
 +
 +A szörnyek megjelenítéséhez ezentúl a ''​BootScene''​-en be kell töltenünk a hozzájuk tartozó spritesheet-et is:
 +
 +<sxh js>
 +this.load.spritesheet('​monsters',​ '​assets/​monsters.png',​ {
 +    frameWidth: 32,
 +    frameHeight:​ 32
 +  });
 +</​sxh>​
 +
 +Ezután a ''​GameScene''​-n létre kell hoznunk a spawnereket,​ a ''​setupSpawners''​ metódushoz a következő sorokat kell hozzáadni (a láda spawnerek létrehozásával analóg módon):
 +
 +<sxh js>
 +Object.keys(this.monsterLocations).forEach(id => {
 +    const config = {
 +      id: `monster-${id}`,​
 +      spawnInterval:​ 3000,
 +      limit: 3,
 +      objectType: '​MONSTER'​
 +    };
 +    const spawner = new Spawner(
 +      config,
 +      this.time,
 +      this.monsterLocations[id],​
 +      (monster) => this.addMonster(monster),​
 +      (id) => this.deleteMonster(id)
 +    );
 +    this.spawners[spawner.id] = spawner;
 +  });
 +</​sxh>​
 +
 +Szintén létre kell hoznunk az itt hivatkozott ''​addMonster''​ és ''​deleteMonster''​ metódusokat:​
 +
 +<sxh js>
 +gameScene.addMonster = function (monsterData) {
 +  this.monsters[monsterData.id] = monsterData;​
 +
 +  const monster = this.add.sprite(monsterData.x * this.scale, monsterData.y * this.scale, '​monsters',​ monsterData.frame);​
 +  monster.config = {
 +    id: monsterData.id,​
 +    coins: monsterData.coins,​
 +    health: monsterData.health,​
 +    attack: monsterData.attack
 +  };
 +  monster.setScale(this.scale);​
 +  this.monsters.add(monster,​ true);
 +};
 +
 +gameScene.deleteMonster = function (monsterId) {
 +  delete this.monsters[monsterId];​
 +};
 +</​sxh>​
 +
 +Ha ezután a pályán mozogva a ládák mellett szörnyekkel is találkozunk,​ jól dolgoztunk:
 +
 +{{:​tanszek:​oktatas:​jatek_prototipusok:​jatek_prototipusok:​monster-spawned.png?​400|}}
 +
 +===== Harc a szörnyekkel =====
 +Minden szörny rendelkezik életerővel (''​health''​),​ sebzéssel (''​attack''​),​ és coin-okkal (''​coins''​). Ezeket az adatokat a játékos szörnyekkel történő harca során használjuk fel.
 +
 +==== Fegyver létrehozása,​ animálása ====
 +Kezdésként a játékos kezében szeretnénk megjeleníteni egy kardot, amit mindig a játékossal együtt mozgatunk. Ehhez a játékos spawn-olásán módosítani fogunk. A ''​Player''​ sprite mellé egy ''​Weapon''​ sprite-ot is létrehozunk,​ majd ezt a kettőt egy konténerben kapcsoljuk össze ([[https://​photonstorm.github.io/​phaser3-docs/​Phaser.GameObjects.Container.html|Container osztály dokumentációja]]).
 +
 +A ''​spawnPlayer''​ metódust változtassuk meg:
 +
 +<sxh js>
 +gameScene.spawnPlayer = function () {
 +  const location = Phaser.Math.RND.pick(this.playerLocations);​
 +  this.player = this.add.image(0,​ 0, '​characters',​ 0);
 +  this.physics.add.existing(this.player);​
 +  this.player.setScale(this.scale);​
 +  this.player.body.setCollideWorldBounds(true);​
 +
 +  this.weapon = this.add.image(this.player.x - 32, this.player.y,​ '​items',​ 4);
 +  this.weapon.flipX = true;
 +  this.physics.add.existing(this.weapon);​
 +  this.weapon.body.setCollideWorldBounds(true);​
 +
 +  this.playerContainer = this.add.container(location[0] * this.scale, location[1] * this.scale, [this.player,​ this.weapon]);​
 +  this.playerContainer.setSize(32 * this.scale, 32 * this.scale);​
 +  this.physics.world.enable(this.playerContainer);​
 +  this.playerContainer.body.setCollideWorldBounds(true);​
 +};
 +</​sxh>​
 +
 +A kódban látható, hogy abszolút pozíciója csak a konténernek van, a játékos és a fegyver koordinátái a konténer pozíciójához képest vannak megadva.
 +
 +A kamerának a továbbiakban nem a ''​player''​ sprite-ot, hanem a ''​playerContainer''​-t kell követnie, ezt módosítsuk:​
 +
 +<sxh js>
 +this.cameras.main.startFollow(this.playerContainer);​
 +</​sxh>​
 +
 +Módosítanunk kell továbbá az ''​updatePlayer''​ metódust is, a megfelelő irányú erőket ezután magán a konténeren helyezzük el, a játékos helyett. Amikor jobbra vagy balra mozdulunk, akkor a fegyvert is ennek megfelelően fordítjuk el, illetve helyezzük át.
 +
 +<sxh js>
 +gameScene.updatePlayer = function () {
 +  this.playerContainer.body.setVelocity(0);​
 +
 +  if (this.cursors.left.isDown) {
 +    this.player.flipX = false;
 +    this.playerContainer.body.setVelocityX(-this.playerSpeed);​
 +    this.weapon.flipX = true;
 +    this.weapon.setPosition(this.player.x - 32, this.player.y);​
 +  } else if (this.cursors.right.isDown) {
 +    this.player.flipX = true;
 +    this.playerContainer.body.setVelocityX(this.playerSpeed);​
 +    this.weapon.flipX = false;
 +    this.weapon.setPosition(this.player.x + 32, this.player.y);​
 +  }
 +
 +  if (this.cursors.up.isDown) {
 +    this.playerContainer.body.setVelocityY(-this.playerSpeed);​
 +  } else if (this.cursors.down.isDown) {
 +    this.playerContainer.body.setVelocityY(this.playerSpeed);​
 +  }
 +};
 +</​sxh>​
 +
 +Módosítsuk továbbá a játékos és a blocked layer közötti collider-t is. Itt szintén a konténer és a blocked layer közötti ütközést kell vizsgálni:
 +
 +<sxh js>
 +this.physics.add.collider(this.playerContainer,​ this.blockedLayer);​
 +</​sxh>​
 +==== Harc megvalósítása ====
 +A harc implementációjának első lépéseként adattagokat fogunk felvenni. Egyrészt tárolnunk kell azt, hogy a játékos éppen harcol-e, ezt adjuk hozzá az ''​init''​ metódushoz:​
 +
 +<sxh js>
 +this.playerAttacking = false;
 +</​sxh>​
 +
 +Másrészt tárolnunk kell a játékos életerejét (''​health''​),​ illetve azt, hogy az aktuális támadás során már megütötte-e a támadott szörnyet (''​swordHit''​). Ezt a ''​spawnPlayer''​ metódusban vezetjük be, a ''​player''​ sprite létrehozása után:
 +
 +<sxh js>
 +this.player.config = {
 +    swordHit: false,
 +    health: 7
 +  };
 +</​sxh>​
 +
 +Következő lépésként egy tween animációt fogunk létrehozni,​ mely a SPACE billentyű lenyomása esetén a kardot 360°-ban megforgatja. Ehhez az ''​updatePlayer''​ metódust egészítsük ki a következő sorokkal:
 +
 +<sxh js>
 +if (Phaser.Input.Keyboard.JustDown(this.cursors.space) && !this.playerAttacking) {
 +    this.playerAttacking = true;
 +
 +    this.tweens.add({
 +      targets: this.weapon,​
 +      duration: 150,
 +      angle: this.weapon.flipX ? -360 : 360,
 +      paused: false,
 +      onComplete: () => {
 +        this.playerAttacking = false;
 +        this.player.config.swordHit = false;
 +      }
 +    });
 +  }
 +</​sxh>​
 +
 +Ha jól dolgoztunk, a SPACE lenyomása után a játékos kardja 360°-ban körbefordul.
 +
 +A ''​Phaser.Input.Keyboard.JustDown()''​ metódus a SPACE billentyű lenyomása után egyszer fog ''​true''​ értéket visszaadni, majd ezután mindig ''​false''​-t (ezzel azt jelzi, hogy éppen most lett-e lenyomva a billentyű, vagy már hosszabb ideje nyomva van). A billentyű újbóli lenyomásakor ugyanez fog történni, így a SPACE egyszeri lenyomásakor egyetlen alkalommal forgatja meg a kardot a játék. Az animáció végeztével a ''​playerAttacking''​ és ''​swordHit''​ tulajdonságok értéke ''​false''​-ra áll vissza, így újabb támadás indítható a SPACE újbóli lenyomásával.
 +
 +A szörnyekkel történő harc tényleges megvalósításához helyezzünk el egy vizsgálatot a ''​create''​ metódusban,​ ami a játékos fegyverének és egy szörnynek az átfedését vizsgálja.
 +
 +<sxh js>
 +this.physics.add.overlap(this.weapon,​ this.monsters,​ (weapon, monster) => this.enemyOverlap(weapon,​ monster));
 +</​sxh>​
 +
 +Ezután adjunk egy kezdeti implementációt az ''​enemyOverlap''​ metódusnak. Ez azt vizsgálja, hogy a játékos támadásban van-e, illetve még nem ütötte-e meg a szörnyet. Ha a feltétel teljesül, akkor rögzítjük,​ hogy az adott támadás során a játékos megütötte a szörnyet, és a későbbiekben változtatni fogjuk a szörny, valamint a játékos életerejét is (de egyelőre csak egy üzenetet íratunk ki a konzolra).
 +
 +<sxh js>
 +gameScene.enemyOverlap = function (weapon, monster) {
 +  if (this.playerAttacking && !this.player.config.swordHit) {
 +    this.player.config.swordHit = true;
 +    console.log('​MONSTER HIT!'​);​
 +  }
 +};
 +</​sxh>​
 +
 +Akkor dolgoztunk jól, ha a szörny támadásakor SPACE billentyű egyszeri lenyomásakor egyszer jelenik meg a konzolon a ''​MONSTER HIT!''​ üzenet (ennek vizsgálatakor a spawnerek logja zavaró lehet, így azt törölhetjük a kódunkból).
 +
 +{{:​tanszek:​oktatas:​jatek_prototipusok:​jatek_prototipusok:​monster-hit.png?​400|}}
 +
 +A metódusunkat tovább bővítjük,​ minden ütéskor 1-et levonunk a szörny életerejéből,​ majd megvizsgáljuk,​ hogy az 0-ra csökkent-e. Amennyiben igen, a szörnyhöz tartozó coin-okat jóváírjuk a játékosnál (és frissítjük a kijelzést),​ majd a szörny adatait eltávolítjuk a spawner-ből,​ és végül magát a sprite-ot is megszüntetjük.
 +
 +<sxh js>
 +gameScene.enemyOverlap = function (weapon, monster) {
 +  if (this.playerAttacking && !this.player.config.swordHit) {
 +    this.player.config.swordHit = true;
 +    ​
 +    if (this.monsters[monster.config.id]) {
 +      monster.config.health -= 1;
 +
 +      if (monster.config.health <= 0) {
 +        this.score += monster.config.coins;​
 +        uiScene.updateScore(this.score);​
 +        this.spawners[this.monsters[monster.config.id].spawnerId].removeObject(monster.config.id);​
 +        monster.destroy();​
 +      }
 +    }
 +  }
 +};
 +</​sxh>​
 +
 +Ha jól dolgoztunk, azt kell látnunk a konzolon, hogy minden ütéssel 1-et csökken az ellenség életereje, majd ha elérte a 0-t, a szörny eltűnik és a játékosnál a pontok jóváíródnak.
 +
 +{{:​tanszek:​oktatas:​jatek_prototipusok:​jatek_prototipusok:​monster-health.png?​400|}}
 +
 +Most tovább bővítjük az implementációt,​ amennyiben a szörny nem halt meg, a játékos életerejét is csökkentjük a szörny ''​attack''​ tulajdonságának értékével. Emellett azt is megvizsgáljuk,​ hogy a játékos életereje 0-ra csökkent-e. Amennyiben igen, a játékot újraindítjuk.
 +
 +<sxh js>
 +gameScene.enemyOverlap = function (weapon, monster) {
 +  if (this.playerAttacking && !this.player.config.swordHit) {
 +    this.player.config.swordHit = true;
 +
 +    if (this.monsters[monster.config.id]) {
 +      monster.config.health -= 1;
 +
 +      console.log('​monster health',​ monster.config.health);​
 +
 +      if (monster.config.health <= 0) {
 +        this.score += monster.config.coins;​
 +        uiScene.updateScore(this.score);​
 +        this.spawners[this.monsters[monster.config.id].spawnerId].removeObject(monster.config.id);​
 +        monster.destroy();​
 +      } else {
 +        this.player.config.health -= monster.config.attack;​
 +        console.log('​player health',​ this.player.config.health);​
 +        if (this.player.config.health <= 0) {
 +          this.scene.restart();​
 +        }
 +      }
 +    }
 +  }
 +};
 +</​sxh>​
 +
 +A konzolon ekkor a szörny és a játékos életerejének csökkenését is látnunk kell.
 +
 +{{:​tanszek:​oktatas:​jatek_prototipusok:​jatek_prototipusok:​attack-health.png?​400|}}
 +
 +A játékos életerejét a felhasználó számára is láthatóvá szeretnénk tenni, ennek érdekében a ''​UiScene''​-t is módosítjuk. A ''​create''​ metódust új szöveg létrehozásával egészítjük ki:
 +
 +<sxh js>
 +this.healthText = this.add.text(690,​ 8, '​Health:​ 0', { fontSize: '​16px',​ fill: '#​fff'​ });
 +</​sxh>​
 +
 +Ezen kívül biztosítunk egy metódust az életerő értékének megváltoztatására:​
 +
 +<sxh js>
 +uiScene.updateHealth = function (newHealth) {
 +  this.healthText.setText(`Health:​ ${newHealth}`);​
 +};
 +</​sxh>​
 +
 +A ''​GameScene''​-en az ''​update''​ metódusban meg is hívjuk az új életerő kijelzését biztosító metódust:
  
-==== Ellenségek spawn-olása ====+<sxh js> 
 +uiScene.updateHealth(this.player.config.health);​ 
 +</​sxh>​
  
-===== Támadás =====+A kódból minden ''​console.log''​ utasítást kitörölhetünk,​ ezek csak a fejlesztés során voltak segítségünkre.
  
-===== Ellenségek mozgatása ​=====+===== Továbbfejlesztési lehetőségek ​===== 
 +  * életerő meghatározott időközönkénti töltése,​ 
 +  * az életerőt grafikusan kijelző health bar létrehozása a játékosnak és az ellenségeknek (lásd virtual pet game > progress bar a betöltési képernyőn),​ 
 +  * ellenségek mozgatása véletlenszerű irányba, 
 +  * object pooling alkalmazása a ládákra/​szörnyekre (lásd platformer példa), 
 +  * achievement rendszer létrehozása,​ 
 +  * új itemek bevezetése (pl. pajzs, páncélzat,​ stb., ezek csökkenthetik a szörnyek sebzését, és növelhetik a játékosét),​ 
 +  * inventory rendszer létrehozása,​ 
 +  * kereskedés itemekkel,​ 
 +  * új itemek craftolása meglévő alapanyagokból,​ 
 +  * szintrendszer bevezetése (pl. a magasabb szintű játékos rendelkezhet nagyobb sebzéssel),​ 
 +  * más pályákra irányító portálok létrehozása.
tanszek/oktatas/jatek_prototipusok/jatek_prototipusok/rpg_tutorial.1665240299.txt.gz · Utolsó módosítás: 2022/10/08 14:44 szerkesztette: kissa