Algunos juegos tienen un número fijo de niveles creados por un diseñador de niveles. De esta manera, el diseñador puede crear los niveles a fin de proporcionar la experiencia de juego deseado para el jugador. Sin embargo, se reduce el valor de repetición del juego, ya que los niveles siempre será el mismo cada vez que se juegan.
Otra alternativa es generar procesalmente niveles usando un algoritmo en lugar de un diseñador de niveles. Esto resulta en un número virtualmente infinito de niveles, y el jugador nunca jugarán el mismo nivel dos veces. Sin embargo, dado que los diseñadores de juegos no tienen un control total sobre los niveles, es probable que no sea tan bueno como si fueran generadas por un diseñador de niveles experimentados.
Cada tipo de juego de nivel (manual o de procedimiento) tiene sus ventajas y desventajas, y se puede aplicar en diferentes tipos de juegos (ambas estrategias, incluso se podrían utilizar en el mismo juego). En este tutorial, vamos a procesalmente crear un calabozo con varias habitaciones, utilizando un algoritmo simple. Tenga en cuenta que hay varias formas de hacerlo (como se puede comprobar aquí), y cada uno puede ser recomendado para un tipo específico de juego. Por lo tanto, si usted está construyendo un juego y necesidad contenidos procedimiento generado (no sólo los niveles), echar un vistazo en diferentes enfoques para ver cuál se ajusta mejor a su juego.
En primer lugar, se explicará el algoritmo que vamos a utilizar para generar nuestra mazmorra. A continuación, vamos a construir una maqueta que crea un calabozo con varias habitaciones y cargarlos usando mapas de baldosas.
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.

    archivos de código fuente

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

    Creación de la baldosa mapas

    Vamos a empezar por la creación de plantillas de salas utilizando baldosa para cada habitación es posible en nuestro juego. Entonces, nuestro juego se carga el mapa correcto de la habitación de acuerdo a la configuración actual de la habitación. Por ejemplo, si el jugador se encuentra actualmente en una habitación con una sola puerta en la dirección norte, se debe cargar el mapa “room_N.json”.

    La siguiente figura muestra un ejemplo de habitación creado usando baldosa. Si no está familiarizado con la baldosa, se puede comprobar una de mis tutoriales anteriores, en el que se cubre con más detalles. Incluso si usted está familiarizado con él, le recomiendo el uso de los mapas proporcionados por el código fuente, ya que es necesario crear una para cada configuración de la sala (que resulta en un total de 15 mapas). Si aún desea crear sus propios mapas, las cosas sólo se requiere a seguir es que se debe crear una capa llamada colisión con un conjunto de propiedades de colisión de verdad, y debe establecer las propiedades del objeto, como se muestra a continuación, ya que esto será necesario para nuestra demo.

    mapa layer_properties object_properties

    El algoritmo de generación Mazmorra

    Vamos a generar procesalmente un calabozo con varias habitaciones en una estructura de rejilla. Lo haremos mediante la creación de un número determinado de habitaciones por viajar una cuadrícula y, a continuación, la conexión de las habitaciones de los vecinos.
    Dada una rejilla y un número deseado de habitaciones “n”, generamos la mazmorra con los siguientes pasos:


    1. Agregue el cordinate de la mitad de la red en una lista llamada “rooms_to_create”
    2. Si bien el número de habitaciones creadas es menor que “n”, repita: obtener la primera coordenada en “rooms_to_create” Crear una habitación con este coordinateAdd esta habitación en un “created_rooms” listAdd un número aleatorio de coordenadas vecino de la sala actual a “rooms_to_create”
    3. Después de todas las habitaciones son creados, conectar todas las habitaciones vecinas

      Tenga en cuenta que, para este algoritmo para el trabajo que debe garantizar que cada iteración del bucle while en el paso dos se suma al menos un nuevo espacio para “rooms_to_create”, por lo que el algoritmo puede continuar hasta que se crean todas las habitaciones. Asimismo, garantizamos que al hacer la rejilla lo suficientemente grande por lo que la mazmorra siempre se puede ampliar a cualquier dirección (esto es posible si la anchura y altura de la rejilla es el doble del número de habitaciones, por ejemplo) y forzando el último paso del tiempo bucle para añadir siempre al menos un vecino a coordinar “rooms_to_create”.
      El código siguiente muestra la implementación de este algoritmo. El algoritmo se ejecuta en el método “generate_dungeon”. En primer lugar, inicializar la red con sus dimensiones es igual a dos veces el número de habitaciones, y añadir la mitad de coordenadas a la lista “rooms_to_create”. A continuación, el bucle while repite el proceso descrito por el algoritmo, la creación de una nueva habitación y la adición de un número aleatorio de vecinos para “rooms_to_create”. Después de crear todas las habitaciones, itera a través de todos ellos conectarlos.
      var ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.Dungeon = function (game_state) { «utilizar estricta»; this.TILE_DIMENSIONS = nuevo Phaser.Point (32, 32); this.game_state = game_state;}; ProceduralGeneration.Dungeon.prototype.generate_dungeon = function (number_of_rooms) { «utilizar estricta»; var grid_size, rooms_to_creates, current_room_coordinate, current_room, created_rooms, initial_room_coordinate, final_room_coordinate, max_distance_to_initial_room, distance_to_initial_room; // inicializar el grid_size rejilla = 2 * number_of_rooms; this.initialize_grid (grid_size); // añadir coordinar el medio como initial_room_coordinate inicial = nuevo Phaser.Point ((grid_size / 2) – 1, (grid_size / 2) – 1); rooms_to_creates = []; rooms_to_creates.push ({consecutivas: initial_room_coordinate.y, columna: initial_room_coordinate.x}); created_rooms = []; // iterate crear habitaciones, mientras que (rooms_to_creates.length> 0 && created_rooms.length 0 && created_rooms.length el método de “check_for_neighbors” es responsable de la adición de los vecinos al azar para “rooms_to_create”. En primer lugar, se encuentra la forma no están ocupados sin embargo, muchos coordenadas vecino, ponerlos en la lista “available_neighbors”. A continuación, selecciona el número de vecinos que serán creados al azar, usando generador de datos aleatorios Phaser (se puede aprender más de ella, en la documentación Phaser). Por último, para coordinar a crearse cada vecino, se elige al azar una de las vecinas disponibles. Esto se realiza mediante la asignación de un rango a cada vecino disponible y generar un número aleatorio entre 0 y 1. El vecino elegido es aquel cuya gama contiene el número generado. Por ejemplo, si hay dos vecinos disponibles, el primero será seleccionado si el número generado es de hasta 0,5, de lo contrario se selecciona el segundo vecino.
      Todavía tenemos que crear nuestra clase de habitaciones, que se muestra a continuación. La habitación tiene sus coordenadas y debe ser capaz de informar a sus vecinos y el nombre de su archivo de mapa. Los “neighbor_coordinates” método simplemente devuelve las coordenadas vecino en cada dirección. El método de “conexión” se utiliza para definir los vecinos que realmente existe y la “TEMPLATE_NAME” devuelve el método del nombre del mapa JSON para ser cargado para esta sala. Este nombre siempre empieza con “room_” y es seguido por las direcciones que hay habitaciones, en orden a la derecha. Por ejemplo, si la habitación tiene puertas en el norte, sur y oeste direcciones, el nombre de la plantilla es “room_NSW.json”.
      var ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.Room = function (game_state, coordinar, tile_dimensions) { «utilizar estricta»; this.game_state = game_state; this.coordinate = coordenadas; this.tile_dimensions = tile_dimensions; this.neighbors = {};}; ProceduralGeneration.Room.prototype.neighbor_coordinates = function () { «utilizar estricta»; neighbor_coordinates var; neighbor_coordinates = [{dirección: «N», fila: this.coordinate.row – 1, columna: this.coordinate.column}, {dirección: «E», fila: this.coordinate.row, columna: this.coordinate. columna + 1}, {dirección: «S», fila: this.coordinate.row + 1, columna: this.coordinate.column}, {dirección: «W», fila: this.coordinate.row, columna: esto. coordinate.column – 1}]; neighbor_coordinates retorno;}; ProceduralGeneration.Room.prototype.connect = función (dirección, sala) { «utilizar estricta»; this.neighbors [dirección] = habitación;}; ProceduralGeneration.Room.prototype.template_name = function () { «utilizar estricta»; template_name var; // el nombre de la plantilla es room_ seguido por las direcciones con los vecinos template_name = «room_»; this.neighbor_coordinates () forEach (function (coordenadas) {if (this.neighbors [coordinate.direction]) {template_name + = coordinate.direction;}}, esto).; template_name + = «.json»; retorno template_name;}; 1234567891011121314151617181920212223242526272829303132333435363738394041varProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.Room = function (game_state, coordinar, tile_dimensions) { «utilizar estricta»; this.game_state = game_state; this.coordinate = coordenada; this.tile_dimensions = tile_dimensions; este .neighbors = {};}; ProceduralGeneration.Room.prototype.neighbor_coordinates = function () { «utilizar estricta»; varneighbor_coordinates; neighbor_coordinates = [{dirección: «N», fila: 1-this.coordinate.row, columna: esta .coordinate.column}, {dirección: «E», fila: this.coordinate.row, columna: this.coordinate.column + 1}, {dirección: «S», fila: this.coordinate.row + 1, columna : this.coordinate.column}, {dirección: «W», fila: this.coordinate.row, columna: this.coordinate.column-1}]; returnneighbor_coordinates;}; ProceduralGeneration.Room.prototype.connect = función (dirección , habitación) { «utilizar estricta»; this.neighbors [dirección] = habitación;}; ProceduralGeneration.Room.prototype.template_name = function () { «utilizar estricta»; vartemplate_name; // el nombre de la plantilla es room_ seguidas por las direcciones con neighborstemplate_name = «habitación _»; this.neighbor_coordinates () forEach (function (coordenadas) {if (this.neighbors [coordinate.direction]) {template_name + = coordinate.direction;}}., este ); template_name + = «JSON.»; returntemplate_name;};

      Phaser estados de nuestra demo

      Vamos a guardar los datos de nivel de nuestra demo en un archivo JSON, que será leído cuando se inicia. El archivo JSON que voy a la práctica se muestra a continuación. Este archivo se definirán los recursos del juego y de los grupos, que será el mismo para cualquier habitación. De esta manera podemos precargar todos los activos en un LoadingState y crear todos los grupos antes de cargar el mapa. Desde el archivo de mapa será diferente para cada habitación, se pasa como un parámetro, en lugar de ser definido en el archivo JSON.
      { «Activos»: { «dungeon_tileset»: { «type»: «imagen», «fuente»: «assets / images / terrains.png»}, «hero_spritesheet»: { «type»: «spritesheet», «fuente» : «assets / images / player.png», «frame_width»: 31, «frame_height»: 30}}, «grupos»: [ «puertas», «héroes»]} {12345678910 «activos»: { «dungeon_tileset»: { «type»: «imagen», «fuente»: «assets / images / terrains.png»}, «hero_spritesheet»: { «type»: «spritesheet», «fuente»: «assets / images / player.png» «frame_width»: 31, «frame_height»: 30}}, «grupos»: [ «puertas», «Heroes»]}

      Nuestra demostración tendrá cuatro estados: BootState, LoadingState, DungeonState y RoomState. Los Frist dos estados son simples y responsable de cargar el archivo JSON juego y todos los elementos necesarios antes de iniciar cualquier habitación. Sus códigos se muestran a continuación. Se puede ver que BootState simplemente carga el archivo JSON en su método “precarga” y se inicia el LoadingState en el método de “crear”. El LoadingState, por su vez, las cargas de todos los activos en el método de “precarga”, utilizando el tipo de activo para llamar al método Phaser apropiado. Cuando se cargan todos los activos, se inicia el estado siguiente (en la variable “next_state”) en el método de “crear”.
      var ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.BootState = function () { «utilizar estricta»; Phaser.State.call (this);}; ProceduralGeneration.BootState.prototype = Object.create (Phaser.State.prototype); ProceduralGeneration.BootState.prototype.constructor = ProceduralGeneration.BootState; ProceduralGeneration.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;}; ProceduralGeneration.BootState.prototype.preload = function () { «utilizar estricta»; this.load.text ( «level1», this.level_file);}; ProceduralGeneration.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);}; 1234567891011121314151617181920212223242526272829varProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.BootState = function () { «utilizar estricta»; Phaser.State.call (this);}; ProceduralGeneration.BootState.prototype = Object.create (Phaser.State.prototype); ProceduralGeneration.BootState.prototype.constructor = ProceduralGeneration.BootState; ProceduralGeneration.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;}; ProceduralGeneration.BootState.prototype.preload = function () { «utilizar estricta»; esta .load.text ( «level1», this.level_file);}; ProceduralGeneration.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 ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.LoadingState = function () { «utilizar estricta»; Phaser.State.call (this);}; ProceduralGeneration.LoadingState.prototype = Object.create (Phaser.State.prototype); ProceduralGeneration.LoadingState.prototype.constructor = ProceduralGeneration.LoadingState; ProceduralGeneration.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;}; ProceduralGeneration.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; }}}}; ProceduralGeneration.LoadingState.prototype.create = function () { «utilizar estricta»; this.game.state.start (this.next_state, verdadero, falso, this.level_data, this.extra_parameters);}; 12345678910111213141516171819202122232425262728293031323334353637383940414243varProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.LoadingState = function () { «utilizar estricta»; Phaser. State.call (this);}; ProceduralGeneration.LoadingState.prototype = Object.create (Phaser.State.prototype); ProceduralGeneration.LoadingState.prototype.constructor = ProceduralGeneration.LoadingState; ProceduralGeneration.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;}; ProceduralGeneration.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 (ASSE t_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, asset.source, null, Phaser.Tilemap.TILED_JSON); break;}}}}; ProceduralGeneration.LoadingState.prototype.create = function () { «utilizar estricta»; esta .game.state.start (this.next_state, verdadero, falso, this.level_data, this.extra_parameters);};

      el DungeonState será responsable de generar la mazmorra, y se muestra a continuación. Se inicializa el objeto de Calabozo en el método “init” y generar una nueva mazmorra en el método de “crear”. Después de generar la mazmorra se inicia un RoomState con la habitación inicial de la cisterna.
      var ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.DungeonState = function () { «utilizar estricta»; Phaser.State.call (this); this.LEVEL_FILE = «activos / niveles / room_level.json»;}; ProceduralGeneration.DungeonState.prototype = Object.create (Phaser.State.prototype); ProceduralGeneration.DungeonState.prototype.constructor = ProceduralGeneration.DungeonState; ProceduralGeneration.DungeonState.prototype .init = function (number_of_rooms) { «uso estricto»; this.number_of_rooms = number_of_rooms; this.dungeon = this.dungeon || nuevo ProceduralGeneration.Dungeon (this);}; ProceduralGeneration.DungeonState.prototype.create = function () { «utilizar estricta»; var initial_room; // generar nuevos mazmorra initial_room = this.dungeon.generate_dungeon (this.number_of_rooms); // iniciar RoomState por la habitación inicial de la this.game.state.start mazmorra ( «RoomState» «BootState», verdadero, falso, this.LEVEL_FILE,, {habitación: initial_room});}; 1234567891011121314151617181920212223242526varProceduralGeneration = ProceduralGeneration || { }; ProceduralGeneration.DungeonState = function () { «utilizar estricta»; Phaser.State.call (this); this.LEVEL_FILE = «activos / niveles / room_level.json»;}; ProceduralGeneration.DungeonState.prototype = Object.create ( Phaser.State.prototype); ProceduralGeneration.DungeonState.prototype.constructor = ProceduralGeneration.DungeonState; ProceduralGeneration.DungeonState.prototype.init = function (number_of_rooms) { «uso estricto»; this.number_of_rooms = number_of_rooms; this.dungeon = this.dungeon || newProceduralGeneration.Dungeon (este);}; ProceduralGeneration.DungeonState.prototype.create = function () { «use strict»; varinitial_room; // Crear nuevo dungeoninitial_room = this.dungeon.generate_dungeon (this.number_of_rooms); // inicio RoomState por la habitación inicial de la dungeonthis.game.state.start ( «BootState», verdadero, falso, this.LEVEL_FILE, «RoomState», {habitación: initial_room});};

      Finalmente, RoomState es donde la mayor parte del juego se ejecutará. En el método “init” se inicia el motor de física, establece la escala y guardar los datos de otros métodos. En el método de “precarga” se carga el archivo de asignación propuesta por el nombre de la plantilla habitación. Luego, en el método de “crear” se crea el mapa, las capas del mapa, los grupos y las casas prefabricadas. Tenga en cuenta que al crear las capas que comprobar si hay colisión de la propiedad para que sea Collidable. El método “create_object” es responsable de la creación de las casas prefabricadas. En primer lugar, se ajusta las posiciones, ya baldosa y Phaser sistemas de coordenadas son diferentes, entonces es una instancia del correcto prefabricada mediante la propiedad “prefab_classes” (inicialmente vacía). Tenga en cuenta que, esto es posible porque todas las casas prefabricadas tienen el mismo constructor, definido en su clase base se muestra a continuación.
      var ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.RoomState = function () { «utilizar estricta»; Phaser.State.call (this); this.MAP_KEY = «room_tilemap»; this.MAP_TILESET = «dungeon_tileset»; this.prefab_classes = {};}; ProceduralGeneration.RoomState.prototype = Object.create (Phaser.State.prototype); ProceduralGeneration.RoomState.prototype.constructor = ProceduralGeneration.RoomState; ProceduralGeneration.RoomState.prototype.init = function (level_data, extra_parameters) { «utilizar estricta»; tileset_index var, tile_dimensions; 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; // obtener actual de la habitación this.room = extra_parameters.room;}; ProceduralGeneration.RoomState.prototype.preload = function () { «use strict»; this.load.tilemap (this.MAP_KEY, «activos / Mapas /» + this.room.template_name (), null, Phaser.Tilemap.TILED_JSON);}; ProceduralGeneration.RoomState.prototype.create = function () { «uso estricto»; GROUP_NAME var, object_layer, collision_tiles; // crear el mapa this.map = this.game.add.tilemap (this.MAP_KEY); this.map.addTilesetImage (this.map.tilesets [0] .name, this.MAP_TILESET); // 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 = {}; // crear objetos (prefabricadas) para (object_layer en this.map.objects) {if (this.map.objects.hasOwnProperty (object_layer)) {// crear capa de objetos this.map.objects [object_layer] .forEach (esto. create_object, este); }}}; ProceduralGeneration.RoomState.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;}; 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495varProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.RoomState = function () { «utilizar estricta»; Phaser.State.call (this); this.MAP_KEY = «room_tilemap «; this.MAP_TILESET =» dungeon_tileset «; this.prefab_classes = {};}; ProceduralGeneration.RoomState.prototype = Object.create (Phaser.State.prototype); ProceduralGeneration.RoomState.prototype.constructor = ProceduralGeneration.RoomState; ProceduralGeneration. RoomState.prototype.init = function (level_data, extra_parameters) { «utilizar estricta»; vartileset_index, tile_dimensions; this.level_data = this.level_data || level_data; this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true; // inicia la física systemthis.game.physics.startSystem (Phaser.Physics.ARCADE); this.game.physics.arcade.gr avity.y = 0; // obtener roomthis.room actual = extra_parameters.room;}; ProceduralGeneration.RoomState.prototype.preload = function () { «use strict»; this.load.tilemap (this.MAP_KEY, «activos / mapas / «+ this.room.template_name (), null, Phaser.Tilemap.TILED_JSON);}; ProceduralGeneration.RoomState.prototype.create = function () {» utilizar estricta «; vargroup_name, object_layer, collision_tiles; // crear MapThis .map = this.game.add.tilemap (this.MAP_KEY); this.map.addTilesetImage (this.map.tilesets [0] .name, this.MAP_TILESET); // crear layersthis.layers mapa = {}; este .map.layers.forEach (función (capa) {this.layers [layer.name] = this.map.createLayer (layer.name), si (layer.properties.collision) {// layercollision_tiles colisión = []; capa .data.forEach (function (data_row) {// encontrar azulejos utilizados en la layerdata_row.forEach (function (teja) {// Comprobar 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_tile s, es cierto, layer.name);}}, this); // cambiar el tamaño del mundo para ser el tamaño de los actuales layerthis.layers [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 = {}; // crear objetos (prefabricadas) para (inthis.map.objects object_layer) {if (this.map.objects.hasOwnProperty (object_layer)) {// crear objectsthis.map.objects capa [object_layer] .forEach (this.create_object, este);} }}; ProceduralGeneration.RoomState.prototype.create_object = function (objeto) { «use strict»; varobject_y, posición, prefabricada; // coordenadas de azulejos se inicia en la parte inferior izquierda cornerobject_y = (object.gid) object.y- (esto? .map.tileHeight / 2): object.y + (object.height / 2); posición = { «x»: object.x + (this.map.tileHeight / 2), «y»: object_y}; // crear el objeto según 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;}; var ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.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); } This.game_state.prefabs [nombre] = esta;}; ProceduralGeneration.Prefab.prototype = Object.create (Phaser.Sprite.prototype); ProceduralGeneration.Prefab.prototype.constructor = ProceduralGeneration.Prefab; 12345678910111213141516171819202122varProceduralGeneration = ProceduralGeneration || {} ; ProceduralGeneration.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);} this.game_state.prefabs [nombre] = esta;}; ProceduralGeneration.Prefab.prototype = Object.create (Phaser.Sprite.prototype); ProceduralGeneration.Prefab.prototype.constructor = ProceduralGeneration. prefabricada;

      Por ahora se puede ya intente ejecutar la demo para ver si su corrección de la carga de los mapas de las habitaciones. Pruebe a ejecutar varias veces y ver si la habitación inicial está cambiando.

      mazmorra

      Navegación por las habitaciones

      Usted puede haber notado la baldosa mapas proporcionados en el código fuente tienen héroe y puertas de las casas prefabricadas, que se utilizan para permitir que nuestro héroe para navegar a través de las salas de las mazmorras. Ahora, vamos a ponerlas en práctica.
      En primer lugar, el código prefabricada héroe se muestra a continuación. Lo único que va a hacer por ahora es a pie, por lo que sólo necesita una propiedad “walking_speed”. En el método de “actualización”, comprobamos para la entrada de jugador para mover el héroe, que se realiza con el objeto “cursores”. Para controlar adecuadamente el héroe, lo movemos a una dirección dada sólo si aún no lo está moviendo en la dirección opuesta. Por ejemplo, el héroe puede mover hacia la izquierda sólo si aún no lo está moviendo bien. Además, si el jugador se mueve, hay que reproducir la animación caminar, de lo contrario detenerlo.
      var ProceduralGeneration = ProceduralGeneration || {}; ProceduralGeneration.Hero = function (game_state, nombre, posición, propiedades) { «utilizar estricta»; ProceduralGeneration.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.walking_speed = + properties.walking_speed; this.game_state.game.physics.arcade.enable (this); this.body.collideWorldBounds = true; this.animations.add ( «pie», [0, 1], 6, true); this.cursors = this.game_state.game.input.keyboard.createCursorKeys();};ProceduralGeneration.Hero.prototype = Object.create(ProceduralGeneration.Prefab.prototype);ProceduralGeneration.Hero.prototype.constructor = ProceduralGeneration.Hero;ProceduralGeneration.Hero.prototype.update = function () { «use strict»; this.game_state.game.physics.arcade.collide (esto, this.game_state.layers.collision); if (this.cursors.left.isDown && this.body.velocity.x <= 0) { // move left if is not already moving right this.body.velocity.x = -this.walking_speed; this.scale.setTo (1, 1); } else if (this.cursors.right.isDown && this.body.velocity.x >= 0) { // move right if is not already moving left this.body.velocity.x = +this.walking_speed; this.scale.setTo (-1, 1); } Else {this.body.velocity.x = 0; } if (this.cursors.up.isDown && this.body.velocity.y <= 0) { // move up if is not already moving down this.body.velocity.y = -this.walking_speed; } else if (this.cursors.down.isDown && this.body.velocity.y >= 0) { // move down if is not already moving up this.body.velocity.y = +this.walking_speed; } else { this.body.velocity.y = 0; } if (this.body.velocity.x === 0 && this.body.velocity.y === 0) { this.animations.stop(); this.frame = 0; } else { // if not moving, stop the animation this.animations.play(«walking»); }};123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051varProceduralGeneration=ProceduralGeneration||{};ProceduralGeneration.Hero=function(game_state,name,position,properties){«use strict»;ProceduralGeneration.Prefab.call(this,game_state,name,position,properties);this.anchor.setTo(0.5);this.walking_speed=+properties.walking_speed;this.game_state.game.physics.arcade.enable(this);this.body.collideWorldBounds=true;this.animations.add(«walking»,[0,1],6,true);this.cursors=this.game_state.game.input.keyboard.createCursorKeys();};ProceduralGeneration.Hero.prototype=Object.create(ProceduralGeneration.Prefab.prototype);ProceduralGeneration.Hero.prototype.constructor=ProceduralGeneration.Hero;ProceduralGeneration.Hero.prototype.update=function(){«use strict»;this.game_state.game.physics.arcade.collide(this,this.game_state.layers.collision);if(this.cursors.left.isDown&& this.body.velocity.x <= 0) { // move left if is not already moving right this.body.velo city.x = -this.walking_speed;this.scale.setTo(1,1);}elseif(this.cursors.right.isDown&& this.body.velocity.x >= 0) { // move right if is not already moving left this.body.velocity.x = +this.walking_speed;this.scale.setTo(-1,1);}else{this.body.velocity.x=0;}if(this.cursors.up.isDown&& this.body.velocity.y <= 0) { // move up if is not already moving down this.body.velocity.y = -this.walking_speed;}elseif(this.cursors.down.isDown&& this.body.velocity.y >= 0) { // move down if is not already moving up this.body.velocity.y = +this.walking_speed;}else{this.body.velocity.y=0;}if(this.body.velocity.x===0&& this.body.velocity.y === 0) { this.animations.stop();this.frame=0;}else{// if not moving, stop the animationthis.animations.play(«walking»);}};

      Now, we implement the Door prefab as shown below. It will have a “direction” property so we can know to where the player is navigating. In the “update” method we check for collisions with the hero, and if so, call the “enter_door” method. This method gets the next room using the door direction and starts a new RoomState for the next room.

      var ProceduralGeneration = ProceduralGeneration || {};ProceduralGeneration.Door = function (game_state, name, position, properties) { «use strict»; ProceduralGeneration.Prefab.call (esto, game_state, nombre, posición, propiedades); this.anchor.setTo (0,5); this.direction = properties.direction; this.game_state.game.physics.arcade.enable (this); this.body.collideWorldBounds = true;};ProceduralGeneration.Door.prototype = Object.create(ProceduralGeneration.Prefab.prototype);ProceduralGeneration.Door.prototype.constructor = ProceduralGeneration.Door;ProceduralGeneration.Door.prototype.update = function () { «use strict»; this.game_state.game.physics.arcade.collide(this, this.game_state.groups.heroes, this.enter_door, null, this);};ProceduralGeneration.Door.prototype.enter_door = function () { «use strict»; next_room var; // find the next room using the door direction next_room = this.game_state.room.neighbors[this.direction]; // start room state for the next room this.game_state.game.state.start(«BootState», true, false, «assets/levels/room_level.json», «RoomState», {room: next_room});};123456789101112131415161718192021222324252627282930varProceduralGeneration=ProceduralGeneration||{};ProceduralGeneration.Door=function(game_state,name,position,properties){«use strict»;ProceduralGeneration.Prefab.call(this,game_state,name,position,properties);this.anchor.setTo(0.5);this.direction=properties.direction;this.game_state.game.physics.arcade.enable(this);this.body.collideWorldBounds=true;};ProceduralGeneration.Door.prototype=Object.create(ProceduralGeneration.Prefab.prototype);ProceduralGeneration.Door.prototype.constructor=ProceduralGeneration.Door;ProceduralGeneration.Door.prototype.update=function(){«use strict»;this.game_state.game.physics.arcade.collide(this,this.game_state.groups.heroes,this.enter_door,null,this);};ProceduralGeneration.Door.prototype.enter_door=function(){«use strict»;varnext_room;// find th e next room using the door directionnext_room=this.game_state.room.neighbors[this.direction];// start room state for the next roomthis.game_state.game.state.start(«BootState»,true,false,»assets/levels/room_level.json»,»RoomState»,{room:next_room});};

      Finally, we add both the Hero and Door prefabs to the “prefab_classes” property in the RoomState. And then, you can actually run the demo navigating through the different rooms in the dungeon.

      this.prefab_classes = { «hero»: ProceduralGeneration.Hero.prototype.constructor, «door»: ProceduralGeneration.Door.prototype.constructor};1234this.prefab_classes={«hero»:ProceduralGeneration.Hero.prototype.constructor,»door»:ProceduralGeneration.Door.prototype.constructor};

      hero
      And this conclude this tutorial. In the next one we will populate the rooms with random obstacles and enemies. Then, we will add an exit, so the hero can leave the dungeon.

      Mensajes relacionados

Deja una respuesta

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