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

Előző változat mindkét oldalon Előző változat
Következő változat
Előző változat
tanszek:oktatas:jatek_prototipusok:jatek_prototipusok:rpg_tutorial [2022/10/08 14:50]
kissa [Tiled map editor]
tanszek:oktatas:jatek_prototipusok:jatek_prototipusok:rpg_tutorial [2022/10/10 08:38] (aktuális)
kissa
Sor 2: Sor 2:
  
 ===== Térkép felépítése ===== ===== Térkép felépítése =====
-Tiled map editor segítségével.+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> <sxh js>
-constructor(config,​ clock, spawnLocations,​ addObject, deleteObject) {+class Spawner { 
 +    ​constructor(config,​ clock, spawnLocations,​ addObject, deleteObject) {
         this.id = config.id;         this.id = config.id;
         this.spawnInterval = config.spawnInterval;​         this.spawnInterval = config.spawnInterval;​
Sor 20: Sor 34:
         this.objectsCreated = [];         this.objectsCreated = [];
  
-        this.objectId = 1000;+        this.objectId = 1; 
 +    } 
 +
 +</​sxh>​
  
-        ​this.start();​+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>​ </​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.
  
-==== Ellenségek ​spawn-olása ====+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:​ 
 + 
 +<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.1665240626.txt.gz · Utolsó módosítás: 2022/10/08 14:50 szerkesztette: kissa