En esta serie de tutoriales, vamos a crear un juego de rol por turnos, tales como la serie Final Fantasy. En primer lugar, vamos a crear el estado de batalla para nuestro juego. A continuación, vamos a crear un estado para el mundo, que cambiará al estado de batalla cada vez que se encuentra un enemigo. Por último, vamos a añadir contenido, como artículos, niveles, NPC y puntos de guardado. En este primer tutorial voy a cubrir el siguiente contenido:


  • Crear un estado de batalla que se llamará durante el juego
  • Creación de un menú para mostrar las unidades de los jugadores y enemigos
  • Creación de un simple juego por turnos, donde cada unidad actúa una vez

    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

      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]dex.ru). 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í.

      arranque y carga Unidos

      creará arranque y carga de estados para cargar todos los activos del juego antes de que comience. Todo este contenido se lee de un archivo JSON, como se muestra a continuación. Tenga en cuenta que en este archivo tenemos que definir el estado de los activos, los grupos y las casas prefabricadas.
      { «activos»: { «rectangle_image»: { «type»: «imagen», «fuente»: «assets / images / rectangle.png»}, «grass_tile_image»: { «type»: «imagen», «fuente» : «assets / images / grass_tile.png»}, «male_fighter_spritesheet»: { «type»: «spritesheet», «fuente»: «assets / images / personajes / fighter_m.png», «frame_width»: 24, «frame_height» : 32}, «female_mage_spritesheet»: { «type»: «spritesheet», «fuente»: «assets / images / personajes / mage_f.png», «frame_width»: 24, «frame_height»: 32}, «bat_spritesheet»: { «type»: «spritesheet», «fuente»: «activos / images / monstruos / bat.png», «frame_width»: 128, «frame_height»: 128}}, «grupos»: [ «player_units «de fondo» «, «enemy_units», «HUD»], «prefabricados»: { «fondo»: { «tipo»: «fondo», «posición»: { «x»: 0, «Y»: 0}, «propiedades» : { «textura»: «grass_tile_image», «grupo»: «fondo», «ancho»: 320, «altura»: 320}}, «enemy_rectangle»: { «type»: «rectángulo», «posición»: { «x»: 0 «y»: 200}, «propiedades»: { «textura»: «rectangle_image», «grupo»: «HUD», «escala»: { «x»: 0,3, «y»: 1}}}, «action_rectangle»: { «type»: «rectángulo», «posición»: { «x»: 96, «y»: 200}, «propiedades»: { «textura»: «rectangle_image», » grupo «: «HUD», «escala»: {» x «: 0,3, «y»: 1}}}, «player_rectangle»: { «type»: «rectángulo», «posición»: {» x «: 192 , «y»: 200}, «propiedades»: { «textura»: «rectangle_image», «grupo»: «HUD», «escala»: { «x»: 0,4, «y»: 1}}}, » combate «: { «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}}}, «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}}}, «bat1»: { «type»: «enemy_unit», «posición»: { «x»: 100, «Y»: 90}, «propiedades «: { «textura»: «bat_spritesheet», «grupo»: «enemy_units», «estadísticas»: { «ataque»: 10, «defensa»: 1, «salud»: 30}}} » bat2″: { «Typ e «: «enemy_unit», «posición»: {» x «: 100, «y»: 170}, «propiedades»: { «textura»: «bat_spritesheet», «grupo»: «enemy_units», «estadísticas»: { «ataque»: 10, «defensa»: 1, «salud»: 30}}}}} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 { «activos»: { «rectangle_image»: { «type»: «imagen», «fuente»: «activos / images / rectangle.png «},» grass_tile_image «: {» type «:» imagen » «fuente»: «assets / images / grass_tile.png»}, «male_fighter_spritesheet»: { «type»: «spritesheet»,» fuente «:» assets / images / personajes / fighter_m.png » «frame_width»: 24, «frame_height»: 32}, «female_mage_spritesheet»: { «type»: «spritesheet», «fuente»:» assets / images / personajes / mage_f.png » «frame_width»: 24, «frame_height»: 32}, «bat_spritesheet»: { «type»: «spritesheet», «fuente»: «assets / images / monstruos / bat.png»,» frame_width «: 128,» frame_height «: 128}},» grupos «: [» aislado » «», «player_units enemy_units»,» h ud «],» prefabricados «: {» fondo «: {» type «:» fondo » «posición»: { «x»: 0 » y»: 0}, «propiedades»: { «textura»:» grass_tile_image » «grupo»: «fondo», «ancho»: 320, «altura»: 320}}, «enemy_rectangle»: { «type»: «rectángulo», «posición»: { «x»: 0″ y «: 200},» propiedades «: {» textura «:» rectangle_image», «grupo»: «HUD», «escala»: { «x»: 0,3, «y»: 1}}}, «action_rectangle» : { «type»: «rectángulo», «posición»: { «x»: 96, «y»: 200}, «propiedades»: { «textura»: «rectangle_image», «grupo»: «HUD»,» escala «: {» x «: 0,3,» y «: 1}}},» player_rectangle «: {» type «:» rectángulo», «posición»: { «x»: 192, «y»: 200}, «propiedades»: { «textura»: «rectangle_image», «grupo»: «HUD», «escala»: { «x»: 0,4, «y»: 1}}}, «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}}}» 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}}} «, ba t1 «: {» type «:» 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}}}}}

      se muestra el código BootState continuación. Sólo se cargará el archivo JSON y llamar a la LoadingState.
      var = RPG RPG || {}; RPG.BootState = function () { «utilizar estricta»; Phaser.State.call (this);}; RPG.BootState.prototype = Object.create (Phaser.State.prototype); RPG.BootState.prototype.constructor = RPG.BootState; RPG.BootState.prototype.init = function ( level_file, next_state) { «utilizar estricta»; this.level_file = level_file; this.next_state = next_state;}; RPG.BootState.prototype.preload = function () { «utilizar estricta»; this.load.text ( «level1», this.level_file);}; RPG.BootState.prototype.create = function () { «utilizar estricta»; level_text var, level_data; level_text = this.game.cache.getText ( «level1»); level_data = JSON.parse (level_text); this.game.state.start ( «LoadingState», verdadero, falso, level_data, this.next_state);}; 12345678910111213141516171819202122232425262728varRPG = RPG || {}; RPG.BootState = function () { «uso estricto»; Phaser.State. llamada (esto);}; RPG.BootState.prototype = Object.create (Phaser.State.prototype); RPG.BootState.prototype.constructor = RPG.BootState; RPG.BootState.prototype.init = function (level_file, next_state) { «utilizar estricta»; this.level_file = level_file; this.next_state = next_state;}; RPG.BootState.prototype.preload = function () { «utilizar estricta»; this.load.text ( «level1», this.level_file );}; RPG.BootState.prototype.create = function () { «utilizar estricta»; varlevel_text, level_data; level_text = this.game.cache.getText ( «level1»); level_data = JSON.parse (level_text); esta .game.state.start ( «LoadingState», verdadero, falso, level_data, this.next_state);};

      El LoadingState es responsable de cargar todos los elementos necesarios. Para hacer eso, se leerá los activos desde el archivo JSON y cargarlos en consecuencia. El código para LoadingState se muestra a continuación. Tenga en cuenta que los “precarga” cargas método del activo Phaser correcto según el tipo de activos. Al final, se llamará al siguiente estado (en nuestro caso, BattleState).
      var = RPG RPG || {}; RPG.LoadingState = function () { «utilizar estricta»; Phaser.State.call (this);}; RPG.LoadingState.prototype = Object.create (Phaser.State.prototype); RPG.LoadingState.prototype.constructor = RPG.LoadingState; RPG.LoadingState.prototype.init = function ( level_data, next_state) { «utilizar estricta»; this.level_data = level_data; this.next_state = next_state;}; RPG.LoadingState.prototype.preload = function () { «utilizar estricta»; var activos, asset_loader, asset_key, activo; = activos this.level_data.assets; para (asset_key en activos) {// activos de carga de acuerdo con la clave activo si (assets.hasOwnProperty (asset_key)) {activos = activos [asset_key]; interruptor (asset.type) {case «imagen»: this.load.image (asset_key, asset.source); descanso; caso «spritesheet»: this.load.spritesheet (asset_key, asset.source, asset.frame_width, asset.frame_height, asset.frames, asset.margin, asset.spacing); descanso; caso «tilemap»: this.load.tilemap (asset_key, asset.source, null, Phaser.Tilemap.TILED_JSON); descanso; }}}}; RPG.LoadingState.prototype.create = function () { «utilizar estricta»; this.game.state.start (this.next_state, verdadero, falso, this.level_data);}; 123456789101112131415161718192021222324252627282930313233343536373839404142varRPG = RPG || {}; RPG.LoadingState = function () { «utilizar estricta»; Phaser.State.call ( este);}; RPG.LoadingState.prototype = Object.create (Phaser.State.prototype); RPG.LoadingState.prototype.constructor = RPG.LoadingState; RPG.LoadingState.prototype.init = function (level_data, next_state) {» uso estricto «; this.level_data = level_data; this.next_state = next_state;}; RPG.LoadingState.prototype.preload = function () {» utilizar estricta «; varassets, asset_loader, asset_key, activo; activos = this.level_data.assets ; para (inassets asset_key) {// activos de carga de acuerdo con keyif activo (assets.hasOwnProperty (asset_key)) {activos = activos [asset_key]; conmutador (asset.type) {case «imagen»: this.load.image (asset_key , asset.source); break; caso «spritesheet»: this.load.spritesheet (asset_key, asset.source, asset.frame_width, asset.frame_height, asset.frames, asset.margin, asset.spacing); break; caso» tilemap «: this.load.tilemap (asset_key, como set.source, null, Phaser.Tilemap.TILED_JSON); break;}}}}; RPG.LoadingState.prototype.create = function () { «utilizar estricta»; this.game.state.start (this.next_state, la verdadera , falso, this.level_data);};

      Creación del estado de batalla

      Vamos a crear un estado de batalla que mostrará el jugador del partido, las unidades enemigas y un menú por lo que el jugador puede elegir qué enemigo para atacar. Inicialmente, nos limitaremos a crear la estructura básica, mostrando las unidades sin los menús. Además, en este tutorial todas las unidades serán leídos desde el archivo JSON (ver imagen superior). En los próximos tutoriales, pasaremos estos datos como un parámetro en el método “init”.

      El código BattleState se muestra a continuación. El método “init” sólo guarda los datos de nivel y establece la escala de juego. Los “crear” método se inicia mediante la creación de todos los grupos y luego crear las casas prefabricadas (grupos y casas prefabricadas se leen desde el archivo JSON).
      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) { «utilizar estricta»; this.level_data = level_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]); }}}; RPG.BattleState.prototype.create_prefab = function (prefab_name, prefab_data) { «utilizar estricta»; prefabricada var; // crear el objeto de acuerdo con su tipo si (this.prefab_classes.hasOwnProperty (prefab_data.type)) {prefabricadas = new this.prefab_classes [prefab_data.type] (Esto, prefab_name, prefab_data.position, prefab_data.properties); }}; 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556varRPG = || 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) { «uso estricto»; this.level_data = level_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.group s [GROUP_NAME] = this.game.add.group ();}, esto); // 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]);}}}; RPG.BattleState.prototype.create_prefab = function (prefab_name, prefab_data) { «utilizar estricta»; varprefab; / / crear el objeto de acuerdo con su typeif (this.prefab_classes.hasOwnProperty (prefab_data.type)) {prefabricada = newthis.prefab_classes [prefab_data.type] (esto, prefab_name, prefab_data.position, prefab_data.properties);}};

      para cada prefabricada, el método de “create_prefab” será una instancia de la correcta según prefabricada para su tipo. Dos cosas son necesarias para que al trabajo:


      1. Todas las casas prefabricadas debe tener el mismo constructor. Para ello, creamos una clase genérica prefabricada (que se muestra a continuación) que todas las casas prefabricadas deben extenderse.
      2. Se debe tener una propiedad mapear cada tipo prefabricada a su constructor. Esta propiedad se define en el BattleState constructor.Since todas las unidades se declaran en el archivo JSON, a estas alturas ya aparecerán en nuestra BattleState, como se muestra en la figura siguiente. Intente ejecutar el juego ahora, para verlo. Se pueden crear clases vacías para las unidades, sólo para hacer que funcione por ahora.

        battle_state

        Creación del estado de batalla HUD

        Ahora, vamos a añadir tres menús para nuestra BattleState:


        1. Reproductor menú de unidades: mostrará las unidades de los jugadores y su salud
        2. menú de acciones: se muestran las acciones disponibles del reproductor
        3. Enemigo menú de unidades: mostrará las unidades enemigas para que el jugador puede elegir la que desee attackBefore hacer esto, vamos a crear un menú y casas prefabricadas MenuItem, como se muestra a continuación. El prefabricada menú tiene una gran variedad de MenuItems y permite navegar a través de ellos cuando está activado. Puesto que vamos a tener más de un menú, al mismo tiempo, necesitamos métodos para activar y desactivar cuando sea necesario. El método permitirá añadirá devoluciones de llamada al teclado para permitir la navegación por los menús. Los otros métodos de menú se utilizarán más adelante en este tutorial.

          var = RPG RPG || {}; RPG.Menu = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; live_index var, la vida; RPG.Prefab.call (esto, game_state, nombre, posición, propiedades); = This.visible FALSE; this.menu_items = properties.menu_items; this.current_item_index = 0;}; RPG.Menu.prototype = Object.create (RPG.Prefab.prototype); RPG.Menu.prototype.constructor = RPG.Menu; RPG.Menu.prototype.process_input = function (event) { «utilizar estricta»; interruptor (event.keyCode) {case Phaser.Keyboard.UP: si (this.current_item_index> 0) {// navegar a this.move_selection anterior artículo (this.current_item_index – 1); } rotura; caso Phaser.Keyboard.DOWN: if (this.current_item_index 0) {this.menu_items [this.current_item_index] .selection_over (); } this.game_state.game.input.keyboard.addCallbacks (esto, this.process_input);}; RPG.Menu.prototype.disable = function () { «utilizar estricta»; si (this.menu_items.length> 0) {this.menu_items [this.current_item_index] .selection_out (); } This.current_item_index = 0;}; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384varRPG = RPG || {}; RPG.Menu = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; varlive_index, la vida; RPG.Prefab.call (esto, game_state , nombre, posición, propiedades); this.visible = false; this.menu_items = properties.menu_items; this.current_item_index = 0;}; RPG.Menu.prototype = Object.create (RPG.Prefab.prototype); RPG.Menu .prototype.constructor = RPG.Menu; RPG.Menu.prototype.process_input = function (evento) { «utilizar estricta»; conmutador (event.keyCode) {casePhaser.Keyboard.UP: if (this.current_item_index> 0) {/ / navegar a itemthis.move_selection anterior (this.current_item_index-1);} break; casePhaser.Keyboard.DOWN: if (this.current_item_index 0) {this.menu_items [this.current_item_index] .selection_over ();} this.game_state.game.input.keyboard.addCallbacks (esto, this.process_input);} ; RPG.Menu.prototype.disable = function () { «utilizar estricta», si (esto. menu_items.length> 0) {this.menu_items [this.current_item_index] .selection_out ();} this.current_item_index = 0;};

          El MenuItem prefabricada únicamente tiene que implementar el “selection_over” y métodos “selection_out” (llamado por menú). Cuando un Menultem ha terminado la selección, que va a cambiar su color. Tenga en cuenta que se extiende Menultem TextPrefab en lugar de paneles prefabricados. Esta clase es similar a la clase prefabricada genérica, sino que se extiende Phaser.Text en lugar de Phaser.Sprite, como se muestra a continuación. Una cosa importante a notar es que todos los textos tendrán el mismo estilo de texto, que se define como “TEXT_STYLE” en BattleState. Sin embargo, hay que utilizar Object.create () para utilizar una copia del mismo al crear cada TextPrefab, de lo contrario todos ellos tendrían una referencia para el mismo objeto.
          var = RPG RPG || {}; RPG.MenuItem = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.TextPrefab.call (esto, game_state, nombre, posición, propiedades);}; RPG.MenuItem.prototype = Object.create (RPG.TextPrefab.prototype); RPG.MenuItem.prototype.constructor = RPG.MenuItem; RPG. MenuItem.prototype.selection_over = function () { «utilizar estricta»; this.fill = «# FFFF00»;}; RPG.MenuItem.prototype.selection_out = function () { «utilizar estricta»; this.fill = «# FFFFFF»;}; 12345678910111213141516171819varRPG = RPG || {}; RPG.MenuItem = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.TextPrefab.call (esto, game_state, nombre , posición, propiedades);}; RPG.MenuItem.prototype = Object.create (RPG.TextPrefab.prototype); RPG.MenuItem.prototype.constructor = RPG.MenuItem; RPG.MenuItem.prototype.selection_over = función () {» utilizar estricta «; this.fill =» # FFFF00 «;}; RPG.MenuItem.prototype.selection_out = function () {» utilizar estricta «; this.fill =» # FFFFFF «;};

          también, aviso de que MenuItem prefabricada no implementa el método de “seleccionar”, que es llamada por menú. Por lo tanto, tenemos que crear nuevas estructuras prefabricadas que se extienden Menultem y aplicarla. Hacemos esto mediante la creación de tres nuevas casas prefabricadas: AttackMenuItem, PlayerMenuItem y EnemyMenuItem. La primera de ellas sólo se desactivará el menú de acciones y habilitar el menú de unidades enemigas, por lo que el jugador puede elegir el objetivo de ataque.
          var = RPG RPG || {}; RPG.AttackMenuItem = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.MenuItem.call (esto, game_state, nombre, posición, propiedades);}; RPG.AttackMenuItem.prototype = Object.create (RPG.MenuItem.prototype); RPG.AttackMenuItem.prototype.constructor = RPG.AttackMenuItem; RPG. AttackMenuItem.prototype.select = function () { «utilizar estricta»; // acciones desactivar this.game_state.prefabs.actions_menu.disable menú (); // Activar menú de unidades enemigas por lo que el jugador puede elegir el this.game_state.prefabs.enemy_units_menu.enable objetivo ();}; 1234567891011121314151617varRPG = || RPG {}; RPG.AttackMenuItem = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.MenuItem.call (esto, game_state, nombre, posición, propiedades);}; RPG.AttackMenuItem.prototype = Object.create (RPG.MenuItem.prototype); RPG.AttackMenuItem.prototype.constructor = RPG .AttackMenuItem; RPG.AttackMenuItem.prototype.select = function () { «use strict»; // acciones desactivar menuthis.game_state.prefabs.actions_menu.disable (); // activar menú de unidades enemigas por lo que el jugador puede elegir los targetthis. game_state.prefabs.enemy_units_menu.enable ();};

          el segundo no hará nada cuando se selecciona, ya que sólo será utilizada para mostrar la unidad de jugador actual. Sin embargo, queremos mostrar la unidad de salud del jugador, por lo que creará una casa prefabricada ShowState (cuyo código se muestra a continuación), que mostrará la unidad de salud del jugador.
          var = RPG RPG || {}; RPG.PlayerMenuItem = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.MenuItem.call (esto, game_state, nombre, posición, propiedades); this.player_unit_health = new RPG.ShowStat (this.game_state, This.Text + «_health», {x: 280, y: this.y}, {grupo: «HUD», texto: «», estilo: properties.style , prefabricada: This.Text, dato: «salud»});}; RPG.PlayerMenuItem.prototype = Object.create (RPG.MenuItem.prototype); RPG.PlayerMenuItem.prototype.constructor = RPG.PlayerMenuItem; RPG.PlayerMenuItem. prototype.select = function () { «utilizar estricta»;}; 123456789101112131415varRPG = RPG || {}; RPG.PlayerMenuItem = function (game_state, nombre, posición, propiedades) { «uso estricto»; RPG.MenuItem.call (esto , game_state, nombre, posición, propiedades); this.player_unit_health = newRPG.ShowStat (this.game_state, This.Text + «_ salud», {x: 280, y: this.y}, {grupo: «HUD», texto: «», estilo: properties.style, prefabricada: This.Text, dato: «salud»});}; RPG.PlayerMenuItem.prototype = Object.create (RPG.MenuItem.prototype); RPG.PlayerMenuItem.prototype.constructor = RPG.PlayerMenuItem; RPG.PlayerMenuItem.prototype.select = function () { «use strict»;};

          Finalmente, el EnemyMenuItem se utiliza para seleccionar el enemigo a ser el ataque ed. Podemos hacer que al conseguir el enemigo prefabricada (el texto del elemento de menú será el nombre prefabricada) y haciendo que la unidad de unidad atacante actual. Vamos a añadir la unidad actual y poner en práctica el método de ataque más tarde.
          var = RPG RPG || {}; RPG.EnemyMenuItem = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; RPG.MenuItem.call (esto, game_state, nombre, posición, propiedades);}; RPG.EnemyMenuItem.prototype = Object.create (RPG.MenuItem.prototype); RPG.EnemyMenuItem.prototype.constructor = RPG.EnemyMenuItem; RPG. EnemyMenuItem.prototype.select = function () { «utilizar estricta»; var enemigo; // obtener enemigo prefabricada enemigo = this.game_state.prefabs [This.Text]; // ataque seleccionado this.game_state.current_unit.attack enemigo (enemigo); // desactivar los menús this.game_state.prefabs.enemy_units_menu.disable (); this.game_state.prefabs.player_units_menu.disable ();}; 123456789101112131415161718192021varRPG = RPG || {}; RPG.EnemyMenuItem = función (game_state, nombre, posición, propiedades) { «uso estricto»; RPG.MenuItem.call (esto, game_state, nombre, posición, propiedades);}; RPG.EnemyMenuItem.prototype = Object.create (RPG.MenuItem.prototype); RPG.EnemyMenuItem.prototype.constructor = RPG.EnemyMenuItem; RPG.EnemyMenuItem.prototype.select = función ( ) { «utilizar estricta»; varenemy; // obtener prefabenemy enemigo = this.game_state.prefabs [This.Text]; // ataque enemythis.game_state.current_unit.attack seleccionado (enemigo); // desactivar menusthis.game_state.prefabs. enemy_units_menu.disable (); this.game_state.prefabs.player_units_menu.disable ();};

          se añadirán todos los menús en el método “init_hud” de BattleState, que se llama en el extremo de la “crear” método. En primer lugar, el método “show_player_actions” crea el menú de acciones, lo que en este tutorial tendrá sólo la acción de ataque. En los próximos tutoriales vamos a añadir más acciones, como la magia y el artículo. A continuación, se utiliza el método de “show_units” para crear las unidades de los jugadores y enemigos. Tenga en cuenta que este método recibe como parámetro el nombre del grupo y unidades de elemento de menú constructor, por lo que puede ser utilizado para crear diferentes tipos de menú de unidades.
          RPG.BattleState.prototype.show_units = function (GROUP_NAME, la posición, menu_item_constructor) { «utilizar estricta»; var unit_index, menu_items, unit_menu_item, units_menu; // crear unidades de elementos de menú unit_index = 0; menu_items = []; this.groups [GROUP_NAME] .forEach (función (unidad) {unit_menu_item = new menu_item_constructor (esto, unit.name + «_menu_item», {x: position.x, y: position.y + unit_index * 20}, {grupo: «HUD», texto: unit.name, el estilo: Object.create (this.TEXT_STYLE)}); unit_index + = 1; menu_items.push (unit_menu_item);}, this); // crear unidades menú units_menu = new RPG.Menu (esto, GROUP_NAME + «_menu», la posición, {grupo: «HUD», menu_items: menu_items});}; RPG.BattleState.prototype.show_player_actions = función (posición) { «utilizar estricta»; var acciones, actions_menu_items, action_index, actions_menu; // acciones de acciones disponibles = [{text: «Ataque», item_constructor: RPG.AttackMenuItem.prototype.constructor}]; actions_menu_items = []; action_index = 0; // crear un elemento de menú para cada actions.forEach acción (función (acción) {actions_menu_items.push (nueva action.item_constructor (esto, action.text + «_menu_item», {x: position.x, y: position.y + action_index * 20}, {grupo: «HUD», texto: action.text, el estilo: Object.create (this.TEXT_STYLE)})); action_index + = 1;}, this); actions_menu = new RPG.Menu (esto «actions_menu», la posición, {grupo: «HUD», menu_items: actions_menu_items});}; 123456789101112131415161718192021222324252627282930RPG.BattleState.prototype.show_units = function (GROUP_NAME, la posición, menu_item_constructor) { «uso estricto «; varunit_index, menu_items, unit_menu_item, units_menu; // crear el menú unidades itemsunit_index = 0; menu_items = []; this.groups [GROUP_NAME] .forEach (función (unidad) {unit_menu_item = newmenu_item_constructor (esto, unit.name +» _ menu_item» , {x: position.x, y: position.y + unit_index * 20}, {grupo: «HUD», texto: unit.name, el estilo: Object.create (this.TEXT_STYLE)}); unit_index + = 1; menu_items .push (unit_menu_item);}, this); // crear unidades menuunits_menu = newRPG.Menu (esto, GROUP_NAME + «_ menú», la posición, {grupo: «HUD», menu_items: menu_items});}; RPG.BattleState.prototype .show_player_actions = función (posición) { «utilizar estricta»; varactions, actions_menu_items, action_index, actions_menu; // actionsactions disponibles = [{text: «ataque», item_constructor: RPG.AttackMenuItem.prototype.constructor}]; ac tions_menu_items = []; action_index = 0; // crear un elemento de menú para cada actionactions.forEach (función (acción) {actions_menu_items.push (newaction.item_constructor (esto, action.text + «_ menu_item», {x: position.x, y: position.y + action_index * 20}, {grupo: «HUD», texto: action.text, el estilo: Object.create (this.TEXT_STYLE)})); action_index + = 1;}, this); actions_menu = newRPG .menu (esto «actions_menu», posición, {grupo: «HUD», menu_items: actions_menu_items});};

          a estas alturas ya se puede ejecutar el juego para ver si se visualiza correctamente el menú. También puede tratar de lograr que algunos menús para ver si se puede navegar a través de ellos correctamente. La única cosa que aún no funciona será la selección del menú.

          HUD

          La aplicación de las vueltas

          En nuestro juego vamos a crear una matriz con todas las unidades y en cada vuelta de la primera unidad de la matriz actuará. En primer lugar, creamos la matriz “unidades” en el método de “crear”, entonces llamamos al método “next_turn”.
          // crear unidades de array con el jugador y enemigas unidades 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 (); 123 456 // crear unidades de array con el jugador y enemigas unitsthis.units = []; this.units = this.units.concat (this.groups.player_units.children); this.units = this.units. concat (this.groups.enemy_units.children); this.next_turn ();

          el método “next_turn” toma la primera unidad de la matriz y, si la unidad está vivo, actúa y se empuja hasta el final de la unidades array. De lo contrario, se llama al siguiente turno. El código siguiente muestra las modificaciones a BattleState.
          RPG.BattleState.prototype.next_turn = function () { «utilizar estricta»; // 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 (); }}; 123456789101112RPG.BattleState.prototype.next_turn = function () { «uso estricto»; // toma el siguiente unitthis.current_unit = this.units.shift (); // si la unidad está vivo, actúa, de otro modo va a la siguiente turnif (this.current_unit.alive) {this.current_unit.act (); this.units.push (this.current_unit);} else {this.next_turn ();}};

          Ahora, tenemos para poner en práctica el método de “acto”, tanto en EnemyUnit y PlayerUnit. El método EnemyUnit “acto” elige una unidad de jugador aleatorio como el objetivo y lo atacan. Por otra parte, la “Ley” método para PlayerUnit pone de relieve la unidad reproductora actual y permite al menú de unidades enemigas, por lo que el jugador puede elegir que el enemigo ataque.
          RPG.EnemyUnit.prototype.act = function () { «utilizar estricta»; var target_index, objetivo, el daño; // elegir aleatoriamente objetivo target_index = this.game_state.rnd.between (0, this.game_state.groups.player_units.countLiving () – 1); target = this.game_state.groups.player_units.children [target_index]; this.attack (objetivo);}; RPG.PlayerUnit.prototype.act = function () { «utilizar estricta»; unit_index var, player_units_menu_items; // buscar el índice de esta unidad en el player_units_menu unit_index = this.game_state.prefabs.player_units_menu.find_item_index (this.name); this.game_state.prefabs.player_units_menu.move_selection (unit_index); // Habilitar el menú para escoger la acción this.game_state.prefabs.actions_menu.enable ();}; 1234567891011121314151617181920RPG.EnemyUnit.prototype.act = function () { «uso estricto»; vartarget_index, objetivo, el daño; // elegir al azar targettarget_index = this.game_state.rnd.between (0, this.game_state.groups.player_units.countLiving () – 1); target = this.game_state.groups.player_units.children [target_index]; this.attack (objetivo);}; RPG.PlayerUnit.prototype.act = function () { «utilizar estricta»; varunit_index, player_units_menu_items; // buscar el índice de esta unidad en el player_units_menuunit_index = this.game_state.prefabs.player_units_menu.find_item_index (this.name); esta .game_state.prefabs.player_units_menu.move_selection (unit_index); // habilitar el menú para elegir el actionthis.game_state.prefabs.actions_menu.enable ();};

          el método “ataque” es la misma para ambas unidades, por lo que se llevará a cabo en la Unidad prefabricada. Se calcula el daño basado en el ataque y la defensa unidad de destino, y se ocupa de que el daño a la unidad objetivo. Tenga en cuenta que el daño es al azar multiplicando el ataque y la defensa por los multiplicadores al azar entre 0,8 y 1,2. Toda generación aleatoria se realiza mediante Phaser RandomDataGenerator (se puede comprobar la documentación para obtener más información). Después de tratar el daño, se mostrará un mensaje de ataque, por lo que el jugador puede tener una retroalimentación visual. El ActionMessage prefabricado (mostrado a continuación), simplemente muestra un texto dentro de un rectángulo, que se mataron después de algún tiempo. Un detalle importante es que la próxima vez se llama cuando este ActionMessage se mató.
          RPG.Unit.prototype.attack = función (objetivo) { «utilizar estricta»; daño var, attack_multiplier, defense_multiplier, action_message_position, action_message_text, attack_message; // objetivo de ataque attack_multiplier = this.game_state.game.rnd.realInRange (0.8, 1.2); defense_multiplier = this.game_state.game.rnd.realInRange (0.8, 1.2); daño = Math.round ((attack_multiplier * this.stats.attack) – (defense_multiplier * target.stats.defense)); target.receive_damage (daño); // Mostrar mensaje ataque action_message_position = new Phaser.Point (this.game_state.game.world.width / 2, this.game_state.game.world.height * 0,1); action_message_text = this.name + «ataques» + target.name + «con» «daño» + daño +; attack_message = new RPG.ActionMessage (this.game_state, this.name + «_action_message», action_message_position, {grupo: «HUD», textura: «rectangle_image», escala: {x: 0,75, y: 0.2}, duración: 1, message: action_message_text});};1234567891011121314RPG.Unit.prototype.attack=function(target){«use strict»;vardamage,attack_multiplier,defense_multiplier,action_message_position,action_message_text,attack_message;// attack targetattack_multiplier=this.game_state.game.rnd .realInRange(0.8,1.2);defense_multiplier=this.game_state.game.rnd.realInRange(0.8,1.2);damage=Math.round((attack_multiplier*this.stats.attack)-(defense_multiplier*target.stats.defense) );target.receive_damage(damage);// show attack messageaction_message_position=newPhaser.Point(this.game_state.game.world.width/2,this.game_state.game.world.height*0.1);action_message_text=this.name+» attacks «+target.name+» with «+damage+» damage»;attack_message=newRPG.ActionMessage(this.game_state,this.name+»_action_message»,action_message_positio n,{group:»hud»,texture:»rectangle_image»,scale:{x:0.75,y:0.2},duration:1,message:action_message_text});};var RPG = RPG || {};RPG.ActionMessage = function (game_state, name, position, properties) { «use strict»; RPG.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); // create message text this.message_text = new RPG.TextPrefab(this.game_state, this.name + «_message», position, {group: «hud», text: properties.message, style: Object.create(this.game_state.TEXT_STYLE)}); this.message_text.anchor.setTo(0.5); // start timer to destroy the message this.kill_timer = this.game_state.game.time.create(); this.kill_timer.add(Phaser.Timer.SECOND * properties.duration, this.kill, this); this.kill_timer.start();};RPG.ActionMessage.prototype = Object.create(RPG.Prefab.prototype);RPG.ActionMessage.prototype.constructor = RPG.ActionMessage;RPG.ActionMessage.prototype.kill = function () { «use strict»; Phaser.Sprite.prototype.kill.call (this); // when the message is destroyed, call next turn this.message_text.kill(); this.game_state.next_turn();};12345678910111213141516171819202122232425262728varRPG=RPG||{};RPG.ActionMessage=function(game_state,name,position,properties){«use strict»;RPG.Prefab.call(this,game_state,name,position,properties);this.anchor.setTo(0.5);// create message textthis.message_text=newRPG.TextPrefab(this.game_state,this.name+»_message»,position,{group:»hud»,text:properties.message,style:Object.create(this.game_state.TEXT_STYLE)});this.message_text.anchor.setTo(0.5);// start timer to destroy the messagethis.kill_timer=this.game_state.game.time.create();this.kill_timer.add(Phaser.Timer.SECOND*properties.duration,this.kill,this);this.kill_timer.start();};RPG.ActionMessage.prototype=Object.create(RPG.Prefab.prototype);RPG.ActionMessage.prototype.constructor=RPG.ActionMessage;RPG.ActionMessage.prototype.kill=function(){«use strict»;Phaser.Sprite.prototype.kill.call(this);// when the message is destroyed, call next turnthis.message_text.kill();this.game_state.next_turn();};

          The “rec eive_damage” method is also the same for both units, and it reduces the unit health and check if it is dead. In addition, it starts an attacked animation, which changes the prefab tint to red and then goes back to the normal.

          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 = 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, this);};RPG.Unit.prototype = Object.create(RPG.Prefab.prototype);RPG.Unit.prototype.constructor = RPG.Unit;RPG.Unit.prototype.receive_damage = function (damage) { «use strict»; this.stats.health -= damage; this.attacked_animation.start(); if (this.stats.health <= 0) { this.stats.health = 0; this.kill (); }};RPG.Unit.prototype.restore_tint = function () { "use strict"; this.tint = 0xFFFFFF;};1234567891011121314151617181920212223242526272829303132varRPG=RPG||{};RPG.Unit=function(game_state,name,position,properties){"use strict";RPG.Prefab.call(this,game_state,name,position,properties);this.anchor.setTo(0.5);this.stats=properties.stats;this.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);};RPG.Unit.prototype=Object.create(RPG.Prefab.prototype);RPG.Unit.prototype.constructor=RPG.Unit;RPG.Unit.prototype.receive_damage=function(damage){"use strict";this.stats.health-=damage;this.attacked_animation.start();if(this.stats.health<=0){this.stats.health=0;this.kill();}};RPG.Unit.prototype.restore_tint=function(){"use strict";this.tint=0xFFFFFF;};

          Finally, we have to change the “kill” method of both EnemyUnit and PlayerUnit to update their menus accordingly. First, when an enemy unit dies, it must remove itself from the enemy units menu, which can be done using the methods we already have in the Menu prefab. On the other hand, the player unit will not remove itself from its menu, but only change the alpha of its menu item, making it darker.

          RPG.EnemyUnit.prototype.kill = function () { «use strict»; var menu_item_index, menu_item; Phaser.Sprite.prototype.kill.call (this); // remove from the menu menu_item_index = this.game_state.prefabs.enemy_units_menu.find_item_index(this.name); menu_item = this.game_state.prefabs.enemy_units_menu.remove_item(menu_item_index); menu_item.kill();};RPG.PlayerUnit.prototype.kill = function () { «use strict»; var menu_item_index, menu_item; Phaser.Sprite.prototype.kill.call (this); // remove from the menu menu_item_index = this.game_state.prefabs.player_units_menu.find_item_index(this.name); this.game_state.prefabs.player_units_menu.menu_items[menu_item_index].alpha = 0.5;};123456789101112131415161718RPG.EnemyUnit.prototype.kill=function(){«use strict»;varmenu_item_index,menu_item;Phaser.Sprite.prototype.kill.call(this);// remove from the menumenu_item_index=this.game_state.prefabs.enemy_units_menu.find_item_index(this.name);menu_item=this.game_state.prefabs.enemy_units_menu.remove_item(menu_item_index);menu_item.kill();};RPG.PlayerUnit.prototype.kill=function(){«use strict»;varmenu_item_index,menu_item;Phaser.Sprite.prototype.kill.call(this);// remove from the menumenu_item_index=this.game_state.prefabs.player_units_menu.find_item_index(this.name);this.game_state.prefabs.player_units_menu.menu_items[menu_item_index].alpha=0.5;};

          Finally, you can try playing the BattleState. Try changing the enemies and player stats to see if everything is working accordingly.

          attack

          And we finished the first part of this tutorial series. In the next tutorials we will add a world that the player can explore and find random enemies. We will also improve the battle state, adding more actions and a different turn-based approach based on the units speed.

          Mensajes relacionados

Deja una respuesta

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