En el último tutorial, que creó el BattleState para nuestro juego de rol por turnos. Ahora, vamos a crear un WorldState estábamos el jugador puede explorar y, finalmente, encontrar enemigos. Además, vamos a mejorar nuestro sistema de batalla que considerar la velocidad de las unidades. Los siguientes temas serán cubiertos en este tutorial:


  • Crear un WorldState el que el jugador puede navegar
  • Crear reproductores enemigas que iniciarán la BattleState
  • Mejorar el sistema de batalla que considerar la velocidad de las unidades

    Para leer este tutorial, es importante que usted está familiarizado con los conceptos siguientes:


    • Javascript y conceptos orientados a objetos.
    • conceptos básicos Phaser, tales como: estados, sprites, los grupos y la física de arcade
    • Creación de mapas con baldosa

      Tabla de contenidos

      Aprender Phaser mediante la construcción de 15 juegos

      Si usted quiere dominar Phaser y aprender cómo publicar juegos Phaser como juegos nativas para iOS y Android se sienten libres para comprobar Zenva En línea En este supuesto The Complete el juego para móvil Curso de Desarrollo – Construir 15 juegos.

      Activos de derechos de autor

      Los monstruos activos utilizados en este tutorial están disponibles en http://opengameart.org/content/2d-rpg-enemy-set y fueron creadas por los siguientes artistas: Brett Steele (Safir-Kreuz), Joe Raucci (Sylon ), Vicki Beinhart (Namakoro), Tyler Olsen (raíces). Los activos caracteres están disponibles en http://opengameart.org/content/24×32-characters-with-faces-big-pack y fueron creadas por Svetlana Kushnariova (e-mail: [email protected]). Todos los activos están disponibles a través de la licencia Creative Commons.

      archivos de código fuente

      Puede descargar los archivos de código fuente tutorial aquí.

      Creación de la WorldState

      Vamos a crear un WorldState que leer un mapa de baldosa (en formato JSON) y permitir al jugador para navegar a través de él. La siguiente figura muestra el mapa que creé. Dado que el objetivo de este tutorial no es en la creación de mapas de baldosas, no voy a entrar en los detalles de la misma, y ​​usted puede crear su propio uso o el previsto en el código fuente. Sin embargo, hay dos cosas importantes acerca de la creación mapa, debido a la forma en que vamos a crear WorldState:

      mapa


      1. Cualquier capa de mosaico Collidable debe tener un conjunto de propiedades de colisión en true.
      2. Todos los prefabricados de juego debe estar definido en la capa de objetos y cada objeto debe contener en sus propiedades al menos: nombre, tipo, grupo y textura. Todas las propiedades adicionales también deben ser definidas.

        Las figuras siguientes muestran las propiedades de una capa de colisión y el jugador objeto, para ilustrar esas dos condiciones.

        collision_layer object_properties

        Además del mapa de baldosa, WorldState leerá otro archivo JSON, como la de abajo. Tenga en cuenta que el archivo JSON debe especificar el activos, grupos y mapa de información.
        { «activos»: { «map_tileset»: { «type»: «imagen», «fuente»: «assets / images / open_tileset.png»}, «enemy_spawner_image»: { «type»: «imagen», «fuente» : «assets / images / enemy_spawner.png»}, «male_fighter_spritesheet»: { «type»: «spritesheet», «fuente»: «assets / images / personajes / fighter_m.png», «frame_width»: 24, «frame_height» : 32}, «level_tilemap»: { «type»: «tilemap», «fuente»: «activos / mapas / map1.json»}}, «grupos»: [ «jugadores», «enemy_spawners»], «mapa» : { «clave»: «level_tilemap», «baldosas»: [ «map_tileset», «male_fighter_spritesheet»]}} 1234567891011121314151617 { «activos»: { «map_tileset»: { «type»: «imagen», «fuente»:» assets / images / open_tileset.png «},» enemy_spawner_image «: {» type «:» imagen», «fuente»: «assets / images / enemy_spawner.png»}, «male_fighter_spritesheet»: { «type»: «spritesheet» , «fuente»: «assets / images / personajes / fighter_m.png», «frame_width»: 24, «frame_height»: 32}, «level_tilemap»: { «type»: «tilemap», «fuente»: «activos / mapas / map1.json «}},» grupos «: [» jugadores » «enemy_spawners»], «mapa»: { «clave»: «level_tilemap»,» tejas ets «: [» map_tileset», «male_fighter_spritesheet»]}}

        El código siguiente muestra la WorldState. El método “init” inicializa el motor de física y crea la forma de mapa en el archivo JSON. También crea un objeto “party_data”, que contiene las estadísticas de todas las unidades de jugador. Tenga en cuenta que este objeto se puede pasar como un parámetro, que se realiza después de cada batalla.
        var = RPG RPG || {}; RPG.WorldState = function () { «utilizar estricta»; Phaser.State.call (this); this.prefab_classes = { «jugador»: RPG.Player.prototype.constructor, «enemy_spawner»: RPG.EnemySpawner.prototype.constructor};}; RPG.WorldState.prototype = Object.create (Phaser.State.prototype); RPG .WorldState.prototype.constructor = RPG.WorldState; RPG.WorldState.prototype.init = function (level_data, extra_parameters) { «utilizar estricta»; tileset_index var; this.level_data = this.level_data || level_data; this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true; sistema de física this.game.physics.startSystem (Phaser.Physics.ARCADE) // empezar; this.game.physics.arcade.gravity.y = 0; // crear el mapa y establecer conjunto de baldosas this.map = this.game.add.tilemap (this.level_data.map.key); tileset_index = 0; this.map.tilesets.forEach (función (conjunto de baldosas) {this.map.addTilesetImage (tileset.name, this.level_data.map.tilesets [tileset_index]); tileset_index + = 1;}, this); // si no hay datos del partido está en los parámetros, inicializar con valores por defecto = this.party_data extra_parameters.party_data || { «Luchador»: { «type»: «player_unit», «posición»: { «x»: 250, «y»: 70}, «propiedades»: { «textura»: «male_fighter_spritesheet», «grupo»: » player_units», «marco»: 10, «estadísticas»: { «ataque»: 15, «defensa»: 5, «salud»: 100, «velocidad»: 15}}} «mago»: { «type»: «player_unit», «posición»: { «x»: 250, «y»: 150}, «propiedades»: { «textura»: «female_mage_spritesheet», «grupo»: «player_units», «marco»: 10, » estadísticas «: { «ataque»: 20, «defensa»: 2, «salud»: 100, «velocidad»: 15}}}}; si (extra_parameters.restart_position) {this.player_position = indefinido; }}; RPG.WorldState.prototype.create = function () { «utilizar estricta»; GROUP_NAME var, object_layer, collision_tiles; // crear capas de mapa this.layers = {}; this.map.layers.forEach (función (capa) {this.layers [layer.name] = this.map.createLayer (layer.name), si (layer.properties.collision) {// collision_tiles capa colisión = [] ; layer.data.forEach (function (data_row) {// encontrar azulejos usados ​​en la capa de data_row.forEach (function (teja) {// Comprobar si se trata de un índice de baldosas válido y no está ya en la lista si (teja. índice> 0 && collision_tiles.indexOf (tile.index) === -1) {collision_tiles.push (tile.index);}}, esto);}, this); this.map.setCollision (collision_tiles, es cierto, capa .name);}}, this); // cambiar el tamaño del mundo para ser el tamaño de las this.layers capa actuales [this.map.layer.name] .resizeWorld (); // crear grupos this.groups = {}; this.level_data.groups.forEach (function (GROUP_NAME) {this.groups [GROUP_NAME] = this.game.add.group ();}, this); this.prefabs = {}; para (object_layer en this.map.objects) {if (this.map.objects.hasOwnProperty (object_layer)) {// crear capa de objetos this.map.objects [object_layer] .forEach (this.create_object, este); }} // si venimos BattleState, mover el jugador a la posición anterior si (this.player_position) {this.prefabs.player.reset (this.player_position.x, this.player_position.y); }}; RPG.WorldState.prototype.create_object = función (objeto) { «utilizar estricta»; var object_y, la posición, prefabricada; // azulejos coordenadas se inicia en la esquina inferior izquierda object_y = (object.gid)? object.y – (this.map.tileHeight / 2): object.y + (object.height / 2); posición = { «x»: object.x + (this.map.tileHeight / 2), «y»: object_y}; // crear el objeto de acuerdo con su tipo si (this.prefab_classes.hasOwnProperty (object.type)) {= prefabricadas nuevas this.prefab_classes [object.type] (esto, object.name, la posición, object.properties); } this.prefabs [object.name] = prefabricada;}; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132varRPG = RPG || {}; RPG.WorldState = function () { «utilizar estricta»; Phaser.State.call (this); this.prefab_classes = {» jugador «: RPG.Player.prototype.constructor,» enemy_spawner «: RPG.EnemySpawner.prototype.constructor};}; RPG.WorldState.prototype = Object.create (Phaser.State.prototype); RPG.WorldState.prototype.constructor = RPG.WorldState; RPG.WorldState.prototype.init = function (level_data, extra_parameters) { «utilizar estricta»; vartileset_index; this.level_data = this.level_data || level_data; this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true; // iniciar systemthis.game.physics.startSystem la física (Phaser.Physics.ARCADE); this.game.physics .arcade.gravity.y = 0; // crear el mapa y conjunto tilesetthis.map = this.game.add.tilemap (this.level_data.map.key); tileset_index = 0; this.map.tilesets.forEach (function ( tileset) {this.map.addTilesetImage (tileset.name, this.level_data.map.tilesets [tileset_index]); tileset_index + = 1;}, this); // si no hay datos partido está en los parámetros, inicializarlo con valoresEsta predeterminados .party_data = extra_parameters.party_data || { «luchador»: { «type»: «player_unit», «posición»: { «x»: 250, «y»: 70}, «propiedades»: { «textura»:» male_fighter_spritesheet » «grupo»: «player_units», «marco»: 10, «estadísticas»: { «ataque»: 15, «defensa»: 5, «salud»: 100, «velocidad»: 15}}}» mage «: {» type «:» player_unit», «posición»: { «x»: 250, «Y»: 150}, «propiedades»: { «textura»: «female_mage_spritesheet», «grupo»: «player_units» , «marco»: 10, «estadísticas»: { «ataque»: 20, «defensa»: 2, «salud»: 100, «velocidad»: 15}}}}, y si (extra_parameters.restart_position) {this.player_position = indefinido;}}; RPG.WorldState.prototype.create = function () { «utilizar estricta»; vargroup_name, object_layer, collision_tiles; // crear mapa layersthis.la yers = {}; this.map.layers.forEach (función (capa) {this.layers [layer.name] = this.map.createLayer (layer.name), si (layer.properties.collision) {// colisión layercollision_tiles = []; layer.data.forEach (function (data_row) {// encontrar azulejos utilizados en la layerdata_row.forEach (function () {// baldosas de verificación si se trata de un índice de baldosas válido y no está ya en la listif ( tile.index> 0 && collision_tiles.indexOf (tile.index) === -1) {collision_tiles.push (tile.index);}}, esto);}, this); this.map.setCollision (collision_tiles, es cierto, layer.name);}}, this); // cambiar el tamaño del mundo para ser el tamaño de las layerthis.layers actuales [this.map.layer.name] .resizeWorld (); // crear groupsthis.groups = {}; this.level_data.groups.forEach (function (GROUP_NAME) {this.groups [GROUP_NAME] = this.game.add.group ();}, this); this.prefabs = {}; para (inthis.map.objects object_layer ) {if (this.map.objects.hasOwnProperty (object_layer)) {// crear objectsthis.map.objects capa [object_layer] .forEach (this.create_object, this);}} // si venimos BattleState, mueva el reproductor de t él positionif anterior (this.player_position) {this.prefabs.player.reset (this.player_position.x, this.player_position.y);}}; RPG.WorldState.prototype.create_object = función (objeto) { «utilizar estricta» ; varobject_y, la posición, prefabricada; // coordenadas azulejos aperturas en la parte inferior izquierda = cornerobject_y (object.gid) object.y- (this.map.tileHeight / 2):? object.y + (object.height / 2); posición = { «x»: object.x + (this.map.tileHeight / 2), «y»: object_y}; // crear objeto de acuerdo con su typeif (this.prefab_classes.hasOwnProperty (object.type)) {prefabricada = newthis .prefab_classes [object.type] (esto, object.name, la posición, object.properties);} this.prefabs [object.name] = prefabricada;};

        El “crear” método debe inicializar las capas del mapa y caza grupos y casas prefabricadas. Las capas estaban disponibles en el mapa de baldosa y ya se lee en el método “init”, por lo que sólo tenemos que iterar a través de ellos la creación de cada uno. Sin embargo, hay que detectar las capas de colisión (por medio de la propiedad agregado) y establecer todos sus azulejos como Collidable.

        Los grupos se crean fácilmente por iteración a través de las definidas en el archivo de nivel JSON. Sin embargo, para crear las casas prefabricadas debemos iterar a través de cada objeto en la capa de objetos y una instancia de la prefabricada correcta. La instanciación prefabricada se realiza en el método de “create_object”, que también ajustar la posición prefabricada debido a las diferentes coordenadas utilizadas por baldosa. Para crear una instancia adecuadamente cada prefabricada, definimos una propiedad “prefab_classes” en el constructor que mapea cada tipo prefabricada a su constructor, como lo hicimos en el BattleState. Al igual que en BattleState, esto es posible porque todas las casas prefabricadas tienen el mismo constructor.

        La última pieza de código en los “crear” restablece método al jugador a una posición guardada anteriormente. Esto debe hacerse porque el WorldState se puede llamar después de un BattleState, por lo que hay que tener guardado la posición anterior jugador. Si se pierde la batalla, hemos restablecido el juego usando el parámetro “reset_position” en el método “init”.

        Agregar el jugador prefabricada

        Ahora vamos a añadir el reproductor prefabricada, que se puede navegar a través del mundo. El código siguiente muestra el prefabricada jugador. En el constructor debe inicializar la velocidad de la marcha, animaciones, y el cuerpo de la física. Entonces, en el método de “actualización”, que es controlada por las teclas de flecha del teclado (obtenido a partir del objeto “cursores”). Nos movemos al jugador a una dirección dada, si se pulsa la tecla de flecha correspondiente y si el jugador no está moviendo en la dirección opuesta. Además, cuando el jugador se pone en movimiento a una dirección dada se reproduce la animación correspondiente. Cuando el jugador se para, la animación se detiene y reinicia su marco al cuadro detenido correcta, de acuerdo con su dirección de orientación.
        var = RPG RPG || {}; RPG.Player = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.walking_speed = + properties.walking_speed; this.animations.add ( «walking_down», [6, 7, 8], 10, true); this.animations.add ( «walking_left», [9, 10, 11], 10, true); this.animations.add ( «walking_right», [3, 4, 5], 10, true); this.animations.add ( «walking_up», [0, 1, 2], 10, true); this.stopped_frames = [7, 10, 4, 1, 7]; this.game_state.game.physics.arcade.enable (this); this.body.setSize (16, 16, 0, 8); this.body.collideWorldBounds = true; this.cursors = this.game_state.game.input.keyboard.createCursorKeys ();}; RPG.Player.prototype = Object.create (RPG.Prefab.prototype); RPG.Player.prototype.constructor = RPG.Player; RPG .Player.prototype.update = function () { «utilizar estricta»; this.game_state.game.physics.arcade.collide (esto, this.game_state.layers.collision_back); this.game_state.game.physics.arcade.collide (esto, this.game_state.layers.collision_front); si (this.cursors.left.isDown && this.body.velocity.x <= 0) {// mover hacia la izquierda this.body.velocity.x = -this.walking_speed; si (this.body.velocity.y === 0) {this.animations.play ( "walking_left"); }} Else if (this.cursors.right.isDown && this.body.velocity.x> = 0) {// movimiento correcto this.body.velocity.x = + this.walking_speed; si (this.body.velocity.y === 0) {this.animations.play ( «walking_right»); }} Else {this.body.velocity.x = 0; } If (this.cursors.up.isDown && this.body.velocity.y <= 0) {// sube this.body.velocity.y = -this.walking_speed; si (this.body.velocity.x === 0) {this.animations.play ( "walking_up"); }} Else if (this.cursors.down.isDown && this.body.velocity.y> = 0) {// baja this.body.velocity.y = + this.walking_speed; si (this.body.velocity.x === 0) {this.animations.play ( «walking_down»); }} Else {this.body.velocity.y = 0; } If (this.body.velocity.x === === 0 && this.body.velocity.y 0) {// parada actual this.animations.stop animación (); this.frame = this.stopped_frames [this.body.facing]; }}; 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970varRPG = RPG || {}; RPG.Player = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.Prefab.call (esto, game_state, nombre, posición, propiedades); esto. anchor.setTo (0,5); this.walking_speed = + properties.walking_speed; this.animations.add ( «walking_down», [6,7,8], 10, true); this.animations.add ( «walking_left», [ 9,10,11], 10, true); this.animations.add ( «walking_right», [3,4,5], 10, true); this.animations.add ( «walking_up», [0,1, 2], 10, true); this.stopped_frames = [7,10,4,1,7]; this.game_state.game.physics.arcade.enable (este); this.body.setSize (16,16,0 , 8); this.body.collideWorldBounds = true; this.cursors = this.game_state.game.input.keyboard.createCursorKeys ();}; RPG.Player.prototype = Object.create (RPG.Prefab.prototype); RPG .Player.prototype.constructor = RPG.Player; RPG.Player.prototype.update = function () { «utilizar estricta»; this.game_state.game.physics.arcade.collide (esto, this.game_state.layers.collis ion_back); this.game_state.game.physics.arcade.collide (esto, this.game_state.layers.collision_front), si (this.cursors.left.isDown && this.body.velocity.x <= 0) {// movimiento izquierda this.body.velocity.x = -this.walking_speed; si (this.body.velocity.y === 0) {this.animations.play ( "walking_left");}} elseif (this.cursors.right. isDown && this.body.velocity.x> = 0) {// movimiento correcto this.body.velocity.x = + this.walking_speed; si (this.body.velocity.y === 0) {this.animations.play ( «walking_right»);}} else {this.body.velocity.x = 0;} if (this.cursors.up.isDown && this.body.velocity.y <= 0) {// sube this.body. velocity.y = -this.walking_speed; si (this.body.velocity.x === 0) {this.animations.play ( "walking_up");}} elseif (this.cursors.down.isDown && this.body. velocity.y> = 0) {// moverse hacia abajo this.body.velocity.y = + this.walking_speed; si (this.body.velocity.x === 0) {this.animations.play ( «walking_down») }} else {this.body.velocity.y = 0;} if (this.body.velocity.x === === 0 && this.body.velocity.y 0) {// actual parada de animación this.animations .detener (); This.frame = this.stopped_frames [this.body.facing];}};

        Ya se puede probar a mover al jugador en el WorldState. Comprobar si las colisiones están funcionando correctamente. Tenga en cuenta que comprobamos por colisiones con todas las capas de colisión en el método de actualización.

        mundo

        Adición de la EnemySpawner prefabricada

        El EnemySpawner será una prefabricada inmuebles que se superpone con el jugador. Cuando se produzca tal solapamiento, se comprobará si hay posibles encuentros enemigos de acuerdo a sus probabilidades. Vamos a empezar por definir los encuentros enemigos en el archivo JSON nivel, como se muestra a continuación. Cada encuentro enemigo tiene una probabilidad y los datos enemigo.
        «enemy_encounters»: [{ «probabilidad»: 1, «enemy_data»: { «BAT1»: { «tipo»: «enemy_unit», «Posición»: { «x»: 100, «Y»: 90}, «propiedades «: { «textura»: «bat_spritesheet», «grupo»: «enemy_units», «estadísticas»: { «ataque»: 10, «defensa»: 1, «salud»: 30}}} » bat2″: { «type»: «enemy_unit», «posición»: { «x»: 100, «Y»: 170}, «propiedades»: { «textura»: «bat_spritesheet», «grupo»: «enemy_units», «estadísticas» : { «ataque»: 10, «defensa»: 1, «salud»: 30}}}}}] 1234567891011121314151617181920212223242526272829303132 «enemy_encounters»: [{ «probabilidad»: 1, «enemy_data»: { «bat1»: { «tipo «:» enemy_unit», «posición»: { «x»: 100, «y»: 90}, «propiedades»: { «textura»: «bat_spritesheet», «grupo»: «enemy_units», «estadísticas»: { «ataque»: 10, «defensa»: 1, «salud»: 30}}}, «bat2»: { «type»: «enemy_unit», «posición»: { «x»: 100, «y»: 170 }, «propiedades»: { «textura»: «bat_spritesheet», «grupo»: «enemy_units», «estadísticas»: { «ataque»: 10, «defensa»: 1, «salud»: 30}}}}} ]

        Ahora, podemos crear el EnemySpawner para comprobar si uno de los posibles encuentros. Su código se muestra a continuación. En el método de “actualización”, comprobamos los solapamientos con el jugador y llamamos al método “check_for_spawn” cuando un occure de solapamiento. Tenga en cuenta que, para llamar a este método sólo una vez por cada solapamiento utilizamos la variable “superposición”, y comprobar si hay desova sólo cuando era falsa.
        var = RPG RPG || {}; RPG.EnemySpawner = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.Prefab.call (esto, game_state, nombre, posición, propiedades); this.game_state.game.physics.arcade.enable (this); = This.body.immovable verdaderos; this.overlapping = true;}; RPG.EnemySpawner.prototype = Object.create (RPG.Prefab.prototype); RPG.EnemySpawner.prototype.constructor = RPG.EnemySpawner; RPG.EnemySpawner.prototype.update = function () { » utilizar estricta «; this.overlapping = this.game_state.game.physics.arcade.overlap (esto, this.game_state.groups.players, this.check_for_spawn, null, este);}; RPG.EnemySpawner.prototype.check_for_spawn = function () { » utilizar estricta «; var spawn_chance, encounter_index, enemy_encounter; // cheque por desovan sólo una vez por solapamiento si {spawn_chance = this.game_state.game.rnd.frac () (this.overlapping!); // cheque si la probabilidad enemigo desovar es menor que el número aleatorio generado para cada desove para (encounter_index = 0; encounter_index el método “check_for_spawn” genera un número aleatorio usando generador de datos aleatorios Phaser y lo compara con las probabilidades encuentros enemigos, la elección de la primera uno cuya probabilidad es mayor que el número generado. Tenga en cuenta que, para que esto funcione los encuentros deben ser ordenados en orden de probabilidad ascendente, dando prioridad a menos probables encuentros. Cuando se produce un encuentro, que llama BattleState con los datos correctos del enemigo y los datos de jugador del partido.

        Actualización BattleState

        Por último, hay que actualizar nuestra BattleState de trabajo con nuestras últimas modificaciones. En lugar de leer las unidades enemigas y jugador de un archivo JSON, que serán pasados ​​como parámetros en el método “init”. Entonces, sólo tenemos que iterar a través de ellos en el método de “crear” y creamos todas sus casas prefabricadas, utilizando el mismo método “create_prefab”. Tenga en cuenta que las unidades enemigas se almacenaron en los datos encuentros enemigos de la WorldState, mientras que las unidades del jugador se almacenan en la variable “party_data” de WorldState (mostrado antes).

        Ahora, tenemos que ir adecuadamente de nuevo a WorldState cuando se termine la batalla. En el método “next_turn”, antes de realizar el acto siguiente unidad, comprobamos si hay restantes unidades enemigas y jugador. Si no hay unidades enemigas restantes, que llamamos un método “end_battle” y, si no hay unidades restantes jugador que llamamos un método “game_over”.

        El método “end_battle” va a volver a actualizar el WorldState “party_data” para reflejar esta batalla. Por lo tanto, hay que recorrer todas las unidades jugador ahorro de sus estadísticas en la variable “party_data”. Por otro lado, el método “game_over” cambiará de nuevo a WorldState sin enviar ningún “party_data”, que restablecerla. Además, hay que contar la WorldState para reiniciar la posición del jugador, en lugar de mantener el último como se muestra en el código WorldState. El código siguiente muestra las modificaciones en el BattleState.
        var = RPG RPG || {}; RPG.BattleState = function () { «utilizar estricta»; Phaser.State.call (this); this.prefab_classes = { «fondo»: RPG.TilePrefab.prototype.constructor, «rectángulo»: RPG.Prefab.prototype.constructor, «player_unit»: RPG.PlayerUnit.prototype.constructor, «enemy_unit»: RPG.EnemyUnit.prototype .constructor}; this.TEXT_STYLE = {font: «14px Arial», relleno: «#FFFFFF»};}; RPG.BattleState.prototype = Object.create (Phaser.State.prototype); RPG.BattleState.prototype.constructor = RPG.BattleState ; RPG.BattleState.prototype.init = function (level_data, extra_parameters) { «utilizar estricta»; this.level_data = level_data; this.enemy_data = extra_parameters.enemy_data; this.party_data = extra_parameters.party_data; this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true;}; RPG.BattleState.prototype.create = function () { «utilizar estricta»; var GROUP_NAME, prefab_name, player_unit_name, enemy_unit_name; // crear grupos this.groups = {}; this.level_data.groups.forEach (function (GROUP_NAME) {this.groups [GROUP_NAME] = this.game.add.group ();}, this); // crear prefabricadas this.prefabs = {}; para (prefab_name en this.level_data.prefabs) {if (this.level_data.prefabs.hasOwnProperty (prefab_name)) {// crear prefabricada this.create_prefab (prefab_name, this.level_data.prefabs [prefab_name]); }} // crear unidades enemigas para (enemy_unit_name en this.enemy_data) {if (this.enemy_data.hasOwnProperty (enemy_unit_name)) {// crear unidades enemigas this.create_prefab (enemy_unit_name, this.enemy_data [enemy_unit_name]); }} // crear unidades de jugador para (player_unit_name en this.party_data) {if (this.party_data.hasOwnProperty (player_unit_name)) {// crear unidades de jugador this.create_prefab (player_unit_name, this.party_data [player_unit_name]); }} This.init_hud (); this.units = []; this.units = this.units.concat (this.groups.player_units.children); this.units = this.units.concat (this.groups.enemy_units.children); this.next_turn ();}; RPG.BattleState.prototype.next_turn = function () { «utilizar estricta»; // si todas las unidades enemigas están muertos, volver al estado mundo si (this.groups.enemy_units.countLiving () === 0) {this.end_battle (); } // si todas las unidades jugador están muertos, reiniciar el juego si (this.groups.player_units.countLiving () === 0) {this.game_over (); } // toma la siguiente unidad this.current_unit = this.units.shift (); // si la unidad está vivo, actúa, de lo contrario pasa al siguiente turno si (this.current_unit.alive) {this.current_unit.act (); this.units.push (this.current_unit); } Else {this.next_turn (); }}; RPG.BattleState.prototype.game_over = function () { «utilizar estricta»; // volver a reiniciar el WorldState this.game.state.start la posición del jugador ( «BootState», verdadero, falso, «activos / niveles / level1.json», «WorldState», {restart_position: true});}; RPG .BattleState.prototype.end_battle = function () { «utilizar estricta»; // Guardar this.groups.player_units.forEach salud actual partido (function (player_unit) {[this.party_data player_unit.name] .properties.stats = player_unit.stats;}, this); // volver a WorldState con el this.game.state.start de datos actual partido ( «BootState», verdadero, falso, «activos / niveles / level1.json», «WorldState», {party_data: this.party_data}); }; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112varRPG = || RPG {}; RPG.BattleState = function () { «use strict»; Phaser.State.call (este); this.prefab_classes = { «fondo»: RPG.TilePrefab.prototype.constructor» rectángulo «: RPG.Prefab.prototype.constructor,» player_unit «: RPG.PlayerUnit.prototype.constructor,» enemy_unit «: RPG.EnemyUnit.prototype.constructor}; this.TEXT_STYLE = {font:» 14px Arial», relleno: «#FFFFFF»};}; RPG.BattleState.prototype = Object.create (Phaser.State.prototype); RPG.BattleState.prototype.constructor = RPG.BattleState; RPG.BattleState.prototype.init = function (level_data, extra_parameters ) { «utilizar estricta»; this.level_data = level_data; this.enemy_data = ext ra_parameters.enemy_data; this.party_data = extra_parameters.party_data; this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true;}; RPG.BattleState.prototype.create = function () { «utilizar estricta»; vargroup_name, prefab_name, player_unit_name, enemy_unit_name; // crear groupsthis.groups = {}; this.level_data.groups.forEach (function (GROUP_NAME) {this.groups [GROUP_NAME] = this.game .add.group ();}, this); // crear prefabsthis.prefabs = {}; para (inthis.level_data.prefabs prefab_name) {if (this.level_data.prefabs.hasOwnProperty (prefab_name)) {// crear prefabthis .create_prefab (prefab_name, this.level_data.prefabs [prefab_name]);}} // crear enemigo unitsfor (inthis.enemy_data enemy_unit_name) {if (this.enemy_data.hasOwnProperty (enemy_unit_name)) {// crear enemigo unitsthis.create_prefab (enemy_unit_name , this.enemy_data [enemy_unit_name]);}} // crear jugador unitsfor (inthis.party_data player_unit_name) {if (this.party_data.hasOwnProperty (player_unit_name)) {// crear juego er unitsthis.create_prefab (player_unit_name, this.party_data [player_unit_name]);}} this.init_hud (); this.units = []; this.units = this.units.concat (this.groups.player_units.children); esta .units = this.units.concat (this.groups.enemy_units.children); this.next_turn ();}; RPG.BattleState.prototype.next_turn = function () { «uso estricto»; // si todas las unidades enemigas son muerto, volver a la stateif mundo (this.groups.enemy_units.countLiving () === 0) {this.end_battle ();} // si todas las unidades jugador están muertos, reinicie los gameif (this.groups.player_units. countLiving () === 0) {this.game_over ();} // toma el siguiente unitthis.current_unit = this.units.shift (); // si la unidad está vivo, actúa, de lo contrario pasa a la siguiente turnif (this.current_unit.alive) {this.current_unit.act (); this.units.push (this.current_unit);} else {this.next_turn ();}}; RPG.BattleState.prototype.game_over = function () { «uso estricto»; // volver a reiniciar el jugador WorldState positionthis.game.state.start ( «BootState», verdadero, falso, «activos / niveles / level1.json», «WorldState», {reinicio _position: true});}; RPG.BattleState.prototype.end_battle = function () { «use strict»; // Guardar healththis.groups.player_units.forEach actual partido (function (player_unit) {this.party_data [player_unit.name .properties.stats] = player_unit.stats;}, this); // volver a WorldState con el datathis.game.state.start actual partido ( «BootState», verdadero, falso, «activos / niveles / level1.json» «WorldState», {party_data: this.party_data}.);};

        a estas alturas, ya se puede intentar jugar con los EnemySpawners

        enemy_spawner

        unidades Teniendo en cuenta la velocidad de las vueltas

        En este tutorial, cada unidad tendrá una estadística de velocidad, que se utilizará para calcular la próxima vez que la unidad actuará. El código siguiente muestra las modificaciones en la unidad prefabricada, y cómo se calcula el siguiente acto a su vez basado en el turno actual y su velocidad. Tenga en cuenta que he utilizado una regla arbitraria para calcular el siguiente turno, y se puede usar el que más te encuentra.
        var = RPG RPG || {}; RPG.Unit = función (game_state, nombre, posición, propiedades) { «uso estricto»; RPG.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.stats = Object.create (properties.stats); this.attacked_animation = this.game_state.game.add.tween (this); this.attacked_animation.to ({tinte: 0xFF0000}, 200); this.attacked_animation.onComplete.add (this.restore_tint, este); this.act_turn = 0;}; RPG.Unit.prototype = Object.create (RPG.Prefab.prototype); RPG.Unit.prototype.constructor = RPG.Unit; RPG.Unit.prototype.calculate_act_turn = function (current_turn) { «utilizar estricta»; // calcular el acto a su vez basado en la velocidad de la unidad this.act_turn = current_turn + Math.ceil (100 / this.stats.speed);} 12345678910111213141516171819202122232425varRPG = RPG || {}; RPG.Unit = function (game_state, nombre, posición , propiedades) { «uso estricto»; RPG.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.stats = Object.create (properties.stats); esto. attacked_animation=this.game_state.game.add.tween(this);this.attacked_animation.to({tint:0xFF0000},200);this.attacked_animation.onComplete.add(this.restore_tint,this);this.act_turn=0 ;};RPG.Unit.prototype=Object.create(RPG.Prefab.prototype);RPG.Unit.prototype.constructor=RPG.Unit;RPG.Unit.prototype.calculate_act_turn=function(current_turn){«use strict»; // calculate the act turn based on the unit speedthis.act_turn=current_turn+Math.ceil(100/this.stats.speed);}

        Now, we will change the BattleState to store the units in a priority queue, instead of an array. A priority queue is a data structure where all elements are always sorted, given a sorting criteria (if you’re not familiar with priority queues, check this wikipedia link). In our case, the sorting criteria will be the unit next acting turn, which means the first unit from the queue is the one that will act earlier. Since the priority queue is a well known data structure, we are going to use the implementation provided by Adam Hooper instead of creating our own.

        The code below shows the modifications in the BattleState to use the priority queue. First, in the end of the “create” method we initialize “units” as a priority queue which compares the units act turn, and add all units to the queue, calculating their first acting turns. Then, in the “next_turn” method, we must update the current unit act turn before adding it to the “units” queue again, so it will be put in the correct position.

        RPG.BattleState.prototype.create = function () { «use strict»; var group_name, prefab_name, player_unit_name, enemy_unit_name; // crear grupos this.groups = {}; this.level_data.groups.forEach (function (GROUP_NAME) {this.groups [GROUP_NAME] = this.game.add.group ();}, this); // crear prefabricadas this.prefabs = {}; para (prefab_name en this.level_data.prefabs) {if (this.level_data.prefabs.hasOwnProperty (prefab_name)) {// crear prefabricada this.create_prefab (prefab_name, this.level_data.prefabs [prefab_name]); } } // create enemy units for (enemy_unit_name in this.enemy_data) { if (this.enemy_data.hasOwnProperty(enemy_unit_name)) { // create enemy units this.create_prefab(enemy_unit_name, this.enemy_data[enemy_unit_name]); } } // create player units for (player_unit_name in this.party_data) { if (this.party_data.hasOwnProperty(player_unit_name)) { // create player units this.create_prefab(player_unit_name, this.party_data[player_unit_name]); } } this.init_hud(); // store units in a priority queue which compares the units act turn this.units = new PriorityQueue({comparator: function (unit_a, unit_b) { return unit_a.act_turn – unit_b.act_turn; }}); this.groups.player_units.forEach(function (unit) { unit.calculate_act_turn(0); this.units.queue(unit); }, this); this.groups.enemy_units.forEach(function (unit) { unit.calculate_act_turn(0); this.units.queue(unit); }, this); this.next_turn();};RPG.BattleState.prototype.next_turn = function () { «use strict»; // if all enemy units are dead, go back to the world state if (this.groups.enemy_units.countLiving() === 0) { this.end_battle(); } // if all player units are dead, restart the game if (this.groups.player_units.countLiving() === 0) { this.game_over(); } // takes the next unit this.current_unit = this.units.dequeue(); // if the unit is alive, it acts, otherwise goes to the next turn if (this.current_unit.alive) { this.current_unit.act(); this.current_unit.calculate_act_turn(this.current_unit.act_turn); this.units.queue(this.current_unit); } else { this.next_turn(); }};12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576RPG.BattleState.prototype.create=function(){«use strict»;vargroup_name,prefab_name,player_unit_name,enemy_unit_name;// create groupsthis.groups={};this.level_data.groups.forEach(function(group_name){this.groups[group_name]=this.game.add.group();},this);// create prefabsthis.prefabs={};for(prefab_name inthis.level_data.prefabs){if(this.level_data.prefabs.hasOwnProperty(prefab_name)){// create prefabthis.create_prefab(prefab_name,this.level_data.prefabs[prefab_name]);}}// create enemy unitsfor(enemy_unit_name inthis.enemy_data){if(this.enemy_data.hasOwnProperty(enemy_unit_name)){// create enemy unitsthis.create_prefab(enemy_unit_name,this.enemy_data[enemy_unit_name]);}}// create player unitsfor(player_unit_name inthis.party_data){if(this.party_data.hasOwnProperty(player_unit_name)){// create player unitsthis.create_prefab(player_unit_name,this. party_data[player_unit_name]);}}this.init_hud();// store units in a priority queue which compares the units act turnthis.units=newPriorityQueue({comparator:function(unit_a,unit_b){returnunit_a.act_turn-unit_b.act_turn;}});this.groups.player_units.forEach(function(unit){unit.calculate_act_turn(0);this.units.queue(unit);},this);this.groups.enemy_units.forEach(function(unit){unit.calculate_act_turn(0);this.units.queue(unit);},this);this.next_turn();};RPG.BattleState.prototype.next_turn=function(){«use strict»;// if all enemy units are dead, go back to the world stateif(this.groups.enemy_units.countLiving()===0){this.end_battle();}// if all player units are dead, restart the gameif(this.groups.player_units.countLiving()===0){this.game_over();}// takes the next unitthis.current_unit=this.units.dequeue();// if the unit is alive, it acts, otherwise goes to the next turnif(this.current_unit.alive){this.current_unit.act();this.current_unit.calculate_act_turn(this.current_unit.act_turn);this.uni ts.queue(this.current_unit);}else{this.next_turn();}};

        Now, you can already try setting some speed values and see if everything is working correctly. Below is the final enemy encounters and party data I used.

        «enemy_encounters»: [ {«probability»: 0.3, «enemy_data»: { «lizard1»: { «type»: «enemy_unit», «position»: {«x»: 100, «y»: 100}, «properties»: { «texture»: «lizard_spritesheet», «group»: «enemy_units», «stats»: { «attack»: 30, «defense»: 10, «health»: 50, «speed»: 15 } } } } }, {«probability»: 0.5, «enemy_data»: { «bat1»: { «type»: «enemy_unit», «position»: {«x»: 100, «y»: 90}, «properties»: { «texture»: «bat_spritesheet», «group»: «enemy_units», «stats»: { «attack»: 10, «defense»: 1, «health»: 30, «speed»: 20 } } }, «bat2»: { «type»: «enemy_unit», «position»: {«x»: 100, «y»: 170}, «properties»: { «texture»: «bat_spritesheet», «group»: «enemy_units», «stats»: { «attack»: 10, «defense»: 1, «health»: 30, «speed»: 20 } } } } }, {«probability»: 1.0, «enemy_data»: { «scorpion1»: { «type»: «enemy_unit», «position»: {«x»: 100, «y»: 50}, «properties»: { «texture»: «scorpion_spritesheet», «group»: «enemy_units», «stats»: { «attack»: 15, «defense»: 1, «health»: 20, «speed»: 10 } } }, «scorpion2»: { «type»: «enemy_unit», «position»: {«x»: 100, «y»: 100}, «properties»: { «texture»: «scorpion_spritesheet», «group»: «enemy_units», «stats»: { «attack»: 15, «defense»: 1, «health»: 20, «speed»: 10 } } }, «scorpion3»: { «type»: «enemy_unit», «position»: {«x»: 100, «y»: 150}, «properties»: { «texture»: «scorpion_spritesheet», «group»: «enemy_units», «stats»: { «attack»: 15, «defense»: 1, «health»: 20, «speed»: 10 } } } } } ]this.party_data = extra_parameters.party_data || { «fighter»: { «type»: «player_unit», «position»: {«x»: 250, «y»: 50}, «properties»: { «texture»: «male_fighter_spritesheet», «group»: «player_units», «frame»: 10, «stats»: { «attack»: 15, «defense»: 5, «health»: 100, «speed»: 15 } } }, «mage»: { «type»: «player_unit», «position»: {«x»: 250, «y»: 100}, «properties»: { «texture»: «female_mage_spritesheet», «group»: «player_units», «frame»: 10, «stats»: { «attack»: 20, «defense»: 2, «health»: 100, «speed»: 10 } } }, «ranger»: { «type»: «player_unit», «position»: {«x»: 250, «y»: 150}, «properties»: { «texture»: «female_ranger_spritesheet», «group»: «player_units», «frame»: 10, «stats»: { «attack»: 10, «defense»: 3, «health»: 100, «speed»: 20 } } } };123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146″enemy_encounters»:[{«probability»:0.3,»enemy_data»:{«lizard1»:{«type»:»enemy_unit»,»position»:{«x»:100,»y»:100},»properties»:{«texture»:»lizard_spritesheet»,»group»:»enemy_units»,»stats»:{«attack»:30,»defense»:10,»health»:50,»speed»:15}}}}},{«probability»:0.5,»enemy_data»:{«bat1»:{«type»:»enemy_unit»,»position»:{«x»:100,»y»:90},»properties»:{«texture»:»bat_spritesheet»,»group»:»enemy_units»,»stats»:{«attack»:10,»defense»:1,»health»:30,»speed»:20}}},»bat2″:{«type»:»enemy_unit»,»position»:{«x»:100,»y»:170},»properties»:{«texture»:»bat_spritesheet»,»group»:»enemy_units»,»stats»:{«attack»:10,»defense»:1,»health»:30,»speed»:20}}}}},{«probability»:1.0,»enemy_data»:{«scorpion1»:{«type»:»enemy_unit»,»position»:{«x»:100,»y»:50},»properties»:{«texture»:»scorpion_spritesheet»,»group»:»enemy_units»,»stats»:{«attack»:15,»defense»:1,»health»:20,»speed»:10}}},»scorpion2″:{«type»:»enemy_unit»,»position»:{«x»:100,»y»:100},»properties»:{«texture»:»scorpion_spritesheet»,»group»:»enemy_units»,»stats»:{«attack»:15,»defense»:1,»health»:20,»speed»:10}}},»scorpion3″:{«type»:»enemy_unit»,»position»:{«x»:100,»y»:150},»properties»:{«texture»:»scorpion_spritesheet»,»group»:»enemy_units»,»stats»:{«attack»:15,»defense»:1,»health»:20,»speed»:10}}}}}]this.party_data=extra_parameters.party_data||{«fighter»:{«type»:»player_unit»,»position»:{«x»:250,»y»:50},»properties»:{«texture»:»male_fighter_spritesheet»,»group»:»player_units»,»frame»:10,»stats»:{«attack»:15,»defense»:5,»health»:100,»speed»:15}}},»mage»:{«type»:»player_unit»,»position»:{«x»:250,»y»:100},»properties»:{«texture»:»female_mage_spritesheet»,»group»:»player_units»,»frame»:10,»stats»:{«attack»:20,»defense»:2,»health»:100,»speed»:10}}},»ranger»:{«type»:»player_unit»,»position»:{«x»:250,»y»:150},»properties»:{«texture»:»female_ranger_spritesheet»,»group»:»player_units»,»frame»:10,»stats»:{«attack»:10,»defense»:3,»health»:100,»speed»:20}}}};

        And now we finished this tutorial. In the next one we are going to add different actions during the battle, such as using magic and items. In addition, the player units will receive experience after each battle, being able to pass levels.

        Mensajes relacionados

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *