Comunidad orientada al desarrollo de videojuegos

Crear un minijuego con MightyEditor y Phaser

De la mano de Guntis nos llega este post invitado de como hacer un minijuego con MightyEditor y Phaser.

 

00_digger

En este artículo, le daré una introducción básica a MightyEditor y al proceso de desarrollo que lo rodea. El tutorial mostrará cómo hacer un prototipo de un minijuego en una hora.

Requisitos

Última versión del buscador de Google Chrome. Puede probar con otros buscadores, pero nosotros no los hemos probado aún.

¿Qué es MightyEditor?

MightyEditor es una solución basada en la nube de código abierta para crear y alojar juegos HTML5. Es compatible con el marco de desarrollo de juegos Phaser, pero también puede usarlo con otras herramientas. Las características del editor son: administración de assets, edición de mapas, editor de código, exportación de datos.

¿Cómo funciona MightyEditor?

El proceso de diseño de usar el editor funciona con los siguientes pasos:

      1. Cree un proyecto
  • Suba assets
  • Arrastre y suelte assets al mapa
  • Agrupe assets en capas, por ej, fondo, bloques…
  • Agregue colisiones y funcionalidad en el editor de código
  • Abra el juego y exporte datos

¿Por qué debo usar MightyEditor?

Tener una herramienta en basada en navegador con toda la funcionalidad básica de edición de mapa y código que le permite enfocarse en un prototipado rápido de juegos. Tan solo con abrir un enlace en el navegador, ya puede empezar. No hay dificultad al instalar y configurar varias soluciones de software y por lo tanto ganar tiempo valioso.

La cooperación con el diseñador, otro miembro del equipo o un cliente es tan sencillo como compartir el enlace con el proyecto. Realmente puede trabajar en equipo, designar a un ilustrador para lidiar con los assets mientras el diseñador de juegos crea diferentes niveles en el editor de mapas y el desarrollador agrega funcionalidad con el código JavaScript.

MightyEditor no requiere que su proyecto dependa de ello. Todos los assets y el código pueden exportarse de una cualquier momento. Y lo que es aún mejor: el editor es de código abierto y los datos pueden moverse a su máquina con la versión local del editor. El código de fuente puede encontrarse en github  https://github.com/TheMightyFingers/mightyeditor.

Idea de mini juegos

En este tutorial, crearemos un minijuego simple llamado Digger. El juego es acerca de un pequeño minero que cava bloques de tierra en busca de oro y vende varios minerales en la tienda. Los cursores se usarán para navegar y se agregará una física simple y colisión al objeto del minero.

Crear un proyecto

Abra el enlace http://mightyeditor.mightyfingers.com. Ingrese el título del proyecto – en este caso será Digger.

 

En el panel inferior derecho, ingrese las dimensiones del juego: worldWidth, worldHeight 640 x 640. Para simplificar, vamos a asignar el tamaño de 640 x 640 al viewportWidth y viewportHeight.

 

Suba los assets del juego

En la siguiente parte del tutorial usaremos activos que pueda descargar aquí. En el panel superior derecho de la lista de selección use la opción subir archivo/upload file y agregue los archivos. En forma alternativa, puede arrastrar y soltar archivos en el panel y los activos se subirán automáticamente.

Cree un mapa

Haga clic en el ícono “sello” en el panel de herramientas de la izquierda y luego seleccione background_sky.png en el panel de assets y luego haga clic en el mapa para crear el fondo (mantenga apretado el botón Ctrl para cambiar a la rejilla). Puede cambiar la posición del fondo seleccionando la flecha del panel de herramientas izquierdo. Para un posicionamiento más preciso, cambie la posición “x,y” en el panel de configuraciones. Luego repita este paso con los siguientes assets: background_city.png, background_hill1.png, background_hill2.png, background_grass.png. Al final todo debe verse como aparece en la siguiente imagen.

Cree un grupo

Creemos un grupo para los objetos de fondo recientemente creados. En la sección del panel derecho Objects de la lista de selección elija Add Group. Cambie el nombre del grupo a bg arrastre objetos a este grupo. Para arrastrar objetos múltiples mantenga apretada la tecla Shift.

Agregue el resto de los gráficos

Creemos 5 hileras de bloques bajo el fondo. Hay cuatro tipos diferentes: roca, suelo, césped, oro. Los bloques oro son coleccionables y no puede cavar rocas. Los objetos tienen que agregarse en el grupo blocks.

Finalmente necesitamos agregar personajes y objetos de tienda. Respecto del asset personajes, la imagen contiene múltiples marcos. Necesitamos definir el ancho y altura del marco en el panel inferior derecho Settings. El tamaño es 90 x 90. Marcos individuales pueden verse en el panel inferior assetPreview. Además, edite anchorX y anchorY. Configúrelos a to 0.5

También puede definir sus propias variables para el objeto. Abra la pestaña userData en el mismo panel y agregue el parámetro gold con el valor 0;

Seleccione el ícono texto en el panel de herramientas izquierdo y ponga objeto texto en la esquina superior derecha del mapa. Escriba “0” como texto que representará puntos. Cambie el nombre del objeto texto en el panel Objetos en el lado derecho.

Ahora estamos listos para agregar objetos y terminar de trabajar con la edición del mapa. Los personajes y objetos de tienda se agregan como en la imagen que aparece a continuación

Hemos terminado con la parte gráfica del tutorial. Luego, vamos a codificar y agregar funcionalidad como controles de personajes, colisiones, etc.

Editor de fuente

Cambie al editor de fuente en el lado superior izquierdo.

 

En el menú del lado izquierdo, aparecen los archivos de juegos. Tendrá que codificar en su mayoría en archivo state/play.js. Puede encontrar atajos de teclas para el editor aquí.

Estados de juegos

Para codificar vamos a usar el marco de desarrollo Phaser. Los estados en Phaser son partes individuales de la lógica del juego. En forma predeterminada el editor le da una plantilla con cuatro estados boot, load, menu, play. Para cada estado hay algunos métodos predefinidos: preload, create, update, render. A los fines del demo debe saber que el método create se llama justo después de que todos los activos se carguen y el método update se llama mediante el bucle juego 60 veces por segundo. Puede saber más viendo la documentación. By default template calls load state. We need to redirect to play state. For this edit menu.js file under create method.

window.Digger.state.load = {
    preload: function(){
        ...
    },

    create: function() {
        this.game.state.start("play");
    }
}

Abra el juego

Primero abra el juego desde el botón del panel superior Open game. Por el momento solo hay una pantalla negra. Para mostrar los objetos recién hechos tenemos que inicializarlos en el método crear del archivo play.js.

create: function() {
    this.bg = mt.create("bg");
    this.blocks = mt.create("blocks");
    this.shop = mt.create("shop");
    this.character = mt.create("character");
    this.points = mt.create("points");
}

Se termina de inicializar con mt.create function. “bg” y “blocks” representan los nombres del grupo en el panel de objeto en el lado derecho. “shop” y “character” representan nombres de sprites en el mismo panel y finalmente “points” representan el texto. Como ve, todos los grupos de objetos, sprites y texto se inicializan con el mismo método. Abra ahora el juego e inicializar objetos será visible en la pantalla.

Agregar física

En forma predeterminada, física está deshabilitado en Phaser por motivos de mejor rendimiento. Habilitaremos la física más liviana y rápida de todas las disponibles: física Arcade. Abra la pestaña física en la esquina inferior derecha. Cambie el parámetro enable toa 1 y el resto aparecerán a continuación. Configure el width del tamaño del cuerpo del personaje: 60 y height 60. Habilite gravedad y configure y a 1000. Por último configure el parámetro collideWorldBounds a 1.

 

Abrir el juego verá al personaje cayendo al fondo de la pantalla.

Controles de personajes

Inicialice las teclas del cursor en crear método.

this.cursors = this.game.input.keyboard.createCursorKeys();

Esta función nos da un objeto con las cuatro teclas de flecha para jugar: arriba, abajo, derecha e izquierda. En el método actualizar rastrearemos cuando la techa flecha a left/right/up arrow se presiona y daremos una velocidad al personaje o las teclas de parada no están inactivas.

update: function() {
    if (this.cursors.left.isDown) {
        this.character.body.velocity.x = -200;
    } else if (this.cursors.right.isDown) {
        this.character.body.velocity.x = 200;
    } else {
        this.character.body.velocity.x = 0;
    } if (this.cursors.up.isDown) {
        this.character.body.velocity.y = -300;
    }
}

Abra el juego y pruebe las teclas flecha para controlar el personaje.

Colisión entre personaje y bloques

Habilite física para bloques y configúrelos en forma inmóvil en el panel física en la parte inferior derecha Map editor.

 

Luego agregue detección de colisión en el método update. Primero, dos argumentos declaran que verificaremos la colisión entre el objeto sprite y el grupo del objeto. El tercer argumento es una función de 2 argumentos que se llame en la colisión.

this.game.physics.arcade.collide(this.character, this.blocks, function(character, block) {
    console.log('Collision between', character, block);
}, null, this);

Al abrir el juego verá que el personaje no se caerá al final de la pantalla si no que se quedará sobre los bloques.

Animaciones del personaje

Primero debemos definir marcos en la hoja sprite para diferentes tipos de animaciones. Agregue estas líneas en el método create.

this.character.animations.add('stand', [0, 1, 2, 3], 10, true);
this.character.animations.add('fly', [4, 5, 6, 7], 10, true);
this.character.animations.add('run', [8, 9, 10], 10, true);
this.character.animations.add('fall', [12, 13, 14, 15], 10, true);
this.character.animations.add('dig', [16, 17, 18], 10, false);
this.character.animations.add('dig_down', [20, 21, 22], 10, false);
this.character.animations.play('stand');

La última línea del código comienza a reproducir la animación predeterminada stand. Para el resto de las animaciones tenemos que cambiar el método update y agregar reproducir animación específica para cada tecla presionada. Tenga en cuenta que tenemos la misma animación para la ejecución derecha e izquierda. Para cambar sprite horizontalmente usamos la escala -1.

if (this.cursors.left.isDown) {
    this.character.body.velocity.x = -200;
    this.character.animations.play('run');
    this.character.scale.x = -1;
} else if (this.cursors.right.isDown) {
    this.character.body.velocity.x = 200;
    this.character.animations.play('run');
    this.character.scale.x = 1;
} else {
    this.character.body.velocity.x = 0;
    this.character.animations.play('stand');
} if (this.cursors.up.isDown) {
    this.character.body.velocity.y = -300;
    this.character.animations.play('fly');
}

Destruir bloques

Para cavar bloques debemos agregar funcionalidad a nuestra función colisionar:

this.game.physics.arcade.collide(this.character, this.blocks, function(character, block) {
    if (this.cursors.left.isDown) {
        if (block.body.touching.right) {
            this.destroyBlock(block);
        }
    }
    else if (this.cursors.right.isDown) {
        if (block.body.touching.left) {
            this.destroyBlock(block);
        }
    }
    else if (this.cursors.down.isDown) {
        if (block.body.touching.up) {
            this.destroyBlock(block);
        }
    }
}, null, this);

arios bloques deben manipularse de forma diferente. Los bloques césped y suelo simplemente se destruyen, las rocas son indestructibles y podemos recolectar oro y guardarlo en el parámetro oro del personaje. Agregue el método destroyBlock en el estado play.

destroyBlock: function(block) {
    switch (block.key) {
        case '/rock.png':
            break;
        case '/grass.png':
        case '/ground.png':
            block.destroy();
            break;
        case '/gold.png':
            this.character.getData().userData.gold++;
            block.destroy();
            break;
    }
},

Superponer y vender oro

En el próximo paso debemos vender el oro recolectado en la tienda. Primero debe volver al editor de mapa y configurar física para el objeto tienda similar cómo si lo hubiera hecho el personaje. Habilitar Configurar parámetros enable: 1, gravity->allow: 0, immovable: 1. Luego de eso vaya al editor de fuente y cambie el método update con las siguientes líneas:

this.game.physics.arcade.overlap(this.character, this.shop,
    function(character, shop) {
        if (character.getData().userData.gold > 0) {
            var newPoints = parseInt(this.points._text) + character.getData().userData.gold;
            this.points.setText(newPoints);
            character.getData().userData.gold = 0;
        }
    }, null, this
);

Refactorización y agregado de animaciones finales

Agregaremos animaciones de cavar como paso final de este tutorial y el método refactorizar update para satisfacer nuestras necesidades de un juego lindo. A juzgar por las muestras previas, el código que aparece a continuación debe explicarse por si mismo.

"use strict";
window.Digger.state.play = {	
	create: function(){
		this.cursors = this.game.input.keyboard.createCursorKeys();
		
		this.bg = mt.create("bg");
		this.blocks = mt.create("blocks");
		this.shop = mt.create("shop");
		this.character = mt.create("character");
		this.points = mt.create("points");
		
		this.character.animations.add('stand', [0, 1, 2, 3], 10, true);
		this.character.animations.add('fly', [4, 5, 6, 7], 10, true);
		this.character.animations.add('run', [8, 9, 10], 10, true);
		this.character.animations.add('fall', [12, 13, 14, 15], 10, true);
		this.character.animations.add('dig', [16, 17, 18], 10, false);
		this.character.animations.add('dig_down', [20, 21, 22], 10, false);
		this.character.animations.play('stand');
	},
	
	update: function(){
		var collideDown = false;
		this.game.physics.arcade.collide(this.character, this.blocks,
		  function(character, block){
			
			if(this.dig) return;	
				
			if(this.cursors.left.isDown){		
				if(block.body.touching.right){
					this.dig = this.character.animations.play('dig');
					this.dig.onComplete.addOnce(function(){
      					this.destroyBlock(block);
					}, this);							
				} else {
					this.character.animations.play('run');
				}					
			}

			else if(this.cursors.right.isDown){					
				if(block.body.touching.left){
					this.dig = this.character.animations.play('dig');
					this.dig.onComplete.addOnce(function(){
      					this.destroyBlock(block);
					}, this);					
				} else {
					this.character.animations.play('run');
				}

			}

			else if(this.cursors.down.isDown){					
				if(block.body.touching.up){
					this.dig = this.character.animations.play('dig_down');
					this.dig.onComplete.addOnce(function(){
      					this.destroyBlock(block);
					}, this);

				} else {
					this.character.animations.play('stand');
				}
			}		
			
			if(block.body.touching.up){
				collideDown = true;
			}
				
			
			
		}, null, this);
										 
		
		if(this.dig){
			return;	
		}
		

		if(this.cursors.left.isDown){
			this.character.scale.x = -1;
			this.character.body.velocity.x = -200;
		}
		else if(this.cursors.right.isDown){
			this.character.scale.x = 1;
			this.character.body.velocity.x = 200;
		}
		else {
			this.character.body.velocity.x = 0;	
		}
		
		
		if(this.cursors.up.isDown){
			this.character.body.velocity.y = -300;
			this.character.animations.play('fly');
		} else {
			if(!collideDown){
				this.character.animations.play('fall');
			}
			else if(this.character.body.velocity.x === 0){
				this.character.animations.play('stand');
			}
		}
		
		this.game.physics.arcade.overlap(this.character, this.shop,
		 	function(character, shop){
				if(character.getData().userData.gold > 0){
					var newPoints = parseInt(this.points._text) + character.getData().userData.gold;
					this.points.setText(newPoints);
					character.getData().userData.gold = 0;
				}
			}, null, this
		);
	},
	
	destroyBlock: function(block){
		this.dig = false;
		switch(block.key){
			case '/rock.png':
				break;
			case '/grass.png':
			case '/ground.png':
				block.destroy();
				break;
			case '/gold.png':
				this.character.getData().userData.gold++;
				block.destroy();
				break;
		}
	},
	
	stopDig: function(){
		this.dig = false;	
	}
};

El proyecto completo del juego está disponible aquí: http://mightyeditor.mightyfingers.com/#pde5-copy Juego final aquí: http://mightyeditor.mightyfingers.com/data/projects/pde5/phaser/index.html iframe:

Leave a Reply