[Xamarin.Forms] Aplicaciones con contenido 3D utilizando Wave Engine

Introducción

A la hora de desarrollar aplicaciones móviles, nos encontramos con una enorme variedad de requisitos a nivel visual y de contenido. En Xamarin.Forms gracias al avance en novedades (pestañas en la parte inferior, bordes redondeados, etc.) junto a SkiaSharp, estilos, efectos y Custom Renderers otorgan grandes opciones para desarrollar aplicaciones visualmente atractivas.

En determinadas ocasiones, la visualización e interacción con elementos 3D o cámaras en 360 grados por ejemplo, otorga una mejor experiencia en nuestras aplicaciones. Por ejemplo, la visualización de edificios y diferentes plantas o la previsulización de un coche que deseamos comprar. Para conseguir la visualización e integración con elementos 3D tenemos diferentes opciones como Urho o Wave Engine.

En este artículo, vamos a ver como integrar elementos 3D en una aplicación Xamarin.Forms utilizando Wave Engine.

Wave Engine

Wave Engine es un motor de videojuegos 2D y 3D multiplataforma, basado en componentes y totalmente gratuito (no hay licencias ni tampoco ningún tipo de royalty). Nos ofrece una API para trabajar con físicas, animaciones, sonido, etc. utilizando C# sin tener que profundizar a más bajo nivel y tener que trabajar con DirectX directamente.

Podemos desarrollar para Android, iOS, Linux, macOS, Windows, etc..

Creando la escena 3D

Vamos a realizar un ejemplo sencillo pero suficientemente interesante para conocer:

  • La creación de una escena 3D.
  • Integrar una escena 3D en Xamarin.Forms.
  • Interactuar desde una vista XAML de Xamarin.Forms con la escena 3D.

Crearemos una escena con un coche y podremos modificar el color del mismo desde un ListView de Xamarin.Forms.

¿Y de dónde obtenemos el modelo 3D?

Para este ejemplo, vamos a utilizar un modelo 3D totalmente gratuito de Sketchfab gracias a Karol Miklas.

Sketchfab

Tras descargar el modelo 3D, arrancamos el editor de Wave Engine.

Editor Wave Engine

NOTA: Puedes descargar Wave Engine desde este enlace.

Creamos nuevo proyecto, y arrastramos el modelo descargado a la carpeta Assets en el apartado Assets Details. Podemos hacer doble clic en el archivo fbx para asegurar que todo es correcto.

Cargar el modelo

Comenzamos a montar nuestra escena arrastrando el modelo que acabamos de previsualizar desde Assets Details a la Scene:

Creando la escena

Llegados a este punto, tenemos suficiente. Podemos mejorar la escena con pequeños cambios como:

  • Añadir entidad plano 3D para ele suelo.
  • Añadir una esfera en forma de cúpula, para añadir “límites” al escenario.
  • Añadir más luces a la añadida por defecto.
  • Etc.

Integrando la escena con proyecto Xamarin.Forms

De momento, tenemos un proyecto Windows (proyecto creado por defecto) con nuestra escena. Podemos lanzar y probar nuestro proyecto.

Ejemplo en Windows

Pero…¿y cómo llegamos a integrarlo todo en Xamarin.Forms?. Wave Engine es un motor multiplataforma. Se pueden crear proyectos para plataformas como Android, iOS, UWP, Linux o macOS. Entre las diferentes opciones, para Android, iOS y UWP hay proyectos con integración con Xamarin.Forms.

Desde el editor de Wave Engine, Edit > Project Properties…

Nuevo perfil

Podemos elegir entre diferentes plataformas, y en cada plataforma diferentes Launchers. En Android, iOS y UWP contamos con Launcher Xamarin.Forms.

Xamarin.Forms

Esto nos crea automáticamente un nuevo proyecto utilizando Xamarin.Forms.

Estructura del proyecto

Tenemos una proyecto Shared con toda la lógica compartida de Wave Engine (escenas, behaviors, componentes, etc.) y otro proyecto de igual tipo donde tenemos la aplicación Xamarin.Forms con las vistas, etc.

Al final, trabajamos con una aplicación Xamarin.Forms habitual donde la carga de la escena 3D se realiza vía Custom Renderer. Contamos con el siguiente control:

public class WaveEngineSurface : View, IViewController
{
     public static readonly BindableProperty GameProperty = BindableProperty.Create(
     propertyName: "Game",
     returnType: typeof(IGame),
     declaringType: typeof(WaveEngineSurface),
     defaultValue: null);

     public IGame Game
     {
          get { return (IGame)GetValue(GameProperty); }
          set { SetValue(GameProperty, value); }
     }
}

Desde una vista XAML:

<AbsoluteLayout>
     <controls:WaveEngineSurface x:Name="WaveEngineSurface"
          AbsoluteLayout.LayoutFlags="All"
          AbsoluteLayout.LayoutBounds="0, 0, 1, 1" />
</AbsoluteLayout>

Interacción con la escena 3D

En este ejemplo, vamos a utilizar SlideOverKit para añadir un menu lateral deslizante, donde mostrar un listado de colores. La idea es permitir cambiar el color de coche en 3D. Pero para conseguirlo, necesitaremos interaccionar con la escena 3D desde nuestra aplicación Xamarin.Forms, ¿cómo lo hacemos?.

Al seleccionar cualquir color, lanzamos un comando en la ViewModel:

public ICommand ColorTappedCommand => new Command<CustomColor>(ChooseColor);

private void ChooseColor(CustomColor color)
{
     // TODO:
}

Al crear el proyecto Xamarin.Forms desde el editor visual tenemos una clase llamada WaveEngineFacade. Desde esta clase tenemos la opción de acceder a la escena. Creamos un método llamado UpdateColor. En el método debemos acceder a la entidad del coche, en concreto a su material, y modificar el color.

¿Cómo sabemos cual es esa entidad?.

Desde el editor, vemos el desglose de entidades (piezas) que componen al coche. Seleccionamos la carrocería y elegimos la opción Copy EntityPath:

Copy EntityPath

public static void UpdateColor(CustomColor color)
{
     var entity = _scene.EntityManager.Find("car.Plane_036.Plane_041");
     var materialComponent = entity.FindComponent<MaterialComponent>();
     var hex = color.Hex;
     ((StandardMaterial)materialComponent.Material).DiffuseColor = FromHex(hex);
}

NOTA: En la variable _scene tenemos acceso a cualquier elemento de la escena (cámara, luces, entidades, etc.). Utilizamos un método Initialize lanzado en el arranque de la aplicación para establecer el valor de la variable.

Desde el comando sencillamente lanzamos nuestro método:

WaveEngineFacade.UpdateColor(color);

El resultado final:

El resultado

Puedes encontrar el ejemplo realizado en GitHub:

Ver GitHub

Más información

[Evento Online] Bienvenidos al desarrollo holográfico!

HololensIntroducción

Desde que Hololens apareció en nuestro horizonte, aparición a aparición, demo tras demo ibamos viendo posibilidades y un nuevo abanico de opciones abiertas de cara al desarrollo. Más tarde conocimos características, se liberó el SDK y comenzaron las primeras hornadas de dispositivos llegando a desarrolladores.

Hololens

Hololens

Ahora, con ellas a nuestra alcance, tras probar a fondo el SDK, emulador, Unity e incluso con la liberación de la versión inicial de la extensión de Hololens de parte de Wave Engine, ¿algo mejor que verlo y probarlo todo a fondo?.

El evento

¿Quieres ver que es lo más nuevo de Microsoft en dispositivos de interacción? ¿Qué es un gaze o bloom? En esta sesión comenzaremos con un unboxing de HoloLens, continuaremos con un análisis de características y apps disponibles y terminaremos viendo como desarrollar apps para HoloLens, adaptar apps UWP para HoloLens y como acceder al dispositivo para controlarlo por voz, etc.

Estrenamos nuevo formato en CartujaDotNet habilitando eventos online para ayudar y contribuir a cualquier miembro de la comunidad. El evento será el próximo Viernes, 03 de Junio.

  • 19:00 en España
  • 13:00 en Colombia
  • 12:00 en México Centro
  • 13:30 en Venezuela
  • 15:00 en Chile continental

¿Te apuntas?

Más información

[Evento CartujaDotNet] Sevilla Mobility Day

El evento

No hay duda, los smartphones han llegado, y ya forman parte de la vida de todos nosotros. En muchos aspectos de nuestra vida accedemos a información, realizamos algna tarea o sencillamente nos entretenemos con uno de ellos.

Esto también nos afecta como desarrolladores. El desarrollo móvil se ha convertido en una prioridad en una gran mayoria de ámbitos. Por ese motivo, el próximo evento de CartujaDotNet se centra efusivamente en este ámbito. Tras las novedades disponibles con el último SDK de Windows Phone 8.1 tenemos una gran cantidad de puntos que tratar. Además, tambien tenemos novedades en el desarrollo multiplataforma donde Xamarin anuncio recientemente la versión 3 del mismo con suculentos cambios o en el desarrollo de videojuegos, donde engines como Wave Engine, nos ponen más fácil la tarea de crear grandes juegos multiplataforma.

Y ante tal cantidad de novedades, ¿que podemos hacer?. En CartujaDotNet, tienen la solución, meterlo todo en una coctelera, agitarlo y asi nace el Sevilla Mobility Day, un día con múltiples sesiones técnicas hablando de aplicaciones universales en Windows Phone, detalles técnicos en el desarrollo de Windows Phone como el uso de Behaviors, animaciones y Visual States, desarrollo de aplicaciones multiplataforma con Xamarin o como desarrollar videojuegos multiplataforma con Wave Engine.

¿Te apuntas?

Sevilla Mobility Day

Sevilla Mobility Day

Fecha

El evento tendrá lugar el próximo Sábado, 05 de Julio de 9:30h a 14:00h. Cada charla tendrá una duración de 1 hora con descanso intermedio de 5 minutos.

Lugar

Tendrá lugar en el Cloud Pointing de Sevilla situado en el Parque Empresarial Nuevo Torneo. Tenéis la información exacta del lugar a continuación:

c\ Biología, 12, Edificio Vilamar 2, 3ª Planta
Parque Empresarial Nuevo Torneo
41015 Sevilla

Ponentes

El evento cuenta con una gran variedad de ponentes de lujo:

  • El evento comienza con un servidor hablando de aplicaciones universales en Windows Phone. En esta sesión aprenderemos como desarrollar aplicaciones unviersales para Windows Phone 8.1 y Windows reutilizando la mayor parte del código.
  • Continuamos con otra sesión de la mano de Josué Yeray. Analizará a un trio clásico de XAML que nos ayudará a crear mejores apps tanto para Windows Phone 8.1 como para Windows Store y Universal Apps. Sin duda hablamos del equipo formado por los behaviors, las animaciones y los visual states.
  • Continuamos con Juan María Lao con otra sesión muy divertida donde veremos como crear un juego multiplataforma con Wave Engine para Windows Phone, Android, iOS, Metro, Mac, Linux, Windows y Ouya.
  • Cerramos el evento con una charla muy interesante sobre desarrollo multiplataforma utilizando Xamarin de la mano de Juan Cano y Marcos Cobeñas.

Más información

[Windows Phone] Creando juegos con WaveEngine. 2º Parte.

WaveEngine LogoIntroducción

En el artículo anterior realizamos una introducción a WaveEngine con el objetivo de analizar el arranque y la funcionalidad básica del engine. Para ello, nos basamos en un clásico como “Super Mario Bross”.

NOTA: “Super Mario Bross” es una marca registrada por Nintendo. Este artículo es un simple homenaje a la saga.

Tras conocer el engine, la gestion de recursos y lograr plasmar el fondo, suelo y al personaje principal, el objetivo de esta entrada es continuar desde el punto anterior y evolucionar el ejemplo hasta conseguir poder mover al personaje (mediante un gamepad táctil) junto al sistema de animaciones.

¿Te apuntas?

Añadiendo comportamientos

El aspecto de nuestro videojuego hasta ahora es el siguiente:

Mario en escena!

Mario en escena!

Interesante pero… ¿Mario sin correr?. Tenemos que resolver esto. Veamos como añadir animaciones a Mario.

Una animación consta de estados. Cada estado a su vez consta de varias imágenes. Tendremos dos estados:

  • Idle: Cuando Mario este parado.
  • Walk: Cuando Mario camina o corre.

Ya aprendimos a utilizar la herramienta Wave Exporter para generar los archivos .gmk de los que se nutre el juego. Podríamos generar un gmk para cada una de las imágenes de cada animación aunque a la larga, penalizaría el rendimiento.

¿Qué hacemos?

La solución se llamda SpriteSheet. Un spritesheet no es más que una imagen que contiene muchas imágenes. Se utiliza en el juego dividiendo y obteniendo cada una de las imágenes que contiene pero todas se obtienen del mismo fichero.

Nosotros en nuestro ejemplo vamos a utilizar el siguiente conjunto de imágenes:

Imágenes

Imágenes

Asi pues, primer objetivo, crear el spritesheet. Podríamos hacerlo a mano con los distintos editores fotográficos conocidos aunque la tarea es bastante tediosa.

Vamos a utilizar la herramienta TexturePacker que puedes descargar desde aquí. Abrimos la herramienta y arrastramos las imágenes anteriores:

TexturePacker01

En el lateral izquierdo tenemos el panel de configuración. El primer cambio importante que realizaremos será elegir el formato de salida elegido. Elegiremos “Generic XML”:

TexturePacker02Además del spritesheet en formato PNG obtendremos un archivo en XML que indicará el tamaño y la posición de cada una de las imágenes que componen al spritesheet. Será importante en nuestro juego.

Finalmente, en la parte superior pulsamos el botón “Publish”:

TexturePacker03

Como resultado además del spritesheet obtenemos el XML:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with TexturePacker http://texturepacker.com-->
<!-- $TexturePacker:SmartUpdate:9253cd2deaa49622a1344586a860f75a$ -->
<!--Format:
n  => name of the sprite
x  => sprite x pos in texture
y  => sprite y pos in texture
w  => sprite width (may be trimmed)
h  => sprite height (may be trimmed)
oX => sprite's x-corner offset (only available if trimmed)
oY => sprite's y-corner offset (only available if trimmed)
oW => sprite's original width (only available if trimmed)
oH => sprite's original height (only available if trimmed)
r => 'y' only set if sprite is rotated
-->
<TextureAtlas imagePath="Mario.png" width="64" height="128">
     <sprite n="0.png" x="2" y="2" w="17" h="27"/>
     <sprite n="1.png" x="21" y="2" w="17" h="28"/>
     <sprite n="2.png" x="40" y="2" w="15" h="28"/>
     <sprite n="3.png" x="2" y="32" w="17" h="28"/>
     <sprite n="4.png" x="21" y="32" w="15" h="28"/>
     <sprite n="5.png" x="38" y="32" w="17" h="28"/>
     <sprite n="6.png" x="2" y="62" w="17" h="27"/>
</TextureAtlas>

Como podéis observar en el archivo XML, contiene la posición de cada una de las imágenes junto a su tamaño. Todo listo para comenzar!

Vamos a añadir el spritesheet y el XML en el proyecto pero … espera!. No directamente, recuerda que tenemos que utilizar la herramienta Wave Exporter para generar el archivo .gmk:

Recursos Mario

Nos centramos en la entidad Mario. El principal cambio con respecto a lo que teníamos hasta ahora será la introducción del componente de tipo Animation2D. El componente Animation2D nos permite definir desde una hasta un conjunto de animaciones:

var mario = new Entity("Mario")
.AddComponent(new Transform2D()
{
     X = WaveServices.Platform.ScreenWidth / 2,
     Y = WaveServices.Platform.ScreenHeight - 46,
     Origin = new Vector2(0.5f, 1)
})
.AddComponent(new Sprite("Content/Mario.wpk"))
.AddComponent(Animation2D.Create<TexturePackerGenericXml>("Content/Mario.xml")
.Add("Idle", new SpriteSheetAnimationSequence() { First = 3, Length = 1, FramesPerSecond = 11 })
.Add("Running", new SpriteSheetAnimationSequence() { First = 0, Length = 3, FramesPerSecond = 27 }))
.AddComponent(new AnimatedSpriteRenderer(DefaultLayers.Alpha));

Nosotros necesitamos por ahora al menos dos conjuntos de animaciones (Mario en estado Idle y Mario en estado Running). Todo ello recordar utilizando el mismo sprite sheet.

Antes de continuar, analicemos el código anterior. Para crear la animación utilizamos el método estático Animation2D.Create que recibe un parámetro genérico (generalmente una clase, en nuestro ejemplo TexturePackerGenericXml) donde se definirá cada sprite perteneciente al spritesheet utilizado en la animación.  Una vez creado el Animation2D definimos todas las animaciones que contendrá. En nuestro caso definimos las dos necesarias (Idle i Running).

En cada animación definida establecemos un nombre junto a un objeto de tipo SpriteSheetAnimationSecuence que definirá el frame inicial de la animación, el frame final y el número de frames a renderizar por segundo.

Continuamos. Antes de avanzar, es importante que no se nos pase añadir la entidad a escena:

EntityManager.Add(mario);

Bien, si ejecutamos en este momento… ops!, no hay cambios. Efectivamente, hemos añadido a Mario animaciones para cuando este parado y para cuando corra pero no tenemos forma de cambiar entre los estados, es decir, aun no somos capaces de hacer correr a Mario.

Pongamos fin a este problema. Vamos a crear un behavior, en nuestro ejemplo llamado MarioBehavior, que nos permitirá desplazar a Mario horizontalmente. Este Behavior capturará las pulsaciones  en la pantalla táctil para permitir modificar el estado de Mario y asi permitir el desplazamiento:

protected override void Update(TimeSpan gameTime)
{
     currentState = AnimState.Idle;

     // touch panel
     var touches = WaveServices.Input.TouchPanelState;
     if (touches.Count > 0)
     {
          var firstTouch = touches[0];
          if (firstTouch.Position.X > WaveServices.Platform.ScreenWidth / 2)
          {
               currentState = AnimState.Right;
          }
          else
          {
               currentState = AnimState.Left;
          }
     }

     // Set current animation if that one is diferent
     if (currentState != lastState)
     {
          switch (currentState)
          {
               case AnimState.Idle:
                    anim2D.CurrentAnimation = "Idle";
                    anim2D.Play(true);
                    direction = NONE;
                    break;
               case AnimState.Right:
                    anim2D.CurrentAnimation = "Running";
                    trans2D.Effect = SpriteEffects.None;
                    anim2D.Play(true);
                    direction = RIGHT;
                    break;
               case AnimState.Left:
                    anim2D.CurrentAnimation = "Running";
                    trans2D.Effect = SpriteEffects.FlipHorizontally;
                    anim2D.Play(true);
                    direction = LEFT;
                    break;
          }
     }

     lastState = currentState;

     // Move sprite
     trans2D.X += direction * SPEED * (gameTime.Milliseconds / 10);

     // Check borders
     if (trans2D.X < BORDER_OFFSET)
          trans2D.X = BORDER_OFFSET;
     else if (trans2D.X > WaveServices.Platform.ScreenWidth - BORDER_OFFSET)
          trans2D.X = WaveServices.Platform.ScreenWidth - BORDER_OFFSET;
}

En la parte superior tenéis el código implementado en el método Update. En el método se realizan varias tareas fundamentales:

  • Captura las pulsaciones en la pantalla (izquierda y derecha).
  • Modifica el estado de la animación a la adecuada (dependiendo del valor de currentState).
  • Se mueve el sprite a la posición correspondiente.
  • Se verifica que el sprite este dentro de los márgenes.

Llegados a este punto lo tenemos todo casi preparado para ver correr por fin a Mario pero… espera!. Debemos modificar la instancia de Mario para añadirle el Behavior:

AddComponent(new MarioBehavior())

Ahora si!. Si ejecutamos y pulsamos en el lateral izquierdo Mario correrá hacia la izquierda hasta el márgen izquierdo como máximo y exactamente igual en sentido contrario. Genial, ¿cierto?. Aunque… llevamos toda la vida jugando con Mario con una cruceta, ¿como lo solucionamos?.

Bueno, ningun dispositivo Windows Phone cuenta con botones físicos específicos para juegos. Sin embargo, tenemos una maravillosa pantalla táctil con todas las posibilidades que ofrece.

¿Que tal si implementamos una cruceta virtual?.

Manos a la obra. Lo primero que debemos hacer es… tener unos gráficos de nuestra cruceta para mostrar en pantalla. Asi que tenemos que crear nuestro wpk correspondiente. Una vez añadido al proyecto, tenemos que instanciarlo:

var joystick = new Entity("joystick")
.AddComponent(new Sprite("Content/Joystick.wpk"))
.AddComponent(new SpriteRenderer(DefaultLayers.Alpha))
.AddComponent(new Transform2D()
{
     X = joystickOffset,
     Y = WaveServices.Platform.ScreenHeight - joystickOffset - 300
});

De esta forma lograremosque la cruceta aparezca en la zona inferior izquierda de la pantalla para molestar lo mínimo posible al jugador. La añadimos a escena:

EntityManager.Add(joystick);

De momento, si pulsamos cualquier dirección de la cruceta, no hace nada… ¿no echáis en falta algo?. Tenemos que añadir comportamiento a la cruceta digital asi que debemos crear un nuevo Behavior. Creamos un Behavior llamado JoyStickBehavior:

protected override void Update(TimeSpan gameTime)
{
     this.UpPressed = this.DownPressed = this.LeftPressed = this.RightPressed = false;

     var touchState = WaveServices.Input.TouchPanelState;

     if (touchState.Count > 0)
     {
          var touch = touchState[0];

          // Up/down check
          if (touch.Position.X > (trans2D.X + 100) && touch.Position.X < (trans2D.X + 200))
          {
               // Here we're inside the vertical area defined by up/down buttons, let's check which one of those
               if (touch.Position.Y > trans2D.Y && touch.Position.Y < (trans2D.Y + 100))
               {
                    // Up
                    this.UpPressed = true;
               }
               else if (touch.Position.Y > (trans2D.Y + 200) && touch.Position.Y < (trans2D.Y + 300))
               {
                    // Down
                    this.DownPressed = true;
               }
          }
          // Left/right check
          else if (touch.Position.Y > (trans2D.Y + 100) && touch.Position.Y < (trans2D.Y + 200))
          {
               // Here we're inside the horizontal area defined by left/right buttons, let's check which one of those
               if (touch.Position.X > trans2D.X && touch.Position.X < (trans2D.X + 100))
               {
                    // Left
                    this.LeftPressed = true;
               }
               else if (touch.Position.X > (trans2D.X + 200) && touch.Position.X < (trans2D.X + 300))
               {
                    // Right
                   this.RightPressed = true;
               }
          }
     }
}

Basicamente detectamos si  el usuario toca en la cruceta. Modificamos el Behavior MarioBehavior para añadir una instancia del Behavior Joystick y detectar si tenemos que cambiar a Mario de estado según el estado del Joystick:

protected override void Update(TimeSpan gameTime)
{
     currentState = AnimState.Idle;

     // touch panel
     if (joystick.RightPressed)
     {
          currentState = AnimState.Right;
     }

     if(joystick.LeftPressed)
     {
          currentState = AnimState.Left;
     }

     // Set current animation if that one is diferent
     if (currentState != lastState)
     {
          switch (currentState)
          {
               case AnimState.Idle:
                    anim2D.CurrentAnimation = "Idle";
                    anim2D.Play(true);
                    direction = NONE;
                    break;
               case AnimState.Right:
                    anim2D.CurrentAnimation = "Running";
                    trans2D.Effect = SpriteEffects.None;
                    anim2D.Play(true);
                    direction = RIGHT;
                    break;
               case AnimState.Left:
                    anim2D.CurrentAnimation = "Running";
                    trans2D.Effect = SpriteEffects.FlipHorizontally;
                    anim2D.Play(true);
                    direction = LEFT;
                    break;
          }
     }

     lastState = currentState;

     // Move sprite
     trans2D.X += direction * SPEED * (gameTime.Milliseconds / 10);

     // Check borders
     if (trans2D.X < BORDER_OFFSET)
          trans2D.X = BORDER_OFFSET;
     else if (trans2D.X > WaveServices.Platform.ScreenWidth - BORDER_OFFSET)
          trans2D.X = WaveServices.Platform.ScreenWidth - BORDER_OFFSET;
}

Añadimos a la instancia Mario el Behavior del Joystick:

AddComponent(new MarioBehavior(EntityManager.Find("joystick")))

La instancia Mario por lo tanto quedaría:

var mario = new Entity("Mario")
.AddComponent(new Transform2D()
{
     X = WaveServices.Platform.ScreenWidth / 2,
     Y = WaveServices.Platform.ScreenHeight - 46,
     Origin = new Vector2(0.5f, 1)
})
.AddComponent(new Sprite("Content/Mario.wpk"))
.AddComponent(Animation2D.Create<TexturePackerGenericXml>("Content/Mario.xml")
.Add("Idle", new SpriteSheetAnimationSequence() { First = 3, Length = 1, FramesPerSecond = 11 })
.Add("Running", new SpriteSheetAnimationSequence() { First = 0, Length = 3, FramesPerSecond = 27 }))
.AddComponent(new AnimatedSpriteRenderer(DefaultLayers.Alpha))
.AddComponent(new MarioBehavior(EntityManager.Find("joystick")));

Si ejecutamos ahora nuestro juego podemos probar que efectivamente pulsando en la cruceta el lateral izquierdo Mario corre a la izquierda y exactamente lo mismo en dirección opuesta ocure si pulsamos la derecha.

Convirtiendo el proyecto a Windows Phone

Pero… espera…el proyecto es una aplicación Windows de escritorio, ¿no ibamos a desarrollar el juego para Windows Phone?

Esto… efectivamente. Vamos a descubrir a continuación una de las grandes características que ofrece el engine que no es otra que poder convertir nuestro proyecto a las siguientes plataformas:

  • Windows Phone 7/8. Por ahora es un proyecto Windows Phone 7. Por compatibilidad binaria funciona sin problemas en Windows Phone 8.
  • IOS (iPhone, iPad & iPod Touch),
  • Windows 8 (Aplicación Windows Store).
  • Android.
WAVE ENGINE Converter Tool

WAVE ENGINE Converter Tool

NOTA: La herramienta la podéis encontrar dentro de una carpeta llamada “Tools” en el directorio de instalación.

¿Cómo la utilizamos?

Muy fácil. Pulsamos el botón Open para seleccionar la solución (archivo .sln) a convertir. Marcamos las casillas de las plataformas que deseamos como destino y pulsamos el boton Convert.

Una vez que termina la conversión si nos dirigimos al directorio de la solción tendremos una nueva solución mas su carpeta asociada por cada casilla marcada (plataforma destino).

Listo!.

¿Ya?. Casi. Las soluciones estan practicamente listas a falta de los recursos (nuestro archivos wpk). Podemos reutilizar los ya utilizados o podemos crear nuevos archivos de recursos adaptados a las nuevas plataformas.

NOTA: Recordar establecer el valor Content en la propiedad Build Action.

Tras añadir los recursos, podemos ejecutar el proyecto convertido en el emulador de Windows Phone o en un dispositivo físico donde podremos probar nuestro juego!

Nuestro juego en Windows Phone!

Nuestro juego en Windows Phone!

En nuestro caso hemos convertido la solución a un proyecto Windows Phone. Le hemos añadido y a continuación tenéis disponible el resultado:

Podéis ver a continuación un video del juego en funcionamiento:

Espero que lo visto en la entrada os haya resultado interesante. Recordar que cualquier duda o sugerencia la podéis dejar en los comentarios de la entrada.

Extra

Es sumamente recomendable que utilicéis la herramienta Sample Browser. Es una herramienta extra de los chicos de Wave Engine que nos permite ver y descargar una gran cantidad de ejemplos realizados con el motor. Una fuente sin duda excelente de aprendizaje. Todo lo visto en esta entrada esta perfectamente cubierto en los ejemplos. Muy recomendado!

Sample Browser

Sample Browser

Conclusiones

Estamos ante un engine joven pero lo suficientemente sólido como para desarrollar juegos de peso. Es gratuito, nos permite desarrollar bajo C# y además podemos exportar nuestros juegos a multitud de plataformas. Sin duda, gran trabajo el realizado hasta ahora por el equipo de Wave Engine con actualizaciones periódicas constantes afinando poco a poco el engine.

Keep Pushing!

Más información

[Windows Phone] Creando juegos con WaveEngine. 1º Parte.

Introducción. Desarrollar videojuegos para Windows Phone.

windows_phone_game_updateCon el lanzamiento de Windows Phone 7 un enorme mundo de posibilidades se abría ante los desarolladores entre los que se incluía sin duda el desarrollo de videojuegos. Contábamos con XNA como vía sólida y bien documentada para desarrollar videojuegos bajo C# para Windows Phone.

Este mismo año se confirmó que no habría nuevas version de XNA, es decir, XNA ha sido descontinuado. Esto no quiere decir que no podamos crear videojuegos para Windows Phone utilizando XNA. Seguimos contando con plantillas para Windows Phone 7 y por compatibilidad binaria funcionarían en Windows Phone 8 aunque en un futuro ante la inexistencia de futuras versiones quizás sea positivo conocer alternativas…

¿WaveEngine?

WaveEngine LogoWave Engine es un motor de videojuegos 2D y 3D multiplataforma, basado en componentes y totalmente gratuito (no hay licencias ni tampoco ningún tipo de royalty). Nos ofrece una API muy completa para trabajar con físicas, animaciones, sonido, etc. utilizando C# sin tener que profundizar a más bajo nivel y tener que trabajar con DirectX directamente.

Podemos desarrollar para plataformas Windows, Windows Desktop, Windows Store y por supuesto Windows Phone.

Además, podemos desarrollar para Android (móviles y tabletas) usando MonoDroid y para IOS utilizando MonoTouch. Así pues si queremos publicar nuestros juegos bajo estas plataformas (Android e IOS) necesitamos adquirir las licencias correspondientes de Xamarin.

NOTA: Xamarin cuenta con una versión Starter totalmente gratuita que permite publicar tus aplicaciones o juegos siempre que cuenten con menos de 32000 líneas de código C# compilado.

Primer proyecto con WaveEngine.

Antes de lanzarnos a la piscina por completo debemos instalar WaveEngine. Una tarea sumamente sencilla. Nos dirigimos a la página oficial:

Accedemos a la sección de descargas:

Downloads

Downloads

Obtenemos el instalador e instalamos WaveEngine.

Manos a la obra!

Tras instalar WaveEngine al abrir Visual Studio tendremos un par de plantillas disponibles bajo la secciñon “Wave Engine”:

  • WaveEngine Library: Nos permite crear una librería donde añadir clases a reutilizar, recursos, etc.
  • WaveEngine Game Project: Crea una solución con dos proyectos, uno con el nombre que hayamos introducido y otro con el mismo nombre mñas el  añadido “Project” al final.
Plantillas

Plantillas

Elegimos la segunda opción y creamos un proyecto.

Conceptos básicos

  • Escena: Concepto básico importante. Una escena es el conjunto de elementos de nuestro juego en un momento dado. Por ejemplo, el menu de nuestro juego es una escena, cada fase del mismo es también una escena. A tener en cuenta, en cada momento solo trabajamos con una de las escenas.
Escena

Escena

Por buscar similitudes e intentar comprenderlo con mayor facilidad podemos trasladar el concepto de escena a un cubo de piezas de lego.

  • Entidad: Cada uno de los elementos que componen el juego. El suelo, el fondo, el personaje principal, etc.
Entidad

Entidad

Siguiente con la metáfora de lego, la casa formada en lego, el coche, etc. serían las entidades. Bloques formados por piezas inferiores (componentes).

  • Componentes: Nos permiten añadir funcionalidad (“piezas”) a las entidades. Lo utilizaremos por ejemplo para posicionar elementos en pantalla o para establecer el tamaño de los mismos.
  • Comportamiento: Que el personaje principal sea quien es lo reflejan una serie de comportamientos, que se mueva o realice acciones según controles de teclado o táctiles por ejemplo es un comportamiento.

La plantilla de la que partimos nos crea por defecto una escena con fondo azul (MyScene.cs) donde poder comenzar a añadir entidades con sus respectivos comportamientos que irán definiendo nuestro juego.

Añadiendo entidades

En nuestro ejemplo vamos a homenajear al mítico “Super Mario Bros” de la NES por lo que necesitaremos para ello sprites y recursos gráficos.

Podemos encontrar rápidamente recursos para la causa en: http://spritedatabase.net

Tras obtener los recursos necesarios, debemos detenernos tan solo un segundo a comprender el concepto de asset. Un asset es cualquier recurso externo (imagen, audio, video, etc) que queramos utilizar en nuestro juego. NO podemos introducirlos al proyecto y trabajar con ellos directamente. Desde WaveEngine trabajamos con archivos con extensión wpk.

¿Cómo transformamos nuestros jpg o png a wpk?

Al instalar WaveEngine se nos instala una herramienta auxiliar llamada Assets Exporter:

Assets Exporter

Assets Exporter

Esta herramienta nos permite crear proyectos que al ser exportados contarán con una carpeta llamada “Exports” donde tendremos los wpk buscados.

Comenzamos a usarla. Pulsamos en File -> New Proyect (creará un fichero con extensión .wproj). Asignamos un nombre. A continuación, podemos añadir recursos pulsamos e botón +. Tras añadir los recursos deseados hacemos clic en Proyect -> Export. Nos dirigimos a la carpeta “Exports” copiamos el wpk y lo copiamos a la carpeta “Content” de nuestro proyecto en Visual Studio.

Una vez copiados los recursos debemos modificar sus propiedades (click derecho, opción Properties):

  • Copy to output folder: Always

Todo listo, a por el código!.

Lo primero que vamos a añadir es el fondo de nuestro juego:

var sky = new Entity("Sky")
.AddComponent(new Sprite("Content/Sky.wpk"))
.AddComponent(new SpriteRenderer(DefaultLayers.Alpha))
.AddComponent(new Transform2D()
);

Hemos creado una entidad llamada Sky donde utilizamos componentes para establecer el wpk a utilizar (la imagen anteriormente exportada a wpk con el Assets Exporter). El componente SpriteRenderer nos permite dibujar en pantalla y el último componente utilizado, Transform2D nos permite posicionar el sprite en pantalla.

Fácil, ¿verdad?

NOTA: Un archivo wpk puede contener múltiples recursos.

Una vez creada la entidad la añadimos a escena:

EntityManager.Add(sky);

El resultado será algo como lo siguiente:

Añadimos el fondo

Añadimos el fondo

Ya no es el fondo azul que nos implementa la plantilla por defecto y comienza a recordarnos en algo al famoso juego del fontanero.

Continuamos. Si algo también era típico en los juegos de Mario eran los ladrillos. Los ladrillos fundamentales forman el suelo por el que Mario puede correr. Vamos a definir el suelo.

¿Qué debemos hacer?

Exacto, en WaveEngine todo lo que compone la escena es una entidad. Vamos a definir otra entidad para el suelo.

var floor = new Entity("Floor")
.AddComponent(new Sprite("Content/Floor.wpk"))
.AddComponent(new SpriteRenderer(DefaultLayers.Alpha))
.AddComponent(new Transform2D()
{
Origin = new Vector2(0.5f, 1),
X = WaveServices.Platform.ScreenWidth / 2,
Y = WaveServices.Platform.ScreenHeight
});

A diferencia de la anterior, en esta ocasión dado que queremos situar el suelo en la parte inferior de la pantalla, hemos establecido las propiedades Origin, X e Y del componente Transform2D.

Añadimos el componente a escena:

EntityManager.Add(floor);

Y el resultado:

Añadimos el suelo

Añadimos el suelo

Nos aproximamos. Repitiendo el proceso realizado con el suelo con un sprite de Mario podríamos obtener:

Mario en escena!

Mario en escena!

Puedes descargar el ejemplo realizado a continuación:

Bonito “cuadro” aunque como juego… si no podemos ni mover a nuestra voluntad al personaje principal carece de mucho sentido.

Además, de momento nuestro proyecto es para escritorio Windows no para Windows Phone…

Proximamente…

En la segunda parte añadiremos comportamiento a Mario para poder controlarlo mediante un touchpad táctil junto a un sistema de animaciones que nos permite disfrutar de Mario corriendo por el escenario. Por supuesto también convertiremos el proyecto a Windows Phone.

Os dejo un adelanto del resultado buscado:

Espero que os suene interesante, ¿os animáis?

Keep Pushing!

Más información

[Evento CartujaDotNet] Wave Engine. Desarrollo de videojuegos multiplataforma!

El evento

El desarrollo de aplicaciones para dispositivos móviles es un área que gana adeptos y suma peso día a día. Entre las aplicaciones destacadas, más descargadas y que aportan grandes beneficios contamos con los juegos. Dada la variedad de dispositivos, SDKs, herramientas y lenguajes a aprender, sacar el máximo partido a cada plataforma con eficacia y rapidez es una tarea complicada.  Wave Engine es un motor multiplataforma en 3D para facilitar la adaptación de los juegos móviles a cualquier plataforma (Android, iOS, Windows Phone y Windows 8). Incluye multitud de herramientas que facilitan tareas importantes como la gestión de publicidad, analítica del juego, etc.

En este evento se realizará una introducción al engine donde se mostrarán todas sus  posibilidades.

WaveEngine

Fecha

El evento tendrá lugar el próximo Jueves, 23 de Mayo de 19:30h a 21:30h (2 horas de duración).

Lugar

Tendrá lugar en el Cloud Pointing de Sevilla situado en el Parque Empresarial Nuevo Torneo. Tenéis la información exacta del lugar a continuación:

c\ Biología, 12, Edificio Vilamar 2, 3ª Planta
Parque Empresarial Nuevo Torneo
41015 Sevilla

Ponentes

  • Marcos Cobeña (LIGHTYEAR): Developer Advisor en Plain Concepts.
  • David Ávila (WOODY): Software Developer Advisor Plain Concepts.

Más información

¿Te lo vas a perder?