10 abril 2014

WaveEngine: Mostrar un sprite 2D y mover con teclado

¿Qué tiene de bueno WaveEngine?


Es un motor de juegos en C#, multiplataforma y gratuito, con el que es fácil crear juegos para Windows 8 y Windows Phone, y no mucho más complicado crearlos para Android e iOS.


¿Qué tiene de malo WaveEngine?


La curva de aprendizaje comienza de forma más pronunciada que con motores más básicos: es fácil crear un sistema de partículas para imitar humo, con poquísimas líneas de código, pero, por el contrario, es relativamente largo (comparado con otras plataformas básicas para 2D) hacer algunas tareas sencillas, como mostrar una imagen en pantalla. Y cuando uno empieza a programar, normalmente prefiere empezar por cosas que tengan una "lógica simple" antes de llegar a los "efectos vistosos".

Por eso, vamos a ver algún ejemplo básico que ayuden a crear un juego 2D simple usando WaveEngine.


Disclaimer


No soy ningún experto en WaveEngine, sólo alguien ha asistido al WaveEngine University Tour 2014, así que puede que alguna cosa se pueda hacer de formas (mucho) más eficientes que la que yo exponga. Si lees esto y conoces alguna, puedes publicar un comentario para ayudar a mejorarlo.


¿Qué pasos hay que dar para instalar (en Windows)?


1.- Descargar e instalar Visual Studio 2012 o 2013 (basta con la versión Express, gratuita): http://www.microsoft.com/es-es/download/details.aspx?id=40787

2.- Si usas Windows 7, deberás descargar (e instalar) "el Framework Punto Net 4.5"; si empleas Windows 8, ya lo tendrás instalado: http://www.microsoft.com/en-US/download/details.aspx?id=30653

3.- Necesitarás instalar el SDK de DirectX (no basta con usar el "runtime" que empleas para jugar a juegos que usan DirectX, porque ahora vas a crear cosas que utilizan DirectX). Puedes usar un instalador web desde http://www.microsoft.com/en-us/download/details.aspx?id=35

4.- Finalmente, el instalador de WaveEngine: http://waveengine.net/Download/Index

5.- Es habitual que las versiones recientes de WaveEngine incluyan el VC++ 2012 Runtime; si no es así, tendrás que descargarlo también): http://www.microsoft.com/en-US/download/details.aspx?id=30679


¿Qué apariencia tiene un programa?


Si entras a Visual Studio y creas un nuevo proyecto de tipo "WaveEngine Game Project", aparecerá un esqueleto casi vacío, como éste:

public class MyScene : Scene
{
    protected override void CreateScene()
    {
        RenderManager.BackgroundColor = Color.CornflowerBlue;

        //Insert your code here
    }
}

Nuestro "juego" tiene una única escena, con color de fondo azul claro... y nada más.

Si lanzas el proyecto (con el botón "Iniciar") de Visual Studio, debería aparecer esa escena vacía con color azul:



Vamos a añadir un elemento que el usuario pueda mover...


En primer lugar, tenemos que buscar la imagen que nos interese (por ejemplo, un fichero PNG) y convertirla al propio formato nativo de WaveEngine, de forma que esa imagen se pueda utilizar en cualquier plataforma.

Para convertir la imagen utilizaremos una de las herramientas que incorpora WaveEgine: el "Assets Exporter". Deberemos abrirla y crear un nuevo proyecto (File / New project), escogiendo una carpeta, que es en la que aparecerán los ficheros exportados. Ahora arrastramos nuestra imagen hasta la palabra "Assets", que aparece en el panel superior izquierdo. Después vamos al menú "Project" y escogemos la opción "Export". No tendremos ningún aviso (al menos en la versión actual) de que todo ha funcionado, pero si vamos a la subcarpeta "Export" dentro de la carpeta que habíamos escogido, veremos un fichero con el mismo nombre que nuestra imagen, y terminado en ".wpk" (en mi caso, a partir de "player.png" se ha generado "player.wpk").



Ya tenemos la imagen preparada. Ahora hay que llevarla a nuestro proyecto de Visual Studio. En el panel superior derecho ("Explorador de soluciones") aparecerá la carpeta "Content". Debemos arrastrar nuestra imagen hasta allí.



Para asegurarnos de que la imagen realmente se incluya en el proyecto distribuible, nos falta un paso adicional: hacer clic en ella, ir a la pestaña "propiedades" y, en la opción "Copiar en el directorio de resultados", cambiar la opción "No copiar" por "Copiar si es posterior".



Nos vamos acercando. Nuestra imagen está incluida al proyecto y será distribuida con el proyecto. Ahora vamos a incluir el código que realmente la muestre en pantalla. Para conseguirlo, deberemos crear una nueva "entidad", un nuevo elemento del juego.



Cada "Entity" (entidad) estará formado por varios "componentes". Algunos de estos componentes serán necesarios para que la entidad sea visible en pantalla:


  • Un "Transform2D", que tendrá detalles como la posición, escala y rotación del elemento.
  • Un "Sprite", que será la imagen en sí.
  • Un "SpriteRenderer", que dará detalles adicionales sobre cómo se debe dibujar el Sprite (y que de momento se limitará a avisar de que se debe mostrar en una "capa opaca").


Así, un fuente básico que muestre la Entity correspondiente a nuestra imagen será:

public class MyScene : Scene
{
    protected override void CreateScene()
    {
        RenderManager.BackgroundColor = Color.CornflowerBlue;

        Entity player = new Entity()
                .AddComponent(new Transform2D())
                .AddComponent(new Sprite("content/player.wpk"))
                .AddComponent(new SpriteRenderer(DefaultLayers.Opaque));
    }
}

En un principio no compilará: no sabrá no que es Transform2D, ni Sprite, ni SpriteRenderer... Podemos añadir

using WaveEngine.Common.Graphics;
using WaveEngine.Components.Graphics2D;

o bien pulsar "Ctrl+." para que el propio VisualStudio nos proponga los "using" que faltan.

Si lo lanzamos, veremos que aún no funciona, sigue apareciendo la pantalla azul, porque falta un último detalle: existe un elemento en el juego, que es el encargado de gestionar todas esas "entidades". Se llama "EntityManager", y debemos incluir en él cada "Entity" que creemos:

EntityManager.Add(player);

Ahora sí aparecerá la imagen dentro de la pantalla de juego:









Pero esa imagen aún es estática. Para añadir un que un objeto se mueve, debemos añadirle un "comportamiento" ("Behavior"). El comportamiento será una clase que herede de "Behavior" y que deberá tener un método "Update" (actualizar), que será llamado de forma regular. Desde ese método podremos comprobar distintos métodos de entrada. Por ejemplo, podemos mirar si se ha pulsado la flecha hacia la derecha con:

if ((WaveServices.Input.KeyboardState.Right == ButtonState.Pressed))

En ese caso deberíamos cambiar la coordenada X del jugador. Podemos acceder a ella a través de su "Transform2D". La forma de conseguirlo es incluyendo al principio de nuestra clase PlayerBehavior las líneas

[RequiredComponent]
private Transform2D playerData;

De modo que a partir de entonces podremos acceder a detalles como la X y la Y del jugador con "playerData.X" y "playerData.Y".  El fuente completo de esa clase "PlayerBehavior" (comportamiento del jugador) sería algo como:

using System;
using WaveEngine.Common.Input;
using WaveEngine.Framework;
using WaveEngine.Framework.Graphics;
using WaveEngine.Framework.Services;

namespace WaveEnginePrueba01Project
{
    class PlayerBehavior : Behavior
    {
        [RequiredComponent]
        private Transform2D playerData;

        protected override void Update(TimeSpan gameTime)
        {
            if ((WaveServices.Input.KeyboardState.Right == ButtonState.Pressed))
            {
                 playerData.X += 5;

            }

            if ((WaveServices.Input.KeyboardState.Down == ButtonState.Pressed))
            {
                playerData.Y += 5;

            }
        }
    }
}

(Sí, sólo se mueve hacia la derecha y hacia abajo, pero seguro que no te cuesta completarlo).

Y así habría quedado el bloque principal, la escena, al que sólo faltaría añadir (con AddComponent) el comportamiento (un nuevo objeto de la clase PlayerBehaviour):

using WaveEngine.Common.Graphics;
using WaveEngine.Components.Graphics2D;
using WaveEngine.Framework;
using WaveEngine.Framework.Graphics;

namespace WaveEnginePrueba01Project
{
    public class MyScene : Scene
    {
        protected override void CreateScene()
        {
            RenderManager.BackgroundColor = Color.CornflowerBlue;

            Entity player = new Entity()
                    .AddComponent(new Transform2D() )
                    .AddComponent(new Sprite("content/player.wpk"))
                    .AddComponent(new SpriteRenderer(DefaultLayers.Opaque))
                    .AddComponent(new PlayerBehavior() );

            EntityManager.Add(player);
        }
    }
}


¿Tienes dudas (que quizá hasta pueda responder... pero no garantizo nada)?  ¿Quieres que esto sea el principio de una serie que cuente más detalles de cómo hacer un juego paso a paso?  ¡Deja un comentario!