A veces, en un juego que desea estar al tanto de los acontecimientos que ocurren en su juego todo el tiempo, wether sería para salvar las estadísticas del juego o para construir un sistema de logros. Por ejemplo, el juego puede necesitar saber cuando un enemigo está muerto para guardar el número de enemigos muertos, o porque hay un logro cuando el jugador mata a un número determinado de enemigos.

En este tutorial voy a mostrar cómo utilizar Phaser.Signal para escuchar a los acontecimientos en su juego con el fin de salvar a las estadísticas de juego. Vamos a ponerlo en práctica en un juego de disparos simples espacio. Al final, voy a explicar brevemente cómo extender este concepto para crear un sistema de logros. Para poder 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, grupos y una galería física

    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.

    archivos de código fuente

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

    Juego Unidos

    Vamos a mantener los datos del juego en un archivo JSON como se muestra a continuación. Este archivo describe los recursos del juego que deben cargarse, los grupos que deben ser creados y las casas prefabricadas. Vamos a crear tres estados de juego para manejar este archivo JSON:. BootState, LoadingState y LevelState
    { «Mundo»: { «origin_x»: 0 «origin_y»: 0, «ancho»: 360, «altura»: 640}, los «activos»: { «space_image»: { «type»: «imagen», » fuente «: «assets / images / space.png»}, «ship_image»: { «type»: «imagen», «fuente»: «assets / images / player.png»}, «bullet_image»: { «tipo» : «imagen», «fuente»: «assets / images / bullet.png»}, «enemy_spritesheet»: { «type»: «spritesheet», «fuente»: «assets / images / green_enemy.png», «frame_width» : 50, «frame_height»: 46, «marcos»: 3, «margen»: 1, «espaciamiento»: 1}}, «grupos»: [ «naves», «player_bullets», «enemigos», «enemy_bullets», «enemy_spawners», «HUD»], «prefabricados»: { «barco»: { «tipo»: «barco», «posición»: { «x»: 180, «Y»: 600}, «propiedades»: { «textura»: «ship_image», «grupo»: «naves», «velocidad»: 200, «shoot_rate»: 5, «bullet_velocity»: 500}}, «enemy_spawner»: { «type»: «enemy_spawner», » posición «: {» x «: 0 » y»: 100}, «propiedades»: { «textura»: «», «grupo»: «enemy_spawners», «spawn_interval»: 1, «enemy_properties»: {» textura «: «enemy_spritesheet», «grupo»: «enemigos»,» vel ocity «: 50, «shoot_rate»: 2, «bullet_velocity»: 300}}}}} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 {» mundo «: {» origin_x «: 0», origin_y «: 0,» ancho «: 360,» altura «: 640}, los «activos»: { «space_image»: { «type»: «imagen», «fuente»: «assets / images / space.png»}, «ship_image»: { «type»: «imagen»,» fuente «:» assets / images / player.png «},» bullet_image «: {» type «:» imagen», «fuente»: «assets / images / bullet.png»}, «enemy_spritesheet»: { «tipo» : «spritesheet», «fuente»: «assets / images / green_enemy.png», «frame_width»: 50, «frame_height»: 46, «marcos»: 3, «margen»: 1, «espaciamiento»: 1}} , «grupos»: [ «naves», «player_bullets», «enemigos», «enemy_bullets», «enemy_spawners», «HUD»], «casas prefabricadas»: { «buque»: { «type»: «barco»,» posición «: {» x «: 180,» y «: 600},» propiedades «: {» textura «:» ship_image», «grupo»: «naves», «velocidad»: 200, «shoot_rate»: 5, «bullet_velocity»: 500}}, «enemy_spawner»: { «type»: «enemy_spawner», «posición»: { «x»: 0 «y»: 100}, «propiedades»: { «textura»: «» , «grupo»: «enemy_spawners», «spawn_interval»: 1, «enemy_properties»: { «textura» : «Enemy_spritesheet», «grupo»: «enemigos», «velocidad»: 50, «shoot_rate»: 2, «bullet_velocity»: 300}}}}}

    códigos BootState y LoadingState se muestran abajo. El primero de ellos simplemente carga el archivo JSON y llama LoadingState con los datos de nivel. LoadingState, por su vez, las cargas de todos los activos de juego llamando al método Phaser correcta de acuerdo con el tipo de activo (por ejemplo, llamar “this.load.image” para cargar una imagen).
    var SignalExample = SignalExample || {}; SignalExample.BootState = function () { «utilizar estricta»; Phaser.State.call (this);}; SignalExample.BootState.prototype = Object.create (Phaser.State.prototype); SignalExample.BootState.prototype.constructor = SignalExample.BootState; SignalExample.BootState.prototype.init = function ( level_file, next_state, extra_parameters) { «uso estricto»; this.level_file = level_file; this.next_state = next_state; this.extra_parameters = extra_parameters;}; SignalExample.BootState.prototype.preload = function () { «utilizar estricta»; this.load.text ( «level1», this.level_file);}; SignalExample.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, this.extra_parameters);}; 1234567891011121314151617181920212223242526272829varSignalExample = SignalExample || {}; SignalExample.BootState = function () { «utilizar estricta»; Phaser.State.call (this);}; SignalExample.BootState.prototype = Object.create (Phaser.State.prototype); SignalExample.BootState.prototype.constructor = SignalExample.BootState; SignalExample.BootState.prototype.init = function ( level_file, next_state, extra_parameters) { «uso estricto»; this.level_file = level_file; this.next_state = next_state; this.extra_parameters = extra_parameters;}; SignalExample.BootState.prototype.preload = function () { «utilizar estricta»; esta .load.text ( «level1», this.level_file);}; SignalExample.BootState.prototype.create = function () { «utilizar estricta»; varlevel_text, 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, this.extra_parameters);}; var SignalExample = S ignalExample || {}; SignalExample.LoadingState = function () { «utilizar estricta»; Phaser.State.call (this);}; SignalExample.LoadingState.prototype = Object.create (Phaser.State.prototype); SignalExample.LoadingState.prototype.constructor = SignalExample.LoadingState; SignalExample.LoadingState.prototype.init = function ( level_data, next_state, extra_parameters) { «uso estricto»; this.level_data = level_data; this.next_state = next_state; this.extra_parameters = extra_parameters;}; SignalExample.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; }}}}; SignalExample.LoadingState.prototype.create = function () { «utilizar estricta»; this.game.state.start (this.next_state, verdadero, falso, this.level_data, this.extra_parameters);}; 12345678910111213141516171819202122232425262728293031323334353637383940414243varSignalExample = SignalExample || {}; SignalExample.LoadingState = function () { «utilizar estricta»; Phaser. State.call (this);}; SignalExample.LoadingState.prototype = Object.create (Phaser.State.prototype); SignalExample.LoadingState.prototype.constructor = SignalExample.LoadingState; SignalExample.LoadingState.prototype.init = function (level_data, next_state, extra_parameters) { «uso estricto»; this.level_data = level_data; this.next_state = next_state; this.extra_parameters = extra_parameters;}; SignalExample.LoadingState.prototype.preload = function () { «utilizar estricta»; varassets, asset_loader , asset_key, activo; activo = this.level_data.assets; for (inassets asset_key) {// activos de carga de acuerdo con keyif activo (assets.hasOwnProperty (asset_key)) {activos = activos [asset_key]; conmutador (asset.type) { caso «imagen»: this.load.image (asset_key, asset.source); break; caso «spritesheet»: this.load.sp ritesheet (asset_key, asset.source, asset.frame_width, asset.frame_height, asset.frames, asset.margin, asset.spacing); break; caso «tilemap»: this.load.tilemap (asset_key, asset.source, null, Phaser.Tilemap.TILED_JSON); break;}}}}; SignalExample.LoadingState.prototype.create = function () { «utilizar estricta»; this.game.state.start (this.next_state, verdadero, falso, this.level_data , this.extra_parameters);};

    LevelState, por su parte, es responsable de la creación de los grupos de juego y casas prefabricadas, como se muestra a continuación. En el método de “crear” se inicia mediante la creación de un Phaser.TileSprite para representar el espacio de fondo. Entonces se crea todos los grupos de juego desde el archivo JSON. Por último, se crea todas las casas prefabricadas por iteración a través de todas las casas prefabricadas que se describen en el archivo JSON llamando al método “create_prefab”.
    var SignalExample = SignalExample || {}; SignalExample.LevelState = function () { «utilizar estricta»; Phaser.State.call (this); this.prefab_classes = { «buque»: SignalExample.Ship.prototype.constructor, «enemy_spawner»: SignalExample.EnemySpawner.prototype.constructor};}; SignalExample.LevelState.prototype = Object.create (Phaser.State.prototype); SignalExample .LevelState.prototype.constructor = SignalExample.LevelState; SignalExample.LevelState.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; sistema de física this.game.physics.startSystem (Phaser.Physics.ARCADE) // empezar; this.game.physics.arcade.gravity.y = 0;}; SignalExample.LevelState.prototype.create = function () { «utilizar estricta»; GROUP_NAME var, prefab_name; this.space = this.add.tileSprite (0, 0, this.game.world.width, this.game.world.height, «space_image»); // 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]); }}}; SignalExample.LevelState.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); }}; 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758varSignalExample = SignalExample || {}; SignalExample.LevelState = function () { «uso estricto»; Phaser.State.call (this); this.prefab_classes = { «ship»: SignalExample.Ship.prototype.constructor, «enemy_spawner»: SignalExample.EnemySpawner.prototype.constructor};}; SignalExample.LevelState.prototype = Object.create (Phaser.State.prototype); SignalExample.LevelState.prototype.constructor = SignalExample.LevelState; SignalExample.LevelState.prototype. init = function (level_data) { «use strict»; this.level_data = level_data; this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true; // iniciar la física systemthis.game.physics.startSystem (Phaser.Physics.ARCADE); this.game.physics.arcade.gravity.y = 0;}; SignalExample.LevelState.prototype.create = function () { «utilizar estricta»; vargroup_name, prefab_name; this.space = this.add.tileSprite (0,0, this.game.world.width, this.game.w orld.height, «space_image»); // crear groupsthis.groups = {}; this.level_data.groups.forEach (function (GROUP_NAME) {this.groups [GROUP_NAME] = this.game.add.group ();} , este); // crear prefabsthis.prefabs = {}; for (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]);}}}; SignalExample.LevelState.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. tipo)) {prefabricada = newthis.prefab_classes [prefab_data.type] (esto, prefab_name, prefab_data.position, prefab_data.properties);}};

    el método “create_prefab” crea el acuerdo prefabricada correcta para su tipo. Tenga en cuenta que esto se puede hacer porque todas las casas prefabricadas tienen el mismo constructor, extendida por la clase genérica prefabricada se muestra a continuación. Además, hay un “prefab_classes” propiedad (en el constructor LevelState) que mapea cada tipo prefabricada a su correspondiente constructor.
    var SignalExample = SignalExample || {}; SignalExample.Prefab = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; Phaser.Sprite.call (esto, game_state.game, position.x, position.y, properties.texture); this.game_state = game_state; this.name = Nombre; this.game_state.groups [properties.group] .add (this); this.frame = + properties.frame; si (properties.scale) {this.scale.setTo (properties.scale.x, properties.scale.y); } If (properties.anchor) {this.anchor.setTo (properties.anchor.x, properties.anchor.y); } This.game_state.prefabs [nombre] = esta;}; SignalExample.Prefab.prototype = Object.create (Phaser.Sprite.prototype); SignalExample.Prefab.prototype.constructor = SignalExample.Prefab; 1234567891011121314151617181920212223242526varSignalExample = SignalExample || {} ; SignalExample.Prefab = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; Phaser.Sprite.call (esto, game_state.game, position.x, position.y, properties.texture); this.game_state = game_state; This.Name = nombre; this.game_state.groups [properties.group] .add (this); this.frame = + properties.frame; si (properties.scale) {this.scale.setTo (properties.scale. x, properties.scale.y);} if (properties.anchor) {this.anchor.setTo (properties.anchor.x, properties.anchor.y);} this.game_state.prefabs [nombre] = esta;}; SignalExample.Prefab.prototype = Object.create (Phaser.Sprite.prototype); SignalExample.Prefab.prototype.constructor = SignalExample.Prefab;

    El GameStats Plugin

    Ahora vamos a crear un plugin Phaser para salvar las estadísticas del juego al escuchar las señales de modulación de fase. Para crear un plug-in Phaser tenemos que crear una clase que se extiende Phaser.Plugin, como se muestra a continuación. El método “init” se llama cuando se añade el plugin para el juego, y lo usamos para guardar todas las propiedades del plugin, como la posición, estilo de texto del juego y las estadísticas que señala el plugin va a escuchar. Todos estos datos se cargan desde el archivo de nivel JSON y se representa con la siguiente estructura:
    «Game_stats_data»: { «posición»: { «x»: 180, «Y»: 300}, «text_style»: { «font»: «28pt Arial», «relleno»: «#FFFFFF»} «game_stats» : { «shots_fired»: { «texto»: «Disparos:», «valor»: 0} «enemies_spawned»: { «texto»: «Los enemigos generados:», «valor»: 0}}, «oyentes» : [{ «grupo»: «naves», «señal»: «onShoot», «stat_name»: «shots_fired», «valor»: 1}, { «grupo»: «enemy_spawners», «señal»: «onSpawn» , «stat_name»: «enemies_spawned», «valor»: 1}]} 123456789101112 «game_stats_data»: { «posición»: { «x»: 180, «y»: 300}, «text_style»: { «font»: «28pt Arial», «relleno»: «# FFFFFF»}, «game_stats»: { «shots_fired»: { «texto»: «Disparos:», «valor»: 0} «enemies_spawned»: { «texto» : «Enemies engendraron:», «valor»: 0}}, «oyentes»: [{ «grupo»: «naves», «señal»: «onShoot», «stat_name»: «shots_fired», «valor»: 1 }, { «grupo»: «enemy_spawners», «señal»: «onSpawn», «stat_name»: «enemies_spawned», «valor»: 1}]}

    el método “listen_to_events” es responsable de hacer que el plugin escucha los eventos del juego. En él se recorre todas las descripciones de los oyentes (guardados en el método “init”) y crea los oyentes para cada uno. Tenga en cuenta que cada oyente describe el grupo tiene el plugin para escuchar, entonces el plugin tiene iterar a través de todos los sprites de ese grupo para escuchar a cada uno por separado. La devolución de llamada de todos los eventos es el método “save_stat”, que recibe como parámetros el sprite que distribuyó el evento, el nombre de estadísticas y el valor que se incremente. Este método simplemente aumenta la estadística de juego correcto con el valor dado. Tenga en cuenta que el objeto “game_stats” ya se ha inicializado en el método “init” con los valores iniciales de “game_stats_data”.

    Por último, vamos a añadir un método de “show_stats”, que será llamado en el final del juego para mostrar los valores finales. Este método se itera a través de todas las estadísticas del juego creando un Phaser.Text a cada uno que muestra el valor final. Tenga en cuenta que la posición inicial de los textos y su estilo se guarda en el método “init”, mientras que su texto final es el texto de la estadística de juego y su valor final.
    var Phaser = Phaser || {}; Var SignalExample = SignalExample || {}; SignalExample.GameStats = function (juego, los padres) { «use strict»; Phaser.Plugin.call (esto, juego, los padres);}; SignalExample.GameStats.prototype = Object.create (Phaser.Plugin.prototype); SignalExample.GameStats.prototype.constructor = SignalExample.GameStats; SignalExample.GameStats.prototype. init = función (game_state, game_stats_data) { «utilizar estricta»; // Guardar propiedades this.game_state = game_state; this.game_stats_position = game_stats_data.position; this.game_stats_text_style = game_stats_data.text_style; this.game_stats = game_stats_data.game_stats; this.listeners = game_stats_data.listeners;}; SignalExample.GameStats.prototype.listen_to_events = function () { «utilizar estricta»; this.listeners.forEach (function (oyente) {// iterar a través del grupo que debe ser this.game_state.groups escuchó [listener.group] .forEach (function (Sprite) {// añadir un oyente para cada sprite en el grupo de sprite.events [listener.signal] .add (this.save_stat, esto, 0, listener.stat_name, listener.value);}, this);}, this);}; SignalExample.GameStats.prototype.save_stat = function ( sprite, stat_name, valor) { «utilizar estricta»; // aumentar los correspondientes this.game_stats estadísticas de juego [stat_name] .value + = valor;}; SignalExample.GameStats.prototype.show_stats = function () { «utilizar estricta»; posición var, game_stat, game_stat_text; posición = nuevo Phaser.Point (this.game_stats_position.x, this.game_stats_position.y); para (game_stat en this.game_stats) {if (this.game_stats.hasOwnProperty (game_stat)) {// crear un texto Phaser para cada stat juego que muestra el valor final = game_stat_text nueva Phaser.Text (this.game_state.game, posición. x, position.y, this.game_stats [game_stat] .text + this.game_stats [game_stat] .value, Object.create (this.game_stats_text_style)); game_stat_text.anchor.setTo (0,5); this.game_state.groups.hud.add (game_stat_text); position.y + = 50; }}}; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354varPhaser = Phaser || {}; varSignalExample = SignalExample || {}; SignalExample.GameStats = function (juego, los padres) { «use strict»; Phaser.Plugin.call (esto, juego, los padres);} ; SignalExample.GameStats.prototype = Object.create (Phaser.Plugin.prototype); SignalExample.GameStats.prototype.constructor = SignalExample.GameStats; SignalExample.GameStats.prototype.init = function (game_state, game_stats_data) { «utilizar estricta»; // Guardar propertiesthis.game_state = game_state; this.game_stats_position = game_stats_data.position; this.game_stats_text_style = game_stats_data.text_style; this.game_stats = game_stats_data.game_stats; this.listeners = game_stats_data.listeners;}; SignalExample.GameStats.prototype.listen_to_events = function () { «utilizar estricta»; this.listeners.forEach (function (oyente) {// iterar a través del grupo que debe ser this.game_state.groups escuchó [listener.group] .forEach (function (Sprite) {/ / añadir un detector para cada sprite en el groupsprite.events [listener.signal] .add (this.save_stat, esto, 0, listener.stat_name, listener.value);}, this);}, this);}; SignalExample.GameStats.prototype.save_stat = function ( sprite, stat_name, valor) { «uso estricto»; // aumentar los correspondientes statthis.game_stats juego [stat_name] .value + = valor;}; SignalExample.GameStats.prototype.show_stats = function () { «uso estricto»; varposition, game_stat, game_stat_text; posición = newPhaser.Point (this.game_stats_position.x, this.game_stats_position.y); for (inthis.game_stats game_stat) {if (this.game_stats.hasOwnProperty (game_stat)) {// crear un texto Phaser para cada stat juego que muestra la valuegame_stat_text final = newPhaser.Text (this.game_state.game, position.x, position.y, this.game_stats [game_stat] .text + this.game_stats [game_stat] .value, Object.create (esto. game_stats_text_style)); game_stat_text.anchor.setTo (0,5); this.game_state.groups.hud.add (game_stat_text); position.y + = 50;}}};

    Ahora que tenemos los GameStats Plugin creados, tenemos para añadirlo a nuestro juego. Hacemos esto llamando “this.game.plugins.add” en el final de LevelState “crear” método. Los parámetros de este método son la clase de complemento, el estado del juego y los datos del juego estadísticas, que se obtiene a partir del archivo de nivel JSON.
    this.game_stats = this.game.plugins.add (SignalExample.GameStats, esto, this.level_data.game_stats_data); 1this.game_stats = this.game.plugins.add (SignalExample.GameStats, esto, this.level_data.game_stats_data);

    Juego prefabricados

    Para comprobar si nuestros GameStats plug-in funciona correctamente, tenemos que crear las estructuras prefabricadas para nuestro juego. En este tutorial voy a probar este plugin en un juego de disparos simples espacio, pero no dude en ponerlo en práctica en su propio juego.

    Las casas prefabricadas que vamos a crear son la Nave, Bala, enemigo y EnemySpawner. Todos ellos se extiende la clase prefabricada mostrado antes.

    La nave prefabricada permitirá al jugador para mover la nave izquierda y derecha, y constantemente se balas disparar a sus enemigos. El constructor inicializa un temporizador que llama al método “disparar” de acuerdo a la tasa de disparar. Este método, por su parte, comienza comprobando si ya existe una bala muertos que pueden ser reutilizados. Si hay uno, sólo se restablece su posición a la posición actual del barco. Ohterwise, se crea una nueva bala.

    El método de “actualización” se encarga de mover el barco. El barco se mueve de acuerdo con el puntero del ratón. Si el jugador hace clic en la pantalla de la nave se mueve hacia la posición del cursor del ratón. Esto se hace por conseguir la posición “activePointer” y ajuste de la velocidad de la nave de acuerdo con ella.
    var SignalExample = SignalExample || {}; SignalExample.Ship = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; SignalExample.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.game_state.game.physics.arcade.enable (this); this.body.setSize (this.width * 0.3, this.height * 0,3); this.velocity = properties.velocity; this.bullet_velocity = properties.bullet_velocity; // crear e iniciar sesión temporizador this.shoot_timer = this.game_state.game.time.create (); this.shoot_timer.loop (Phaser.Timer.SECOND / properties.shoot_rate, this.shoot, este); this.shoot_timer.start ();}; SignalExample.Ship.prototype = Object.create (Phaser.Sprite.prototype); SignalExample.Ship.prototype.constructor = SignalExample.Ship; SignalExample.Ship.prototype.update = function () { «utilizar estricta»; target_x var; this.game_state.game.physics.arcade.overlap (esto, this.game_state.groups.enemy_bullets, this.kill, null, este); this.body.velocity.x = 0; // mover el barco cuando se hace clic en el ratón si (this.game_state.game.input.activePointer.isDown) {// obtener la posición seleccionada target_x = this.game_state.game.input.activePointer.x; si (target_x this.x) {// si la posición se ha hecho clic es derecho de la nave, mover a la derecha this.body.velocity.x = this.velocity; }}}; SignalExample.Ship.prototype.kill = function () { «utilizar estricta»; Phaser.Sprite.prototype.kill.call (this); // parada de rodaje this.shoot_timer.stop temporizador (); // en caso de fallecimiento de la nave, es más de juego this.game_state.game_over ();}; SignalExample.Ship.prototype.shoot = function () { «use strict»; bullet var, bullet_position, bullet_name, bullet_properties; // comprobar si hay una bala muertos a bala reutilización = this.game_state.groups.player_bullets.getFirstDead (); bullet_position = nuevo Phaser.Point (this.x, this.y); si (bala) {// si hay una bala muertos restablece a la posición actual bullet.reset (bullet_position.x, bullet_position.y); } Else {// si no hay ninguna bala muertos, crear un nuevo bullet_name uno this.name = + «_bullet» + this.game_state.groups.player_bullets.countLiving (); bullet_properties = {textura: «bullet_image», grupo: «player_bullets», dirección: -1, velocidad: this.bullet_velocity}; bullet = nuevo SignalExample.Bullet (this.game_state, bullet_name, bullet_position, bullet_properties); }}; 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768varSignalExample = SignalExample || {}; SignalExample.Ship = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; SignalExample.Prefab.call (esto, game_state, nombre, posición, propiedades); esto. anchor.setTo (0,5); this.game_state.game.physics.arcade.enable (this); this.body.setSize (this.width * 0.3, this.height * 0,3); this.velocity = properties.velocity; este .bullet_velocity = properties.bullet_velocity; // crear e iniciar sesión timerthis.shoot_timer = this.game_state.game.time.create (); this.shoot_timer.loop (Phaser.Timer.SECOND / properties.shoot_rate, this.shoot, este ); this.shoot_timer.start ();}; SignalExample.Ship.prototype = Object.create Phaser.Sprite.prototype) (; SignalExample.Ship.prototype.constructor = SignalExample.Ship; SignalExample.Ship.prototype.update = function () { «utilizar estricta»; vartarget_x; this.game_state.game.physics.arcade.overlap (esto, this.game_state.groups.enemy_bullets, this.kil l, null, este); this.body.velocity.x = 0; // mover el barco cuando el ratón está clickedif (this.game_state.game.input.activePointer.isDown) {// obtener el positiontarget_x hecho clic = esto. game_state.game.input.activePointer.x; si (target_x this.x) {// si la posición se ha hecho clic es derecho de la nave, mover rightthis.body.velocity.x = this.velocity;}}}; SignalExample.Ship.prototype.kill = function () { «use strict» ; Phaser.Sprite.prototype.kill.call (this); // parada de rodaje timerthis.shoot_timer.stop (); // en caso de fallecimiento de la nave, es overthis.game_state.game_over juego ();}; SignalExample.Ship. prototype.shoot = function () { «uso estricto»; varbullet, bullet_position, bullet_name, bullet_properties; // comprobar si hay una bala muerto para reusebullet = this.game_state.groups.player_bullets.getFirstDead (); bullet_position = newPhaser.Point (this.x, this.y), si (bala) {// si hay una bala muertos reconfigúrela a la actual positionbullet.rese t (bullet_position.x, bullet_position.y);} else {// si no hay ninguna bala muertos, crear un nuevo onebullet_name = this.name + «_ bala» + this.game_state.groups.player_bullets.countLiving (); bullet_properties = { textura: «bullet_image», grupo: «player_bullets», dirección: -1, velocidad: this.bullet_velocity}; bullet = newSignalExample.Bullet (this.game_state, bullet_name, bullet_position, bullet_properties);}};

    también, en el método de actualización que tenemos que comprobar cuando el barco se superpone con las balas enemigas y, si es así, acabar con él. Cuando el barco se mató, el juego debe terminar, lo que se hace en el método de “matar”. El método “game_over” se muestra a continuación, y simplemente muestran las estadísticas del juego de las finales GameStats plugin.
    SignalExample.LevelState.prototype.game_over = function () { «utilizar estricta»; //this.game.state.start(«BootState», verdadero, falso, «activos / niveles / level1.json», «LevelState»); this.game_stats.show_stats ();}; 12345SignalExample.LevelState.prototype.game_over = función () { «utilizar estricta»; // this.game.state.start ( «BootState», verdadero, falso, «activos / niveles / level1.json», «LevelState»); this.game_stats.show_stats ();};

    El prefabricada bala es muy simple, como se muestra a continuación. Simplemente crea el cuerpo físico bala y establece su velocidad.
    var SignalExample = SignalExample || {}; SignalExample.Bullet = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; SignalExample.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.game_state.game.physics.arcade.enable (this); this.body.velocity.y = properties.direction * properties.velocity;}; SignalExample.Bullet.prototype = Object.create (Phaser.Sprite.prototype); SignalExample.Bullet.prototype.constructor = SignalExample.Bullet; 123456789101112131415varSignalExample = SignalExample || {}; SignalExample.Bullet = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; SignalExample.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5) ; this.game_state.game.physics.arcade.enable (this); this.body.velocity.y = properties.direction * properties.velocity;}; SignalExample.Bullet.prototype = Object.create (Phaser.Sprite.prototype) ; SignalExample.Bullet.prototype.constructor = SignalExample.Bullet;

    el enemigo prefabricada es similar a la nave, donde la diferencia principal es que sólo se mueve verticalmente en la pantalla. Se establece su velocidad en el constructor y crea el temporizador de disparar, como la Nave. Su método de “disparar” es casi el mismo que el de la nave, excepto las balas son del grupo “enemy_bullets”, y tienen diferente dirección y velocidad. En el método de “actualización” que sólo tiene que comprobar si hay solapamientos con balas jugador.
    var SignalExample = SignalExample || {}; SignalExample.Enemy = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; SignalExample.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.game_state.game.physics.arcade.enable (this); this.velocity = properties.velocity; this.body.velocity.y = this.velocity; this.checkWorldBounds = true; this.outOfBoundsKill = true; this.bullet_velocity = properties.bullet_velocity; // crear e iniciar sesión temporizador this.shoot_timer = this.game_state.game.time.create (); this.shoot_timer.loop (Phaser.Timer.SECOND / properties.shoot_rate, this.shoot, este); this.shoot_timer.start ();}; SignalExample.Enemy.prototype = Object.create (Phaser.Sprite.prototype); SignalExample.Enemy.prototype.constructor = SignalExample.Enemy; SignalExample.Enemy.prototype.update = function () { «utilizar estricta»; // morir si las balas jugador toca this.game_state.game.physics.arcade.overlap (esto, this.game_state.groups.player_bullets, this.kill, null, este);}; SignalExample.Enemy.prototype.kill = function ( ) { «utilizar estricta»; Phaser.Sprite.prototype.kill.call (this); // dejar de disparar this.shoot_timer.pause ();}; SignalExample.Enemy.prototype.reset = función (x, y) { «uso estricto»; Phaser.Sprite.prototype.reset.call (esto, x, y); this.body.velocity.y = this.velocity; this.shoot_timer.resume ();}; SignalExample.Enemy.prototype.shoot = function () { «utilizar estricta»; bullet var, bullet_position, bullet_name, bullet_properties; // comprobar si hay una bala muertos a bala reutilización = this.game_state.groups.enemy_bullets.getFirstDead (); bullet_position = nuevo Phaser.Point (this.x, this.y); si (bala) {// si hay una bala muertos restablece a la posición actual bullet.reset (bullet_position.x, bullet_position.y); } Else {// si no hay ninguna bala muertos, crear un nuevo bullet_name uno this.name = + «_bullet» + this.game_state.groups.enemy_bullets.countLiving (); bullet_properties = {textura: «bullet_image», grupo: «enemy_bullets», dirección: 1, velocidad: this.bullet_velocity}; bullet = nuevo SignalExample.Bullet (this.game_state, bullet_name, bullet_position, bullet_properties); }}; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263varSignalExample = SignalExample || {}; SignalExample.Enemy = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; SignalExample.Prefab.call (esto, game_state, nombre, posición, propiedades); esto. anchor.setTo (0,5); this.game_state.game.physics.arcade.enable (this); this.velocity = properties.velocity; this.body.velocity.y = this.velocity; this.checkWorldBounds = true; esto. outOfBoundsKill = true; this.bullet_velocity = properties.bullet_velocity; // crear e iniciar sesión timerthis.shoot_timer = this.game_state.game.time.create (); this.shoot_timer.loop (Phaser.Timer.SECOND / properties.shoot_rate, this.shoot, este); this.shoot_timer.start ();}; SignalExample.Enemy.prototype = Object.create (Phaser.Sprite.prototype); SignalExample.Enemy.prototype.constructor = SignalExample.Enemy; SignalExample.Enemy. prototype.update=function(){«use strict»;// die if touches player bulletsthis.game_state.game.physics.arcade.overl ap(this,this.game_state.groups.player_bullets,this.kill,null,this);};SignalExample.Enemy.prototype.kill=function(){«use strict»;Phaser.Sprite.prototype.kill.call(this);// stop shootingthis.shoot_timer.pause();};SignalExample.Enemy.prototype.reset=function(x,y){«use strict»;Phaser.Sprite.prototype.reset.call(this,x,y);this.body.velocity.y=this.velocity;this.shoot_timer.resume();};SignalExample.Enemy.prototype.shoot=function(){«use strict»;varbullet,bullet_position,bullet_name,bullet_properties;// check if there is a dead bullet to reusebullet=this.game_state.groups.enemy_bullets.getFirstDead();bullet_position=newPhaser.Point(this.x,this.y);if(bullet){// if there is a dead bullet reset it to the current positionbullet.reset(bullet_position.x,bullet_position.y);}else{// if there is no dead bullet, create a new onebullet_name=this.name+»_bullet»+this.game_state.groups.enemy_bullets.countLiving();bullet_properties={texture:»bullet_image»,group:»enemy_bullets»,direction:1,velocity:this.bulle t_velocity};bullet=newSignalExample.Bullet(this.game_state,bullet_name,bullet_position,bullet_properties);}};

    Notice that in the Enemy prefab “kill” method we have to pause the shoot timer, and in the “reset” method we have to resume it and set the enemy velocity again. This happens because in our game enemies will be constantly spawning, and we want to reuse dead enemies, so we have to properly handle timers and the physical body when they are reused.

    Finally, the EnemySpawner is shown below. In the constructor it starts a loop event that calls the “spawn” method. This method is responsible for creating enemies using the same strategy we are using to create the bullets: first, it checks if there is a dead enemy to reuse and if so, resets it to the desired position. Otherwise, it creates a new enemy. The main difference here is that the position is random between 10% and 90% of the game world width. We generate this random position using Phaser RandomDataGenerator (you can check the documentation if you are not familiar with it).

    var SignalExample = SignalExample || {};SignalExample.EnemySpawner = function (game_state, name, position, properties) { «use strict»; SignalExample.Prefab.call(this, game_state, name, position, properties); this.enemy_properties = properties.enemy_properties; // start spawning event this.game_state.game.time.events.loop(Phaser.Timer.SECOND * properties.spawn_interval, this.spawn, this);};SignalExample.EnemySpawner.prototype = Object.create(Phaser.Sprite.prototype);SignalExample.EnemySpawner.prototype.constructor = SignalExample.EnemySpawner;SignalExample.EnemySpawner.prototype.spawn = function () { «use strict»; var enemy, enemy_position, enemy_name, enemy_properties; // check if there is a dead enemy to reuse enemy = this.game_state.groups.enemies.getFirstDead(); // spawn enemy in a random position inside the world enemy_position = new Phaser.Point(this.game_state.game.rnd.between(0.1 * this.game_state.game.world.width, 0.9 * this.game_state.game.world.width), this.y); if (enemy) { // if there is a dead enemy reset it to the current position enemy.reset(enemy_position.x, enemy_position.y); } else { // if there is no dead enemy, create a new one enemy_name = this.name + «_enemy» + this.game_state.groups.enemies.countLiving(); enemy = new SignalExample.Enemy(this.game_state, enemy_name, enemy_position, this.enemy_properties); }};12345678910111213141516171819202122232425262728293031varSignalExample=SignalExample||{};SignalExample.EnemySpawner=function(game_state,name,position,properties){«use strict»;SignalExample.Prefab.call(this,game_state,name,position,properties);this.enemy_properties=properties.enemy_properties;// start spawning eventthis.game_state.game.time.events.loop(Phaser.Timer.SECOND*properties.spawn_interval,this.spawn,this);};SignalExample.EnemySpawner.prototype=Object.create(Phaser.Sprite.prototype);SignalExample.EnemySpawner.prototype.constructor=SignalExample.EnemySpawner;SignalExample.EnemySpawner.prototype.spawn=function(){«use strict»;varenemy,enemy_position,enemy_name,enemy_properties;// check if there is a dead enemy to reuseenemy=this.game_state.groups.enemies.getFirstDead();// spawn enemy in a random position inside the worldenemy_position=newPhaser.Point(this.game_state.game.rnd.between(0.1*this.game_state.game.world.width,0.9*this.game_state.game.world.width),this.y);if(enemy){// if there is a dead enemy reset it to the current positionenemy.reset(enemy_position.x,enemy_position.y);}else{// if there is no dead enemy, create a new oneenemy_name=this.name+»_enemy»+this.game_state.groups.enemies.countLiving();enemy=newSignalExample.Enemy(this.game_state,enemy_name,enemy_position,this.enemy_properties);}};

    By now, you can try playing the game without the events, so it will work but the game statistics won’t be saved. This way you can check if everything is working before moving on to add the game signals.

    game

    Adding the signals

    We are going to create two signals to save game statistics: onShoot, dispatched when the ship shoots, and onSpawn, dispatched when the EnemySpawner spawns an enemy.

    The onShoot signal is created in the Ship constructor, and is dispatched in the “shoot” method, as shown below. The onSpawn signal, by its turn is created in the constructor of EnemySpawner, and dispatched in the “spawn” method. Notice that both signals have to send the sprite as a parameter dispatching the event, since the GameStats plugin is expecting it.

    SignalExample.Ship = function (game_state, name, position, properties) { «use strict»; SignalExample.Prefab.call(this, game_state, name, position, properties); this.anchor.setTo(0.5); this.game_state.game.physics.arcade.enable(this); this.body.setSize(this.width * 0.3, this.height * 0.3); this.velocity = properties.velocity; this.bullet_velocity = properties.bullet_velocity; // create and start shoot timer this.shoot_timer = this.game_state.game.time.create(); this.shoot_timer.loop(Phaser.Timer.SECOND / properties.shoot_rate, this.shoot, this); this.shoot_timer.start(); // creating shooting event to be listened this.events.onShoot = new Phaser.Signal();};SignalExample.Ship.prototype.shoot = function () { «use strict»; var bullet, bullet_position, bullet_name, bullet_properties; // check if there is a dead bullet to reuse bullet = this.game_state.groups.player_bullets.getFirstDead(); bullet_position = new Phaser.Point(this.x, this.y); if (bullet) { // if there is a dead bullet reset it to the current position bullet.reset(bullet_position.x, bullet_position.y); } else { // if there is no dead bullet, create a new one bullet_name = this.name + «_bullet» + this.game_state.groups.player_bullets.countLiving(); bullet_properties = {texture: «bullet_image», group: «player_bullets», direction: -1, velocity: this.bullet_velocity}; bullet = new SignalExample.Bullet(this.game_state, bullet_name, bullet_position, bullet_properties); } // dispatch shoot event this.events.onShoot.dispatch(this);};12345678910111213141516171819202122232425262728293031323334353637383940SignalExample.Ship=function(game_state,name,position,properties){«use strict»;SignalExample.Prefab.call(this,game_state,name,position,properties);this.anchor.setTo(0.5);this.game_state.game.physics.arcade.enable(this);this.body.setSize(this.width*0.3,this.height*0.3);this.velocity=properties.velocity;this.bullet_velocity=properties.bullet_velocity;// create and start shoot timerthis.shoot_timer=this.game_state.game.time.create();this.shoot_timer.loop(Phaser.Timer.SECOND/properties.shoot_rate,this.shoot,this);this.shoot_timer.start();// creating shooting event to be listenedthis.events.onShoot=newPhaser.Signal();};SignalExample.Ship.prototype.shoot=function(){«use strict»;varbullet,bullet_position,bullet_name,bullet_properties;// check if there is a dead bullet to reusebullet=this.game_state.groups.player_bullets.getFirstDead();bullet_position=newPhaser.Point(this.x,this.y);if(bullet){// if there is a dead bullet reset it to the current positionbullet.reset(bullet_position.x,bullet_position.y);}else{// if there is no dead bullet, create a new onebullet_name=this.name+»_bullet»+this.game_state.groups.player_bullets.countLiving();bullet_properties={texture:»bullet_image»,group:»player_bullets»,direction:-1,velocity:this.bullet_velocity};bullet=newSignalExample.Bullet(this.game_state,bullet_name,bullet_position,bullet_properties);}// dispatch shoot eventthis.events.onShoot.dispatch(this);};SignalExample.EnemySpawner = function (game_state, name, position, properties) { «use strict»; SignalExample.Prefab.call(this, game_state, name, position, properties); this.enemy_properties = properties.enemy_properties; // start spawning event this.game_state.game.time.events.loop(Phaser.Timer.SECOND * properties.spawn_interval, this.spawn, this); // create spawn event to be listened this.events.onSpawn = new Phaser.Signal();};SignalExample.EnemySpawner.prototype.spawn = function () { «use strict»; var enemy, enemy_position, enemy_name, enemy_properties; // check if there is a dead enemy to reuse enemy = this.game_state.groups.enemies.getFirstDead(); // spawn enemy in a random position inside the world enemy_position = new Phaser.Point(this.game_state.game.rnd.between(0.1 * this.game_state.game.world.width, 0.9 * this.game_state.game.world.width), this.y); if (enemy) { // if there is a dead enemy reset it to the current position enemy.reset(enemy_position.x, enemy_position.y); } else { // if there is no dead enemy, create a new one enemy_name = this.name + «_enemy» + this.game_state.groups.enemies.countLiving(); enemy = new SignalExample.Enemy(this.game_state, enemy_name, enemy_position, this.enemy_properties); } // dispatch spawn event this.events.onSpawn.dispatch(this);};1234567891011121314151617181920212223242526272829303132SignalExample.EnemySpawner=function(game_state,name,position,properties){«use strict»;SignalExample.Prefab.call(this,game_state,name,position,properties);this.enemy_properties=properties.enemy_properties;// start spawning eventthis.game_state.game.time.events.loop(Phaser.Timer.SECOND*properties.spawn_interval,this.spawn,this);// create spawn event to be listenedthis.events.onSpawn=newPhaser.Signal();};SignalExample.EnemySpawner.prototype.spawn=function(){«use strict»;varenemy,enemy_position,enemy_name,enemy_properties;// check if there is a dead enemy to reuseenemy=this.game_state.groups.enemies.getFirstDead();// spawn enemy in a random position inside the worldenemy_position=newPhaser.Point(this.game_state.game.rnd.between(0.1*this.game_state.game.world.width,0.9*this.game_state.game.world.width),this.y);if(enemy){// if there is a dead enemy reset it to the current positionenemy.reset(enemy_position.x,enemy_position.y);}else{// if there is no dead enemy, create a new oneenemy_name=this.name+»_enemy»+this.game_state.groups.enemies.countLiving();enemy=newSignalExample.Enemy(this.game_state,enemy_name,enemy_position,this.enemy_properties);}// dispatch spawn eventthis.events.onSpawn.dispatch(this);};

    Finally, we call the “listen_to_events” method from the plugin after adding it to LevelState, so it will add the listeners described in the JSON file (shown before), which will be called when the new signals are dispatched.

    this.game_stats.listen_to_events();1this.game_stats.listen_to_events();

    By now, you can already try playing the game with the game statistics. Check if all statistics are being correctly saved, and try adding different signals.

    game_stats

    Creating an achievement system

    Before finishing the tutorial, I would like to briefly show how easily you can change the GameStats plugin to an GameAchievements plugin. There are two main differences between those plugins:

    1. For the GameAchievements plugin it would be interesting to have listeners to specific prefabs, not only groups. For example, the game might have an achievement when a given boss is defeated, so it should be listened to that specific boss.
    2. The callback of the listeners in the GameAchievements plugin would be more complex than simply saving a game statistic, so it will probably be necessary to have different callbacks for different achivements.
    3. Apart from those two differences, the signals and listeners can be created the same way. Try creating achievements for your game, and see how they work!

      Mensajes relacionados
      The Complete Mobile Game Development Course – Platinum Edition

Deja una respuesta

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