Novedades de IE11: SPDY

Como ya es habitual una nueva versión de Windows (en este caso la 8.1) viene acompañado de un nuevo IE11. Y como siempre IE11 viene con varias novedades siendo quizá las dos más destacadas el soporte para WebGL (gráficos 3D) y el tema de este post, el soporte del protocolo SPDY.

Este será un post ligeramente distinto a los habituales del blog porque no hablaré de nada de desarrollo 🙂

Un protocolo de Nivel 7

Niveles OSI (sacado de Wikipedia)

El nivel OSI de un protocolo de comunicaciones determina “cuan alejado del medio físico” está este protocolo. Apareció a mediados de los 80 y determina 7 capas por las cuales los datos deben pasar para llegar desde el medio físico (p. ej. un cable) hacia el usuario final.

Para una descripción detallada de cada nivel OSI podéis consultar este artículo de la microsoft. Por curiosidad os puedo comentar que el protocolo IP (tanto v4 como v6) está situado en el nivel 3, TCP está situado en el nivel 4, SSL (o su variante más moderna TLS) está situado en el nivel 5 y que HTTP está situado en el nivel 7. Por cierto, aprovecho para comentar que TCP e IP son dos protocolos, así que cuando decimos que internet “usa el protocolo TCP/IP” realmente queremos decir que usa “el protocolo TCP funcionando bajo el protocolo IP”. Pero bajo IP pueden funcionar otros protocolos (como UDP p. ej.) o al revés TCP puede funcionar bajo otros protocolos de nivel 3 como IPX.

Pues bien, SPDY (pronunciado speedy) es un nuevo protocolo, situado en la capa 7 de OSI que viene a reemplazar HTTP.

La frase en negrita del párrafo anterior es la frase sensacionalista, porque como ahora verás el hecho de que SPDY sustituya a HTTP no te afecta para nada como desarrollador de aplicaciones web. Dejemos esto bien claro a partir de ya, ¿ok? SPDY tiene impacto cero para los desarrolladores de aplicaciones web pues toda la semántica de HTTP se mantiene. De hecho familiarmente hay quien se refiere a SPDY como HTTP 2.0 pero eso no es ni mucho menos una denominación oficial y además se presta a confusión puesto que hay una especificación (en borrador) de HTTP 2.0 (que además se basa en SPDY pero eso es otra historia).

32x, 3.1Ghz, 12 Mpx…

Los seres humanos somos simplistas por naturaleza, generalmente nos agobia trabajar con demasiados datos y deseamos reducirlo todo a un solo indicador. Por supuesto los que se encargan de venderte cosas ya escogerán el indicador que les vaya mejor a ellos para venderte el producto con independencia de que dicho indicador sea el que mejor refleje la calidad global del producto. En los tiempos de las unidades de CD los fabricantes se esmeraban en tener un valor de “equis” lo más alto posible. Y así se asoció que una unidad de 50x era mucho mejor que una de 32x aunque esto era la velocidad punta y no la sostenida que sería mucho más importante (pero menos impactante) para dar cuenta del rendimiento de la unidad. Pero es que incluso la velocidad sostenida por si sola tampoco da toda la información. Algo parecido ocurre con los ordenadores que se suelen vender publicitando más los Ghz del procesador aunque este dato por si solo es totalmente irrelevante. O con las cámaras de fotos y los megapíxeles (solo hay que ver el revuelo que se está armando con este futuro nokia de 41 de megapíxeles). Los megapíxeles importan sí, pero es mucho más importante la calidad de las lentes, el estabilizador y, a partir de cierto número de Mpxs, el tamaño físico del CCD. Pero analizar todo esto son demasiados datos y como digo somos simplistas…

Esto mismo aplica a tu conexión de internet. Y es que… es mejor una conexión de 20 Megas que una de 15 no? Pues no, necesariamente. No voy a escribir el porque una conexión de 20 Megas puede ser peor que una de 15 (asumindo que ambas funcionen al 100% de su velocidad) porque ya lo hizo el fenómeno de José Manuel Alarcón en su blog. Léete el post porque es un must read.

Volvamos a SPDY. Seguramente la pregunta que nos podríamos hacer sería… ¿por qué un nuevo protocolo para reemplazar HTTP? HTTP funciona y es la base de Internet que es probablemente una de las revoluciones más importantes de la (corta) historia de la humanidad. ¿Entonces?

Hoy en día las páginas web se han echo muy complejas, para tener números actualizados acabo de probar varias páginas para contar cuantas peticiones http se realizan para cargarlas todas (el javascript, las imágenes, css, videos, etc). No quiero fijarme en el tamaño de los elementos transferidos, solo en cuantas peticiones http debe enviar el navegador. Pues bien, esos son algunos números:

  • facebook.com (con usuario logado): 153 peticiones http
  • marca.com: 455 peticiones http
  • twitter.com (con usuaruo logado): 44 peticiones http
  • google.com: 16 peticiones que se disparan a las 29 al empezar a teclear una búsqueda.

(Todas estas mediciones son usando las webs de producción y sin cache).

Como desarrollador web probablemente ya sabes lo importante que es minimizar el número de peticiones HTTP (usando CSS sprites, imágenes en data-uris, compactación de js, etc). Los navegadores también ayudan y suelen usar hasta 6 (antes eran 2) conexiones simultáneas.

Pero tenemos dos problemas ahí… veamos ambos.

Problema 1: Crear una conexión TCP es “lento”.

La “lentitud” de crear una conexión TCP viene dada porque usa un sistema conocido como 3-way-handshake para establecer dicha conexión:

El cliente primero debe enviar un paquete SYN, el servidor debe responder con un SYN ACK y finalmente el cliente debe responder con un ACK.

En este punto la conexión TCP está establecida y se pueden empezar a mandar datos.

 

Es decir para poder empezar a mandar datos hay una comunicación del cliente al servidor, una del servidor al cliente y otra del cliente al servidor. ¿Y cuando digo lento cuan lento quiero decir? Pues con un buen ADSL (y no me refiero a las megas) pues establecer una conexión TCP te puede costar de 20 a 30ms. Pero ojo… en un móvil estos tiempos se pueden disparar hasta los 300ms por cada conexión TCP.

En resumen: El ancho de banda NO es importante. Lo verdaderamente importante es la latencia (¿te ha dicho tu proveedor de adsl la latencia de tu conexión? ¿No, verdad?).

En HTTP1.0 cada petición HTTP abre y cierra una conexión TCP.

Solución: HTTP1.1 al rescate

En HTTP1.1 es posible usar conexiones keep-alive lo que básicamente significa que bajo una misma conexión TCP se pueden enviar varias peticiones HTTP solucionando así el problema anterior.

Actualmente (casi) todos los servidores y todos los navegadores soportan HTTP1.1 así que… problema arreglado 🙂

Problema #2: Todo el proceso es síncrono

Vale… gracias a las conexiones keep-alive de HTTP1.1 hemos “eliminado” el problema de la “lentitud” de abrir conexiones TCP. Pero nos queda el problema fundamental: Todo el proceso es síncrono:

HTTP sin pipeline

A pesar de que podemos utilizar una sola conexión TCP para enviar los datos, el navegador no puede enviar la segunda petición HTTP hasta haber recibido la primera y no puede enviar la tercera hasta haber recibio la segunda.

Si la segunda petición tarda mucho (por la razón que sea y en la dirección que sea) el navegador estará esperando y esperando hasta recibir la respuesta sin poder enviar la tercera petición.

 

Intento de solución: HTTP1.1 pipelining

HTTP1.1 además de conexiones keep-alive ofrecía un modo pipeline que básicamente viene a romper la sincronidad. Permite que el navegador envíe todas las peticiones de golpe y espere por las respuestas:

Pipelining en HTTP1.1

Con esto el problema #2 parece solucionado ¿no? El navegador puede enviar todas las peticiones y luego esperar a que lleguen todas las respuestas de golpe.

Pues no.

Primero hay un motivo práctico: Para que el pipeline de HTTP1.1 funcione es necesario que esté soportado por todos los dispositivos intermedios que hay entre el servidor y el funcione (o sea, los proxies) y hay algunos no lo soportan. Por esta razón HTTP1.1 pipelining apenas se usa (de hecho está soportado pero desactivado en casi todos los navegadores de la actualidad).

Pero incluso si HTTP1.1. pipelining se estuviese usando de forma masiva no es la solución ideal al problema. ¿Por qué? Pues porque HTTP obliga a una semántica FIFO en las peticiones. Es decir el navegador DEBE recibir la respuesta de la primera petición antes de recibir la respuesta de la segunda. Y DEBE recibir la respuesta de la segunda petición antes de la respuesta de la tercera. Y así sucesivamente.

Por lo tanto si hay una petición lenta todas las posteriores se verán retrasadas también, porque la respuesta a esta petición lenta debe ser enviada al navegador antes que las respuestas de las peticiones siguientes.

Solución: SPDY al rescate

Aquí es donde entra SPDY. Este protocolo, desarrollado inicialmente por Google, ofrece un pipelining real sobre una sola conexión TCP (además de otras mejoras) para de esta manera reducir los tiempos de latencia y espera.

Además SPDY añade más funcionalidades a HTTP (como server push) y toda la petición es comprimida (en HTTP se puede comprimir la respuesta pero no las cabeceras).

Y lo más importante de todo: SPDY no requiere ningún cambio en la infraestructura de red actual ni en las aplicaciones web desarrolladas. Insisto: a ti, como desarrollador web, que se use SPDY te es totalmente transparente. Recuerda: la semántica de HTTP (verbos, cabeceras, URLs) está totalmente mantenida. SPDY tan solo modifica en como se usa TCP por debajo (una sola conexión y pipelining real)

Para que se use SPDY tan solo es necesario que el servidor web y el navegador lo soporten. IE11 finalmente soporta SPDY y se une así a Firefox, Opera y obviamente Chrome. En Android la ultima versión de Chrome y Opera Mobile soportan SPDY. De Safari, la verdad no tengo ni idea. Y en el mundo de los servidores hay módulos SPDY para los principales servidores web. De IIS no tengo noticias, pero imagino que a partir de Windows 8.1 estará soportado (a nivel de http.sys supongo).

En fin, el soporte para SPDY de IE11 es una muy buena noticia que ayudará a que este protocolo se vaya extendiendo más y que todos tengamos una web (un poco) más rápida!

Como dije… un post diferente 😉

Saludos!

Backbone: El misterioso caso del sync que no se lanzaba

Muy buenas! Estos días he estado resolviendo un misterio que me sucedía con un proyecto utilizando Backbone.

En concreto, se supone que, a partir de la versión 1.0, cuando se guarda un modelo de Backbone al servidor (usando p. ej. save) si la operación tiene éxito, el modelo nos lanza el evento sync para informarnos, precisamente, del éxito de la operación.

Así, una secuencia típica de operaciones, se supone que es:

  1. El usuario hace click en un enlace, botón, o en cualquier elemento del DOM para que se “guarde” algo.
  2. La vista de Backbone recoge este evento y hace lo que tenga que hacer (validaciones, modificaciones de UI en cliente) para terminar modificando el modelo según los datos de la UI.
  3. Una vez el modelo está actualizado la vista llama a save() del modelo.
  4. Alguien (usualmente la propia vista) recoge el sync y hace lo que tenga que hacer (mostrar un mensaje, redirigir el usuario a otra acción del enrutador, etc).

Esa es la idea, sencilla ¿no? Llamaas al método save() del modelo y este te lanza el evento sync cuando el guardado en el servidor ha sido completado.

Todo muy bonito salvo que a mi no me iba. No se me lanzaba el evento sync, a pesar de que la llamada al servidor se completaba y no lanzaba ningún error.

Aquí un pequeño inciso para los que no conozcáis Backbone: el método save() del modelo lo que hace es una petición (usualmente POST) a una url (que se especifica al definir la “clase” del modelo) con los datos a guardar. La idea es que Backbone se integre muy fácilmente con una API estilo REST que tengamos. En mi caso, era una API hecha con WebApi. Pongo aquí el código más relevante del método de servidor invocado:

public void PostOne(TaskDto task)

{

    // Guardar datos…

}

Repito: El método se invocaba correctamente y el objeto TaskDto era guardado. De hecho en la ventana de Network de Chrome tenía lo siguiente:

image

Cuando ya estaba seguro (si es que se puede estarlo alguna vez) de que el error no estaba en mi código, empecé a fijarme en la respuesta enviada al navegador. ASP.NET WebApi devuelve código HTTP 204 (No Content) en los métodos que devuelven void. Tiene toda la lógica y es un uso coherente de los códigos HTTP, el 204 significa precisamente esto: Ha ido todo bien (es un 2xx) pero no hay datos adicionales al respecto.

Entonces me surgió la duda… ¿Y si Backbone no entiende el 204? Por supuesto esto sería un “error” de Backbone, pero lo probé. Mi forma de probarlo, muy pedrestre, fue “obligar” a WebApi a devolver algún resultado. En este caso entonces ya no se usa el 204 (no puede usarse el 204 si hay contenido en la respuesta) si no el más genérico 200.

Modifiqué el método de WebApi:

public string PostOne(TaskDto task)

{

    // Guardar datos…

        return "";

}

¡Genial! con eso ya tenía mi código de respuesta 200:

 image

Y…. ¡funcionó! El evento sync se me lanzaba tal y como se supone que debe ocurrir.

Luego pensé en otra cosa: “¿Y si no es el código 204 lo que BackBone no entiende si no una respuesta vacía?”… Para probar mi teoría modifiqué de nuevo el método PostOne:

public HttpResponseMessage PostOne(TaskDto task)

{

    // Guardar datos…

    return Request.CreateResponse(HttpStatusCode.OK);

}

Ahora estaba mandando el código 200 pero sin nada (ni una cadena vacía) en la respuesta. Pues bien… el evento sync no se lanzaba. Es decir, el problema NO era usar un código distinto a 200, el problema para Backbone era una respuesta sin cuerpo.

Así, que bueno… ya que Backbone parece insistir que quiere un cuerpo en  la respuesta, al final terminé con esta implementación:

public HttpResponseMessage PostOne(TaskDto task)

{

    // Guardar datos…

    return Request.CreateResponse(HttpStatusCode.Created, true);

}

El código Created és el 201, que mira, me parecía un poco más correcto que usar el genérico 200, ya que realmente esta API siempre añadía una tarea.

Y así, todos contentos: Backbone ya tiene su cuerpo en la respuesta y yo mi evento sync que se lanzaba.

Así que recuerda: A Backbone no le gustan las respuestas sin contenido cuando se guarda el modelo.

¡Saludos!

ASP.NET MVC – Tratando con enums.

En un proyecto ASP.NET MVC en el que estoy colaborando, surgió la necesidad de tratar con viewmodels que tenían propiedades cuyo tipo era un enum. Algo así como:

[Flags]

public enum TestEnum

{

    None = 0,

    One = 1,

    Two =2,

    Four =4

}

 

public class FooModel

{

    public TestEnum TestData { get; set; }

}

Los valores de TestEnum son combinables a nivel de bits (de ahí que esté decorado con [Flags], es decir el valor de la propiedad TestData puede ser cualquiera de los cuatro o bien una combinación (p. ej. One y Two).

ASP.NET MVC no gestiona, por defecto, bien estos casos. Imaginemos que tenemos el siguiente código en el controlador:

public ActionResult Index()

{

    var model = new FooModel();

    model.TestData = TestEnum.One | TestEnum.Two;

    return View(model);

}

Y el código correspondiente en la vista:

@using (Html.BeginForm())

{

    @Html.EditorFor(x => x.TestData)  

    <input type="submit" value="enviar" />

}

Lo que nos generará será un TextBox con los valores del enum (en este caso One y Two) separados por comas:

image

Es obvio que no es una buena manera que el usuario entre esos datos. Por su parte el enlazado funcionará correctamente (es decir en el método que recibe el POST se recibirá que la propiedad TestData vale TestEnum.One | TestEnum.Two, ya que el DefaultModelBinder sí que es capaz de tratar este caso

En el proyecto lo que se quería era que en lugar de mostrar este texto se mostraran tantas checkboxes como valores tiene el enum y que se pudiesen marcar una o varias.

Eso se consigue con relativa facilidad usando un EditorTemplate. En ASP.NET MVC los EditorTemplates (y sus equivalentes de visualización los DisplayTemplates) son vistas parciales que son renderizadas cuando se debe de editar o visualizar un valor de un tipo o propiedad en concreto. Los EditorTemplates se colocan por lo general en la carpeta Views/Shared/EditorTemplates (los Display Templates en la Views/Shared/DisplayTemplates).

Así pues creamos un Editor Template que nos cree tantas checkboxes como valores tiene el enum:

@model Enum

@{

    var modelType = @Model.GetType();

}

 

@foreach (var name in Enum.GetNames(modelType))

{

    var value = Convert.ToInt32(Enum.Parse(modelType, name));

    if (value != 0)

    {

        var isChecked = ((Convert.ToInt32(Model) & value) == value) ? "checked" : null;

        <input type="checkbox" name="@ViewData.TemplateInfo.HtmlFieldPrefix" value="@name" checked="@isChecked" />  @name<br />

    }

}

El código es relativamente sencillo y lo que hace es crear una checkbox por cada clave (valor) del enum y marcarla si es necesario (es decir, si un and a nivel a de bits entre el valor del enum y el valor de cada clave da distinto de cero). Hay una comprobación addicional para no renderizar una checkbox si el valor de la clave es 0 (en nuestro caso sería el None). Para usar este EditorTemplate (que yo he llamado FlagEnum.cshtml) una posibilidad es indicarle al viewmodel que lo use, decorando la propiedad con [UIHint]:

public class FooModel

{

    [UIHint("FlagEnum")]

    public TestEnum TestData { get; set; }

}

Dado que en nuestra vista ya usábamos EditorFor para generar el editor de la propiedad TestData, no es necesario ningún cambio más. El resultado es ahora más interesante:

image

Parece que hemos terminado, eh? Pues no… Aparece un problema. Si le damos a enviar tal cual, lo que ahora recibimos en el método que recibe los datos POST es:

image

¡Ahora no se nos enlaza bien el campo! Eso es debido a como funciona el Model Binder de ASP.NET MVC. La diferencia con antes (cuando había el textbox) es que antes teníamos un solo campo en la petición y ahora tenemos N (tantos como checkboxes marcadas). Si comparamos las peticiones enviadas, lo veremos mejor.

Esta es la petición que el navegador envía si NO usamos nuestro EditorTemplate:

POST http://localhost:19515/ HTTP/1.1
Accept: text/html
Referer: http://localhost:19515/
Origin: http://localhost:19515
Content-Type: application/x-www-form-urlencoded

TestData=One%2C+Two

Y esta otra la petición que se envía si usamos el EditorTemplate:

POST http://localhost:19515/ HTTP/1.1
Accept: text/html
Referer: http://localhost:19515/
Origin: http://localhost:19515
Content-Type: application/x-www-form-urlencoded

TestData=One&TestData=Two

Fijaos en la diferencia (marcada en negrita). El Model Binder por defecto de ASP.NET MVC es capaz de gestionar el primer caso, pero no el segundo, de ahí que ahora no funcione y nos enlace tan solo el primer TestData que se encuentra (y que vale One).

¿Y la solución? Pues hacernos un Model Binder propio que sea capaz de tratar estos casos. Por suerte el código no es excesivamente complejo:

public class EnumFlagModelBinder : DefaultModelBinder

    {

        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

        {

            if (!(bindingContext.Model  is Enum))

            {

                return base.BindModel(controllerContext, bindingContext);   

            }

 

            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (value.RawValue is string[])

            {

                var tokens = (string[]) value.RawValue;

                int intValue = 0;

                foreach (var token in tokens)

                {

                    var currentIntValue = 0;

                    var isIntValue = Int32.TryParse(token, out currentIntValue);

                    if (isIntValue)

                    {

                        intValue |= currentIntValue;

                    }

                    else

                    {

                        var modelType = bindingContext.Model.GetType();

                        intValue |= Convert.ToInt32(Enum.Parse(modelType, token));

                    }

                }

 

                return Enum.Parse(bindingContext.Model.GetType(), intValue.ToString());

            }

 

            return base.BindModel(controllerContext, bindingContext);

 

 

        }

    }

El código es muy sencillo:

Si el tipo del modelo no es un Enum derivamos hacia el enlace por defecto de ASP.NET MVC. En caso contrario obtenemos el valor de la propiedad que se está enlazando, consultando a los Value Providers.

Este valor será un string[] con los diversos valores que el usuario ha entrado (p. ej. un string[] con dos elementos “One” y “Two”). Iteramos sobre este array de valores y por cada valor:

  1. Miramos si es un entero. Si lo es, hacemos un or a nivel de bits entre una variable (intValue inicialmente a cero) y este valor.
  2. Si NO es un entero (p.ej. es “One”) obtenemos el valor entero con Enum.Parse y realizamos el mismo or a nivel de bits.

Finalmente devolvemos el valor asociado al valor de entero que hemos obtenido.

P. ej. si el usuario ha marcado las checks “One” y “Two” el array (tokens) tendrá esos dos valores. Al final IntValue valdrá 3 (el resultado de hacer un or de bits entre 1 y 2) y devolveremos el resultado de hacer Enum.Parse de este 3 (que es precisamente One | Two).

Nota: El hecho de mirar primero si el valor de token es un entero, es un pequeño refinamiento para que nuestro EditorTemplate funcione también en aquellos casos en que nos envíen los valores numéricos asociados (que nos envien p. ej. TestData=1&TestData=2 en la petición. Usando nuestro EditorTemplate este caso no se da).

Ahora tan solo debemos indicarle a ASP.NET MC que use nuestro Model Binder para enlazar las propiedades de tipo TestEnum. Aunque nuestro Model Binder podría enlazar cualquier Enum (es genérico) ASP.NET MVC no nos permite asociar un Model Binder a “cualquier Enum”. Para indicarle a ASP.NET MVC que use nuestro Model Binder para las propiedades de tipo TestEnum basta con añadir en el Application_Start:

ModelBinders.Binders.Add(typeof(TestEnum), new EnumFlagModelBinder());

Y ¡listos! Con esto hemos terminado.

Espero que os haya sido interesante.

[WaveEngine] Medidor de fps

Bueno… sigo mi serie de posts sobre WaveEngine. En los dos primeros posts vimos como poner un sprite en pantalla y luego como animarlo. Ambos pasos (y algunos más sobre los que todavía no he comentado nada) están descritos en uno de los hand-on-labs de Wave: el platform game sample.

Antes que nada el disclaimer obligatorio: En todos esos posts sobre Wave, explico la manera que he encontrado yo para hacer las cosas. Eso no significa que sea la mejor, la más óptima o incluso la correcta. Por el momento la documentación sobre Wave es bastante escueta. Algunos tutoriales, código fuente de algunos ejemplos y una referencia de todas las clases (que no sirve apenas para nada).

Bueno, aclarado esto, en este post voy a mostrar como crear un indicador que nos muestre, en todo momento, a cuantos frames por segundo se está ejecutando nuestro juego.

Jerarquía de elementos de Wave

En el primer post vimos que un juego en Wave tiene una jerarquía de elementos realmente sencilla:

  • Escena
    • Entidades
      • Componentes

Básicamente una escena es un conjunto de entidades y cada entidad contiene uno o varios (generalmente varios) componentes. Los componentes son cualquier cosa que es susceptible de formar parte de una entidad. Algunos componentes, como p.ej. Sprite son meros contenedores de datos, pero hay dos tipos de componentes especiales:

  1. Behaviors: Son componentes de “lógica”. Disponen de un método Update() que Wave llama automáticamente en cada iteración del game-loop. Para implementarlos debe derivarse de Behavior.
  2. Drawables: Son componentes de “dibujo”. Disponen de un método Draw() que Wave llama automáticamente en cada iteración del game-loop. Para implementarlos debe derivarse de Drawable2D o Drawable3D según sea el caso.

Por supuesto es posible que un componente dependa de otro componente. P. ej. el componente Sprite depende del componente Transform2D, es decir siempre que a una entidad se le agregue una instancia del primero, deberá agregársele también una instancia del segundo. Para indicar que un componente depende de otro, se usa el atributo RequiredComponentAttribute:

[RequiredComponent]

public Transform2D Transform2D;

Automáticamente Wave inyectará en la variable decorada con [RequiredComponent] el componente del tipo indicado (en este caso Transform2D) que esté añadido a la misma entidad.

Opción 1: Usar un Drawable

Para crear el monitor de fps vamos a crear un componente nuevo. Dado que debe dibujar en la pantalla (el valor de fps) el componente derivará de Drawable2D.

Por el mero hecho de derivar de Drawable2D obtenemos un método Draw que Wave nos llama en cada iteración del game-loop. Además nos pasa un parámetro que es un TimeSpan con el tiempo que ha pasado desde la llamada anterior. Eso es porque Wave no se sincroniza a ninguna velocidad en concreto. Es decir el game-loop de Wave básicamente:

  1. Llama al método Update() de todos los Behaviors
  2. Llama al método Draw() de todos los Drawables

Y eso lo hace continuamente y sin parar. Si queremos que nuestro videojuego vaya, al menos, a 30 fps, eso significa que la ejecución de todos los métodos Update y Draw de todos los componentes de todas las entidades de la escena debe tardar menos de 1/30 segundos. Cuanto menos tarde Wave en ejecutar todos componentes de la escena, nuestro juego irá a más fps. Por lo tanto, el fps no es un valor fijo si no que depende de la complejidad de la escena y del hardware que ejecuta el sistema. De ahí que Wave nos pase el TimeSpan para que podamos saber cuanto tiempo (cuantos milisegundos) han pasado desde la llamada anterior (este valor tampoco es constante ya que pueden aparecer o desaparecer entidades de la escena en cualquier momento).

Si derivamos de la clase Drawable2D veremos que existen DOS métodos Draw. Uno llamado Draw y otro llamado DrawBasicUnit que es abstracto y estamos obligados a implementar. Tras buscar en vano información sobre que diferencia había entre ambos métodos, al final me sumergí (con la ayuda de Resharper) en el código de Wave Engine y he llegado a la siguiente conclusión:

  • El código de renderizado debe ir en DrawBasicUnit. El parámetro que recibimos (un int llamado con el somero nombre de “parameter”) es el orden de nuestro Drawable. Es decir valdrá 0 si se ha invocado el primero, 1 si es el segundo, etc.
  • En el método Draw() debe ir todo aquel código que dependa de saber el valor del TimeStamp que mencionaba antes. Pero NO pongas código de renderizado en él.

Si tu Drawable no requiere sincronizarse con el tiempo (la gran mayoría no lo necesitan, ya que simplemente se dibujan cada vez) entonces no debes redefinir Draw(). Sólo debes implementar DrawBasicUnit.

Armado con esta suposición he codificado mi componente que muestra los fps tal y como sigue:

public class FpsMeasure : Drawable2D

{

    private int _fps;

    private int _currentSecondFps;

    private int _ms;

    private SpriteFont _font;

    private static int _instances;

 

    public FpsMeasure()

        : base("fps" + (_instances++), DefaultLayers.Opaque)

    {

    }

 

    protected override void Initialize()

    {

        base.Initialize();

        _font = Assets.LoadAsset<SpriteFont>("Content/Arial Rounded MT.wpk");

    }

 

    protected override void Dispose(bool disposing)

    {

    }

 

    public override void Draw(TimeSpan gameTime)

    {

        _ms += gameTime.Milliseconds;

        _currentSecondFps++;

        if (_ms >= 1000)

        {

            _fps = _currentSecondFps;

            _currentSecondFps = 0;

            _ms = 0;

        }

        base.Draw(gameTime);

    }

 

    protected override void DrawBasicUnit(int parameter)

    {

        if (_fps != 0)

        {

            spriteBatch.DrawStringVM(_font, _fps.ToString(), new Vector2(10, 10), Color.White);

        }

    }

}

Aspectos a comentar de este código:

  1. En el constructor asignamos un nombre único a este componente (ya que todos los componentes deben tener uno).
  2. En el Initialize() es el lugar donde podemos inicializar todo aquello que dependa de Wave. En este punto los componentes requeridos por nuestro componente (y que hemos decorado con [RequiredComponent] ya tendrán valor). En mi caso cargo la fuente de un asset que he creado
  3. Para crear el asset con la fuente he usado el Assets Exporter, y he añadido una fuente (menú Project –> Add Font). Al exportar me genera el .wpk de la fuente que debo añadir al proyecto de VS2012.
  4. En el método Draw simplemente guardo en una variable (_fps) los frames del segundo actual. Pero NO dibujo nada en la pantalla. Simplemente actualizo el valor de _fps cada segundo.
  5. En el método DrawBasicUnit es donde hay el código de dibujo: llamo al método DrawStringVM del objeto SpriteBatch que como Drawable tenemos. El método DrawStringVM básicamente dibuja un texto en la posición especificada.

Nota importante: Si redefines Draw ¡no te olvides la llamada a base.Draw o el método DrawBasicUnit no se llamará nunca!

Para usar nuestro componente debemos vincularlo a una entidad para poder añadirlo a la escena:

var fps = new Entity("fps").

    AddComponent(new FpsMeasure());

EntityManager.Add(fps);

¡Y listos! Con esto ya tenemos un contador que nos muestra los fps a los que se está ejecutando nuestro juego.

Opción 2: Usar un Drawable y un Behavior

El Drawable que hemos creado tiene cierta lógica en el método Draw: calcular los fps. Para mostrar la naturaleza modular de Wave y la interdependencia entre Componentes, vamos a mover esta lógica y colocarla en un Behavior. El Drawable quedará tan solo con el código de renderizado.

Empezamos quitando todo el código de cálculo de los frames por segundo del Drawable y dejando solo el de renderizado e inicialización de la fuente:

public class FpsMeasure : Drawable2D

{

    private SpriteFont _font;

    private static int _instances;

 

    public int FpsValue { get; set; }

 

    public FpsMeasure()

        : base("fps" + (_instances++), DefaultLayers.Opaque)

    {

    }

 

    protected override void Initialize()

    {

        base.Initialize();

        _font = Assets.LoadAsset<SpriteFont>("Content/Arial Rounded MT.wpk");

    }

 

    protected override void Dispose(bool disposing)

    {

 

    }

 

    protected override void DrawBasicUnit(int parameter)

    {

        if (FpsValue != 0)

        {

            spriteBatch.DrawStringVM(_font, FpsValue.ToString(), new Vector2(10, 10), Color.White);

        }

    }

}

Fíjate como ahora ya no hay necesidad de redefinir Draw, ya que tenemos un Drawable que básicamente lo único que hace es dibujar el valor de la propiedad FpsValue.

Ahora nos toca crear el Behavior. Así pues creamos una clase que derive de Behavior y en el método Update() ponemos el código que teníamos antes en el método Draw():

public class FpsMeasureBehavior : Behavior

{

    private int _currentSecondFps;

    private int _ms;

    private static int _instances;

 

    public FpsMeasureBehavior()

        : base("fpsBehavior" + (_instances++))

    {

    }

 

    [RequiredComponent]

    public FpsMeasure fpsDrawable;

 

    protected override void Update(TimeSpan gameTime)

    {

        _ms += gameTime.Milliseconds;

        _currentSecondFps++;

        if (_ms >= 1000)

        {

            fpsDrawable.FpsValue = _currentSecondFps;

            _currentSecondFps = 0;

            _ms = 0;

        }

    }

}

Observa el uso de [RequiredComponent] para indicar que este Behavior requiere del componente FpsMeasure (el Drawable) para su funcionamiento. Si creásemos una entidad y le añadiésemos este Behavior, pero no le añadiésemos un FpsMeasure, Wave nos daría un error:

SNAGHTMLa191587

¡Listos! Ahora ya tenemos nuestro contador de fps implementado a través de un Behavior (que proporciona la lógica) y un Drawable (que se limita a dibujar datos). Por supuesto, para que funcione hemos de crear una entidad con ambos componentes:

var fps = new Entity("fps").

    AddComponent(new FpsMeasure()).

    AddComponent(new FpsMeasureBehavior());

¿Y cual es mi opción preferida?

La segunda, sin duda alguna. Porque es mucho modular, porque separa mejor las responsabilidades y porque permite reaprovechar más los componentes. Fíjate que realmente el Drawable FpsMeasure, se comporta en realidad como una label (se limita a mostrar texto sin  importarle de donde viene este texto), así que con poco trabajo sería convertible a un componente más genérico (p. ej. TextSprite o algo así) y ser reutilizado en otros sitios.

En fin, espero que os haya resultado interesante 😉

Saludos!