[Wave Engine] Animando a Mai

Buenas! Este post es el segundo sobre WaveEngine, esta maravilla que han parido los chicos de Plain Concepts 🙂

En el post anterior, vimos los fundamentos de Wave Engine y terminamos con un programa que mostraba a Mai Shiranui en la esquina inferior izquierda de la pantalla. Pero Mai Shiranui gana mucho cuando se mueve (¿por qué será?) así que vamos a ver como podemos hacerlo para que nuestra bella protagoniste se anime.

Aunque a tenor del post anterior pueda parecer que tan solo hemos mostrado una imagen por pantalla, desde el punto de vista de Wave hicimos mucho más: definimos una entidad y le asociamos entre otros el componente de sprite.

Hoy vamos a añadir un componente nuevo, el de animación. Y es que, obviamente, en Wave los sprites pueden estar animados.

Sprites sheets

En Wave una animación consta de varios “estados” (varias animaciones realmente) y cada estado consta de varias imágenes. Así podemos tener una animación (realmente un componente de tipo Animation2D) que defina los estados “Idle” (cuando el personaje no hace nada) y  “walk” (cuando el personaje camina). Cada uno de esos estados consta de varios gráficos que conformarán cada de esos estados.

En este post la animación tendrá un solo estado (que llamaremos “idle”) y que constará de 12 imágnes (12 pngs que se llaman mai_idle (0).png hasta mai_idle (11).png).

Por supuesto podríamos usar la herramienta Wave Exporter para generar 12 .wpks (es decir uno por cada png tal y como hicimos en el post anterior) pero es mucho más eficiente usar un sprite sheet. Los que vengáis del mundo web probablemente conocereis este concepto ya que se usa mucho en CSS. La razón es que es mucho más eficiente bajarse una sola imagen grande y mostrar (por CSS) solo la parte que nos interesa que descargarse varias imágenes pequeñas. Pues en videojuegos ocurre lo mismo: es mucho mejor tener una sola textura (recordad que las imágenes son realmente texturas) grande y mostrar tan solo una parte que tener varias texturas pequeñas.

El primer paso consiste en generar una sola imagen (un png) que combine todas las imágenes que formaran parte de mi sprite sheet. Ni se te ocurra hacerlo a mano (cortando y pegando), ya que por suerte hay herramientas para ello. En los tutoriales de Wave hay una explicación muy buena de como hacerlo paso a paso utilizando Texture Packer. Para no repetirme yo haré un ejemplo utilizando otra herramienta, llamada Shoebox. La verdad es que Texture Packer es más completa (pero hay funcionalidades de pago), pero usar Shoebox me va a permitir contaros una cosilla más sobre Wave 🙂

Lo primero es generar el .png que contenga los 12 pngs, esto con Shoebox es muy sencillo. Arranco Shoebox, selecciono los 12 pngs desde el explorador de archivos y los arrastro sobre el icono “Sprite Sheet” de la ventana de Shoebox. Hecho esto me aparece una ventana con el sprite sheet:

image

Con el botón se settings puedo modificar varias propiedades, como p. ej. si la textura debe ser cuadrada, debe tener un tamaño que sea potencia de dos (eso es deseable en según que motores gráficos por temas de rendimiento) y también el formato del fichero XML que nos generará:

image

Sí, he dicho formato XML, porque tener un png grande con todos los pngs pequeños incrustados no sirve de nada si no tenemos manera de saber donde empieza y termina cada “sub-imagen”. ShoeBox nos genera un fichero XML con toda esa información (Texture Packer también, por supuesto).

En mi caso el contenido del fichero XML es el siguiente (en el formato por defecto que genera Shoebox):

<TextureAtlas imagePath="sheet.png">

    <SubTexture name="mai_idle (0).png" x="0" y="189" width="77" height="93"/>

    <SubTexture name="mai_idle (1).png" x="0" y="0" width="77" height="92"/>

    <SubTexture name="mai_idle (2).png" x="0" y="94" width="77" height="93"/>

    <SubTexture name="mai_idle (3).png" x="157" y="98" width="76" height="95"/>

    <SubTexture name="mai_idle (4).png" x="157" y="0" width="76" height="96"/>

    <SubTexture name="mai_idle (5).png" x="235" y="192" width="76" height="95"/>

    <SubTexture name="mai_idle (6).png" x="235" y="0" width="76" height="93"/>

    <SubTexture name="mai_idle (7).png" x="79" y="0" width="76" height="92"/>

    <SubTexture name="mai_idle (8).png" x="79" y="192" width="76" height="93"/>

    <SubTexture name="mai_idle (9).png" x="157" y="195" width="76" height="95"/>

    <SubTexture name="mai_idle (10).png" x="79" y="94" width="76" height="96"/>

    <SubTexture name="mai_idle (11).png" x="235" y="95" width="76" height="95"/>

</TextureAtlas>

Básicamente se incluye el nombre del png original y su posición y tamaño dentro del png “global”.

Ahora usamos Assets Exporter para convertir el png grande a un asset en formato .wpk e incluímos dicho asset y el .xml en el proyecto de VS2012:

image

Y con eso ya tendremos suficiente. Los 12 pngs originales no los necesitamos para nada.

Animaciones 2D en WaveEngine

Ahora el siguiente paso es modificar la definición de entidad para añadirle un componente nuevo de tipo Animation2D. Este componente, a pesar de su nombre, puede contener un conjunto de animaciones. De momento nosotros tendremos tan solo una (Mai en estado idle), pero podríamos tener varias animaciones distintas (andar, saltar, etc) usando un solo sprite sheet.

El código de definición de la entidad es como sigue:

var mai = new Entity("Mai").

    AddComponent(new Transform2D()

    {

        X = 50,

        Y = WaveServices.Platform.ScreenHeight 46,

        Origin = new Vector2(0.5f, 1)

    }).

    AddComponent(new Sprite("Content/mai.wpk")).

    AddComponent(Animation2D.Create<ShoeBoxXmlSpriteSheetLoader>("Content/mai.xml").

        Add("Idle", new SpriteSheetAnimationSequence() {

            First = 1,

            Length = 12,

            FramesPerSecond = 9

        })).

    AddComponent(new AnimatedSpriteRenderer(DefaultLayers.Alpha));

Las dos diferencias respecto el código del post anterior (además del uso de la fluent interface) son:

  1. Que se añade el componente Animation2D
  2. El componente SpriteRenderer es modificado por el AnimatedSpriteRenderer

Centrémonos en el primer punto: llamamos al método estático Animation2D.Create para crear una animación. A dicho método le tenemos que pasar un parámetro genérico que es una clase que va a ser la encargada de indicar donde, dentro del asset gráfico, está cada frame de la animación. En mi caso uso la clase ShoeBoxXmlSpriteSheetLoader. Esta clase me la he creado yo y lo que básicamente hace es leer el fichero XML generado por Shoebox y devolver un array de objetos Rectangle que indican la posición y tamaño de cada frame de la animación. El código es como sigue:

class ShoeBoxXmlSpriteSheetLoader : ISpriteSheetLoader

{

    public Rectangle[] Parse(string path)

    {

        var doc = XDocument.Load(path);

        var descs = doc.Descendants(XName.Get("SubTexture")).

            Select(node => new Rectangle

            {

                X = int.Parse(node.Attribute("x").Value),

                Y = int.Parse(node.Attribute("y").Value),

                Width = int.Parse(node.Attribute("width").Value),

                Height = int.Parse(node.Attribute("height").Value)

            }).ToArray();

 

        return descs;

    }

}

Una vez tenemos el Animation2D creado debemos añadirle todas las animaciones que realmente contiene (recuerda que puede contener varias). En este caso tan solo contiene una, y por ello tenemos tan solo una llamada al método Add. A cada animación le damos un nombre, y luego un objeto SpriteSheetAnimationSecuence que determina cual es el frame inicial (dentro del Animation2D), cual es el final y cuantos frames deben renderizarse por segundo. En mi caso le coloco 9, y así toda la animación (que consta de 12 frames) tardará 1,3 segundos en realizarse lo que me pareció un valor razonable.

Finalmente el resto del código de la escena es parecido al del post anterior:

EntityManager.Add(mai);

mai.FindComponentOfType<Animation2D>().Play(true);

Añadimos la entidad a la escena y empezamos a reproducir la animación. El método Play reproduce la animación actual dentro del componente Animation2D (en nuestro caso solo hay una, si hay más de una se puede usar la propiedad CurrentAnimation del propio componente). El parámetro booleano que vale true es para indicar que la animación se vaya repitiendo en un bucle.

¡Y listos! Con esto ya tenemos a Mai en todo su esplendor: http://screencast.com/t/eUwEVcSVn6xg

Let’s fight!

Espero que os haya resultado interesante…

Un saludo!!!

ASP.NET MVC – Patrón PRG sin sesión

Buenas! El patrón PRG (Post – Redirect – Get) es un patrón muy usado en el desarrollo web. Consiste en que la respuesta de una petición POST es siempre una redirección, lo que genera un GET del navegador y de ahí el nombre.

La idea que subyace tras el patrón PRG es, que dado que dado que las peticiones GET son (¡deberían ser!) idempotentes esas son las únicas que el usuario debe poder refrescar. De hecho los navegadores nos avisan si refrescamos una petición POST:

image

La razón de este aviso no es tanto notificar al usuario que se reenviarán esos datos, la razón es que el hecho de que se envíen via POST hace que se asuma que dicha petición no es idempotente, o dicho de otro modo modifica datos en el sistema (da de alta un usuario, o borra un producto o realiza una compra). Es pues un mecanismo de protección.

Para evitar esto en el patrón PRG cada método de acción que gestiona un POST, no devuelve la vista con el resultado de dicha petición si no que devuelve una redirección a otra acción que es la que muestra el resultado. Así si tenemos una vista que tiene un formulario como el siguiente:

@using (Html.BeginForm())
{
    @Html.TextBox("somevalue")
    <input type="submit" value="send post" />
}

El método de acción podría ser algo como:

[HttpPost]
public ActionResult Index(string somevalue)
{
    // Procesar resultados.
    var id = new Random().Next();
    // ...
    return RedirectToAction("View", new { id = id });
}

El método que procesa el POST realiza las tareas que sean necesarias y luego redirecciona a otra acción, por lo que lo después de enviar el formulario el usuario refresca la página, refrescará la última petición web que es la petición GET (en lugar de intentar refrescar el POST).

Usar el patrón PRG es una muy buena práctica, pero conlleva un pequeño problema: como pasar información desde la acción POST hacia la acción GET.

P. ej. en el POST podemos crear o modificar datos de alguna entidad y luego en el GET podemos mostrar esa entidad creada o modificada. Una solución es pasarle el ID de la entidad (tal y como se hace en el código anterior). Eso es perfectamente válido pero implica un round-trip a la BBDD. En el POST teníamos los datos de toda la entidad, pero en el GET debemos recuperarlos de nuevo ya que solo tenemos el ID.

Si hubiese alguna manera de pasar todos los datos necesarios (p. ej. toda la entidad) des del POST hacía el GET entonces, en algunos casos, nos podríamos ahorrar tener que ir a buscar datos que ya teníamos.

En ASP.NET MVC existe un mecanismo pensado para transferir datos entre redirecciones, que es justo lo que necesitamos y es TempData:

[HttpPost]
public ActionResult Index(string somevalue)
{
    // Procesar resultados.
    var id = new Random().Next();
    var someData = new Person() { 
       Id = id, Name = somevalue 
     };
    TempData["someData"] = someData;
    return RedirectToAction("View", 
          new { id = id });
}
 
[ActionName("View")]
public ActionResult ViewGet(string id)
{
    var data = TempData["someData"] as Person;
    if (data == null)
    {
        // Recargar data usando el id que 
        //tenemos por parámetro
    }
    return View(data);
}

Fíjate que usar TempData no exime de pasar el id igualmente a la acción GET ya que los datos que recogemos de TempData pueden no existir. Si el usuario refresca la página o bien teclea directamente la URL de la acción View los datos de TempData no existirán. Recuerda que TempData es un contenedor que permite una sola lectura (los datos desaparecen una vez leídos).

Bien, el punto a tener en cuenta (y motivo principal de este post) al usar TempData es que éste usa sesión. Y no siempre podemos o queremos usar la sesión. Si por cualquier razón no queremos usar la sesión, ¿podemos seguir usando TempData?

La respuesta es que si, pero debes implementarte un custom TempData provider. Claro que la otra pregunta es donde podemos guardar esos datos. Recuerda que TempData debe persistir entre peticiones (de ahí que por defecto se use la sesión). No hay muchos lugares más donde lo podamos guardar, así que si estás pensando en cookies has dado en el clavo. Si en el POST enviamos una cookie, cuando el navegador realice la petición GET posterior reenviará la cookie que contendrá los datos de TempData.

Para crear un proveedor propio de TempData tenemos que seguir 2 pasos:

  1. Crear una clase que implemente ITempDataProvider. Esta interfaz define dos métodos (SaveTempData y LoadTempData).
  2. Redefinir el método CreateTempDataProvider de la clase Controller y devolver una instancia de nuestra clase que implementa ITempDataProvider. Esto debemos hacerlo en cada controlador que queramos que use nuestro TempData provider o bien lo podemos poner en un controlador base.

No voy a colgarme medallas que no me pertenecen poniendo la implementación de un proveedor de TempData que use cookies, ya que hay varios por internet e incluso en el código fuente de MVC4 viene uno. Está en el código fuente pero no en los binarios, ya que forma parte del paquete Mvc4Futures. Si quieres usarlo, debes instalar primero este paquete usando Install-Package Mvc4Futures desde la cónsola de NuGet o bien usando la GUI.

Y ya puedes usar TempData sin necesidad de usar la sesión! 😉

Saludos!

Herramienta: HFS – Http File Server

Muy buenas! Cuando preparo demos de HTML5 y JS, si no hay involucrado un servidor de por medio, no suelo utilizar VS para generar el proyecto si no algún editor más liviano, como Sublime Text o Notepad++ (personalmente prefiero el primero mil veces al segundo).

El único problema reside en que algunos navegadores, por seguridad, no ejecutan Javascript cuando el origen es file:// (es decir cuando estamos cargando un fichero del sistema de ficheros). P. ej. tengo una página que usa el API de geolocalización de HTML5 para mostrar mis coordenadas y cuando la cargo desde el sistema de ficheros, Chrome deniega la petición para geolocalización automáticamente, sin preguntar:

image 

Por otro lado IE no es tan restrictivo, pero me salta con un mensaje diciendo que los scripts (o ActiveX) se han bloqueado y un botón para permitir su ejecución:

image

Bien, aunque esto personalmente me gusta (es una buena medida de seguridad) a veces, cuando preparas demos, da un poco por el saco. La solución es, obviamente, servir los ficheros via http, desde un servidor web, así que busqué la manera más sencilla de hacerlo.

Una es, teniendo instalado IIS, copiar los ficheros al directorio Inetpubwwwroot de IIS, pero hacer esto cada vez (además con un directorio protegido con derechos de administrador) es un peñazo.

Otra es crear un proyecto ASP.NET en Visual Studio, meter allí los html y ejecutarlo. Pero claro, iniciar VS tan solo para ejecutar un par de htmls y javascripts me parece excesivo. Pero vaya, eso es más o menos lo que iba haciendo, hasta que un día me dije “tiene que haber una manera más sencilla”.

Y nada, así di con HFS (Http File Server): un pequeño programa que al ejecutarlo crea un servidor http y empieza a servir los ficheros que tu le digas. Una vez lo descargas y ejecutas (no se instala ni nada), aparece la ventana principal:

image

Y luego tan solo arrastras los ficheros que quieres servir via http. P. ej. si arrastro el fichero c:personalgeolocalizacion.html automáticamente aparece en la lista de la izquierda, indicando que ya se puede acceder a él, via http:

image

Lo bueno: El fichero NO se copia en ningún otro directorio, ni nada parecido. No hay nada más a configurar. Ahora ya puedo abrir un navegador y ver mi fichero servido via http:

image 

Fíjate como ahora, la página está servida via http y Chrome si que me pregunta si quiero compartir mi ubicación con localhost:8080 (tal y como manda la especificación de HTML5).

Personalmente me parece una herramienta muy sencilla y útil y la quería compartir con todos vosotros 🙂

Un saludo a todos!

[WaveEngine] Primeros pasos…

Buenas! Hace algunos días, no muchos, que me estoy pegando (en el buen sentido de la palabra) con WaveEngine, esta maravilla que han creado los chicos de Plain Concepts.

Disclaimer: Este post (y todos los que puedan venir) no pretenden sustituir la documentación oficial. No me considero un experto en Wave ni de lejos, realmente soy un aprendiz de nivel 1 🙂 Simplemente voy a expresar mis experiencias y lo iré haciendo a medida que las vaya teniendo, así que bueno… puede haber inexactitudes, errores, omisiones, etc… en estos posts. Así que comentarios son más que bienvenidos.

Introducción – ¿Qué es Wave Engine?

Bueno, pues básicamente WaveEngine es un motor multiplataforma de videojuegos. No es el único hay una larga lista de ellos (algunos más multiplataforma que otros) como cocos2d (y sus derivados tales como cocos2dx), Delta Engine o el todopoderoso Unity3D. Todos ellos nacen con filosofías distintas, lo que termina redundando en características, y precios, distintos.

Wave Engine es totalmente gratuito: la descarga es gratuita y no hay que pagar licencia de ningún tipo ni royalty por juego publicado ni nada parecido. El único detalle a tener en cuenta es que Wave Engine permite desarrollar para iOS y Anrdoid a través de Monotouch y Monodroid (de Xamarin) y esos productos no son libres. Aquí pues hay un coste, que es la licencia de Monotouch y Monodroid. Por supuesto esto os aplica tan solo si quereis desplegar en Android o iOS.

Como todo motor de videojuegos, Wave nos ofrece una API de relativo alto nivel para evitar tener que lidiar directamente con DirectX (o OpenGL), además de integrar muchas otras facetas: animaciones, motores de física, etc. En definitiva, un ahorro de tiempo considerable.

Panorama actual del desarrollo de videojuegos en Windows 8

En Windows 7 y anteriores la situación del desarrollo de videojuegos era relativamente sencilla. Básicamente, motores de terceros aparte, había dos opciones básicas:

  1. Usar C++ y DirectX directamente. La opción más potente y la menos productiva ya que DirectX es una API de bajo nivel.
  2. Usar .NET (C#) y XNA. Una opción que ha sido muy usada por desarrolladores indie y pequeños estudios ya que XNA es una API de medio nivel, que evita que uno tenga que pegarse con DirectX directamente.

Con Windows 8 y la aparición de las nuevas aplicaciones para la Windows Store, el panorama ha cambiado. XNA no permite realizar aplicaciones para la Windows Store y además MS lo ha discontinuado. No habrá una futura versión de XNA.

El panorama oficial para desarrollar videojuegos para la Windows Store ahora es:

  1. Usar XAML y C#. No es óptimo ni de lejos, ya que no se usa toda la potencia gráfica del ordenador.
  2. Usar C++ y DirectX… Lo que después de venir usando XNA es un paso atrás en productividad descomunal.

Por suerte, la comunidad no se está quieta, y así ha surgido el proyecto SharpDX. SharpDX es un wrapper en .NET para DirectX. Usándolo podemos desarrollar videojuegos en C# y DirectX. Aunque es una mejora no te creas que es la panacea: DirectX es de bajo nivel por lo que SharpDX también lo es. Otra alternativa interesante es MonoGame que es un port de XNA. Como su nombre indica usa Mono (Monotouch y Monodroid) para permitir desarrollar videojuegos para iOS y Android y usa por debajo SharpDX para permitir hacer lo mismo para aplicaciones Windows Store.

Y finalmente un escalón por encima están los motores de videojuegos, como Wave. Por supuesto Wave por debajo usa SharpDX pero nosotros quedamos completamente al margen.

Estructura de un proyecto de Wave

Cuando instalamos Wave Engine nos aparecen nuevas plantillas de proyecto en VS2012:

image

Si seleccionamos la opción de “Game Project” VS2012 nos añadirá dos proyectos a nuestra solución. Uno con el nombre que hayamos elegido y otro con el añadido “Project” al final. No sé todavía porque se crean esos dos proyectos pero realmente el primero es el ejecutable y es una lanzadora del segundo. Supongo que esto es porque el primero es específico por cada plataforma mientras que el segundo (que tiene realmente todo el código) es el mismo por todas las plataformas. Sospecho que los tiros van por ahí.

A partir de ahí Wave usa conceptos muy simples de entender:

  1. Escena: Es toda la información de nuestro juego en un momento dado. P. ej. un videojuego que tuviese varios niveles  podría tener varias escenas. Otra opción sería tener una escena para el menú principal y otra para el juego en sí. En un momento dado se está ejecutando (por decirlo de algún modo) una escena.
  2. Componente: Es la unidad de modularización de Wave. Los componentes son como “piezas” que se añaden a las entidades. P.ej. para posicionar algo en pantalla (si estamos haciendo un juego 2D) vamos a necesitar un componente llamado Transform2D. Todas las entidades que tengan una posición 2D tendrán una instancia de este componente.
  3. Entidad: Cada uno de los elementos de los que se compone tu juego. El héroe, la princesa o los nubarrones del fondo son entidades.
  4. Comportamientos (Behaviors): Son componentes que permiten que una entidad tenga lógica, es decir se comporte de una manera u otra. Que hace que la princesa sea una princesa indefensa y el dragón un dragón que escupa fuego? Pues sus comportamientos.

Por defecto la plantilla de proyecto de Wave nos crea la clase que representa el juego y una escena vacía. Nuestra misión es crear entidades (con sus componentes y comportamientos) y añadirlas a la escena. Y con esto tendremos un juego 🙂

Hello World con Wave Engine

Venga, empecemos por lo básico de lo básico. Vamos a crear un pequeño programa en 2D que simplemente muestre un sprite. Luego más adelante veremos como animarlo y darle un poco de vida 😉

Lo primero que debemos hacer es crear un nuevo proyecto de tipo WaveEngine Game Project. En mi caso he llamado “Mai” al proyecto.

Con esto VS2012 me va a crear los proyectos “Mai” y “MaiProject”. Como he dicho antes el segundo es el que contendrá “toda la chicha” 🙂

En MaiProject se me habrán creado los ficheros Game.cs que contiene la clase que pone en marcha el juego y MyScene.cs, la única escena que (de momento) tiene nuestro juego.

Ahora, lo único que vamos a hacer es mostrar un gráfico en pantalla. Para ello debemos introducir otro concepto de Wave: los assets.

Un asset no es nada más que un elemento que proviene de un fichero externo y que forma parte de nuestro juego. P. ej. si quiero mostrar un fichero .png este .png será un asset. Pero lo mismo ocurrirá si tengo un modelo 3D exportado en formato .x p.ej. WaveEngine no entiende de formatos gráficos o de formatos 3D o de cualquier otro formato externo. Wave entiende tan solo de un formato de asset genérico, el .wpk. Por lo tanto NO podemos usar directamente un .png, si no que debemos convertirlo antes a este formato .wpk.

Para ello debemos usar la herramienta (que se instala junto con Wave) llamada Assets Exporter. Si la ponemos en marcha veremos una interfaz muy, muy negra:

image

En mi caso tengo un fichero .png, llamado mai_idle (0).png y quiero convertirlo a un .wpk para poder usarlo desde Wave. Para ello, debo crear un proyecto nuevo de assets exporter. Así que le doy a File->New Project y selecciono una carpeta. Ello me crea un fichero .wproj y una estructura de carpetas dentro de la carpeta seleccionada. Una de esas carpetas es llamada Assets y contendrá los ficheros de origen (en mi caso el .png). Para añadir assets al proyecto basta con pulsar el botón de “+” (el primero por la izquierda) y seleccionar el fichero. Al hacerlo el fichero es copiado automáticamente a la carpeta Assets.

Una vez tenemos todos los Assets podemos darle a exportar (Project –> Export) y en la carpeta “Exports” dentro de la carpeta que hemos elegido al crear el proyecto del assets exporter tendremos el fichero .wpk.

Ahora debemos copiar este fichero a la carpeta “Content” del proyecto de VS2102 y establecer en las propiedades del fichero “Copy to output folder” a “Always”:

image

Bien! Esta es la forma habitual de proceder con los assets 🙂

A partir de ahora ya tan solo nos queda codificar. En nuestro caso vamos a mostrar tan solo una imagen. Para ello vamos a crear una entidad (todo son entidades en Wave) que va a tener varios componentes. Vamos a construirlo paso  a paso. Todo el código va en el método CreateScene de MyScene. Empezamos por crear la entidad:

var mai = new Entity(«Mai»);

Y ahora vamos a irle añadiendo componentes. Empezaremos por una posición. Haremos que mai aparezca en la esquina inferior izquierda de la pantalla. En Wave una posición es un componente de tipo Transform2D (estamos en un videojuego 2D, los 3D son otro mundo):

mai.AddComponent(new Transform2D()

    {

        X = 50,

        Y = WaveServices.Platform.ScreenHeight 46,

        Origin = new Vector2(0.5f, 1)

    });

El siguiente paso es tener un asset gráfico. De hecho Wave, como buen motor, nos da el concepto de sprite, es decir un conjunto de gráficos:

mai.AddComponent(new Sprite(«Content/mai_idle (0).wpk»));

Al constructor de Sprite se le pasa el nombre del fichero .wpk que contiene el gráfico (técnicamente la textura). Un tema a destacar, que ya veremos en otro post, es que un fichero .wpk puede contener más de un gráfico de nuestro sprite.

Finalmente tan solo nos queda añadir el renderizador, es decir el componente que se encarga de “dibujar” en pantalla. Te puede parecer extraño que los renderizadores sean componentes, pero esto permite que una misma entidad se dibuje (se renderice) en pantalla de formas distintas. ¡Modularidad ante todo!

mai.AddComponent(new SpriteRenderer(DefaultLayers.Alpha));

Nota: No la he usado en este ejemplo, pero Wave tiene una API fluent, de forma que en lugar de ir haciendo mai.XXX cada vez, podeis encadenar las llamadas a AddComponent una tras de otra.

Finalmente debemos agregar esta entidad que hemos creado a la escena:

EntityManager.Add(mai);

¡Y listos! Hemos terminado, ya podemos darle a F5 para ver nuestra obra de arte:

image

En el siguiente post veremos como darle un poco de movimiento… que si alguien se merce ser vista en pleno movimiento es Mai Shiranui 😛 😛

[C#]–Enumeraciones y corutinas

¡Buenas!

Empezamos con una pregunta:

¿Cual es el resultado de este programa?

class Program

{

    static void Main(string[] args)

    {

        var data = Foos;

        foreach (var foo in data)

        {

            ChangeFooValue(foo);

        }

 

        var firstFoo = data.First();

        Console.WriteLine(firstFoo.Value);

        Console.ReadLine();

    }

 

    private static IEnumerable<Foo> Foos

    {

        get

        {

            for (var idx = 1; idx <= 10; idx++)

            {

                yield return new Foo(idx);

            }

        }

    }

 

    private static void ChangeFooValue(Foo foo)

    {

        foo.Value = foo.Value + 10;

    }

}

 

internal class Foo

{

    public int Value { get; set; }

    public Foo(int i)

    {

        Value = i;

    }

}

¿Ya lo has meditado?

Pues ahora la solución…

Aunque el sentido común te pueda decir que el valor que se imprimirá es 11, el valor que realmente se imprime es 1.

¿Y eso? A priori todo parece claro: Tenemos una propiedad llamada Foos que nos devuelve 10 objetos Foo (con valores del 1 al 10). Nos guardamos dichos valores en data, iteramos sobre ellos y añadimos 10 al valor de cada objeto Foo. Luego imprimimos el valor del primero de esos objetos. Todo está perfecto, salvo que el resultado es 1 y no 11 como debería ser.

La clave es en como está definida la propiedad Foo (usando yield return) lo que impacta directamente en lo que es la variable data. Por qué… ¿Qué es data? ¿Es una lista? ¿Es un array? ¿Alguien lo sabe?

Cuando usamos yield return no se crea un espacio de almacenamiento para guardar los valores que vamos devolviendo. Se van creando uno tras otro tantas veces como se necesita. Realmente data no contiene ningún objeto (la frase que he usado antes de “Nos guardamos dichos valores en data” es totalmente inexacta), es como un apuntador a “una colección virtual (de 10 elementos)”. Por eso cuando iteramos con el foreach pasamos 10 veces por el yield return y cuando luego usamos el .First() pasamos otra vez más por el yield return. Aunque antes en el foreach se han recuperado los 10 elementos de Foos, como no están guardados en ningún sitio, al hacer .First() se vuelve a recuperar el primero. Lo que crea un Foo nuevo cuyo valor es 1. De ahí el resultado final.

Si usas Resharper, que sepas que te va avisar:

image

Este aviso, simplemente te indica que se está recorriendo más de una vez el mismo IEnumerable (en este ejemplo se recorre una vez con el foreach y otra vez al usar .First()). Recorrer dos veces un IEnumerable puede tener consecuencias no esperadas o puede ser perfectamente correcto. Como Resharper no tiene manera de saberlo, te avisa, para que le eches un vistazo.

Esto es así tan solo si en algún momento NO se guardan en ningún sitio los elementos recuperados. Basta con hacer:

var data = Foos.ToList();

Ahora el resultado final es 11, ya que el método .ToList() copia los valores a una List<T> por lo que ahora data si que tiene almacenados los 10 valores Foo. Y cuando hacemos data.First() recuperamos simplemente el primer valor almacenado en data.

Un caso real…

Bien, probablemente pienses que esto tampoco tiene mucha importancia porque tu no vas por ahí haciendo “colecciones virtuales” con yield, pero que sepas que hay alguien que si que lo hace: alguien llamado EF. En un proyecto en el que estuve nos encontramos con este comportamiento precisamente con el uso de EF: Usábamos EF para recuperar ciertos registros de la BBDD que luego convertíamos a DTOs con un método extensor que dado un IEnumerable<T> devolvía un IEnumerable<TDto>. Dicho método extensor iteraba sobre el IEnumerable<T> original y devolvia otro IEnumerable de Dtos:

public static IEnumerable<R> Map<T, R>(this IEnumerable<T> source)

{

    foreach (var t in source)

    {

        yield return Mapper.Map<T, R>(t);

    }

}

(En nuestro caso la clase Mapper era AutoMapper).

A priori parece todo correcto: Obtenemos datos de EF y los mapeamos a DTOs. Si nos limitamos a devolver el IEnumerable obtenido por Map<T,R> todo va perfecto. El problema es si modificamos alguno de los DTOs que hemos obtenido. Cuando volvemos a iterar sobre la variable que contiene los resultados volvemos a obtener los resultados originales. Eso es debido a que EF crea una colección virtual (es decir usa yield return) para devolvernos los resultados y nuestro Map<T,R> hace lo mismo, por lo que en ningún momento hemos “guardado” esos datos en ningún sitio. Estamos en la misma situación que el ejemplo inicial de este post.

La solución pasa por materializar (es decir llamar a .ToList() o .ToArray()) el resultado devuelto por EF.

Para finalizar solo comentar que el uso de yield return en C# es un ejemplo de lo que se conoce como “corutina”. Una corutina no deja de ser una subrutina (es decir una función o pedazo de código) pero que es reentrante en distintos puntos. Otro ejemplo de corutinas en C# lo puedes encontrar en el uso de la palabra clave await (en C# 5).

¡Un saludo!