Wave Engine ya está disponible

Este jueves por fin lanzamos WaveEngine (waveengine.net) tras más de dos años de desarrollo. Creo que es un proyecto muy interesante para desarrolladores C# por lo que os animo a todos a probarlo y darnos feedback para continuar mejorándolo.

A continuación para motivaros a probarlo os voy a contar rápidamente algunas API highlights del proyecto, las cuales desde mi punto de vista son bastantes atractivas e interesantes para desarrollados en C#.

WaveEngine ha sido diseñado bajo los principios de Component Based Game Engine (el primer artículo que yo leí sobre esta arquitectura fue este): existe el concepto de scene, donde se registran las Entities y estás a su vez están formadas por Components, puede observarse esto en la inicialización de una entidad, lo cual hace muy fácil la integración de dicha API con un editor.

Entity primitive = new Entity("Primitive")

    .AddComponent(new Spinner() { AxisTotalIncreases = new Vector3(1f, 2f, 1f) })

    .AddComponent(new Transform3D())

    .AddComponent(Model.CreateSphere())

    .AddComponent(new BoxCollider())

    .AddComponent(new ChangeModel())

    .AddComponent(new MaterialsMap())

    .AddComponent(new ModelRenderer());

 

EntityManager.Add(primitive);

 

Además puede apreciarse la aplicación del concepto Fluent Interface por toda la API para facilitar la legibilidad y productividad del código del usuario.

Una vez construida una entidad se ha aprovechado el uso de Generics para facilitar el acceso a alguna de las partes de una entidad:

var boxCollider = primitive.FindComponent<BoxCollider>();

 

Sin embargo para entidades prefabricadas (Camaras, Controles de UI, etc) en el motor, esta arquitectura presenta serios problemas para el usuario, ya que es prácticamente necesario conocer los componentes que conforma una entidad para poder editar cualquier característica de ellos.

primitive.FindComponent<Spinner>().IncreaseY = 0;

 

Por ello descartamos el uso de factorías que construían las entidades prefabricadas fácilmente pero seguía manteniendo el problema para el usuario de tener que conocer su construcción interna para poder modificar cualquier cosa.

La solución llegó tras la introducción del Decorator Pattern, el cual permite mantener los principios de la arquitectura (Component Based Game Engine) y aportar una API común para las entidades prefabricadas o de terceros, algunos ejemplos:

Button b_reset = new Button()

{

    Text = "Reset",

    Margin = new Thickness(10, 0, 0, 20),

    VerticalAlignment = VerticalAlignment.Bottom,

    Foreground = Color.White,

    BackgroundColor = lightColor,

};

 

b_reset.Click += (s, o) =>

{

    radio4.IsChecked = true;

    toggleTexture.IsOn = true;

    sliderScale.Value = 50;

    sliderRotX.Value = 0;

    sliderRotY.Value = 0;

};

 

El decorador permite que el usuario no tenga porqué conocer la construcción interna de un botón de UI:

this.entity = new Entity(name)

.AddComponent(new Transform2D())

     .AddComponent(new RectangleCollider())

       .AddComponent(new TouchGestures())

      .AddComponent(new ButtonBehavior())

       .AddComponent(new PanelControl(DefaultWidth, DefaultHeight))

       .AddComponent(new PanelControlRenderer())

       .AddComponent(new BorderRenderer())

       .AddChild(new Entity("TextEntity")

           .AddComponent(new Transform2D()

           {

               DrawOrder = 0.4f

           })

           .AddComponent(new AnimationUI())

           .AddComponent(new TextControl()

           {

                   Text = "Button",

                   Margin = this.defaultMargin,

                   HorizontalAlignment = HorizontalAlignment.Center,

                   VerticalAlignment = VerticalAlignment.Center

           })

           .AddComponent(new TextControlRenderer()));

 

A la vez que le ofrece una API sencilla a la hora de modificar cualquiera de sus propiedades.

Por otra parte internamente estos componentes son elementos de software aislados respetando así una de las máximas de los principios SOLID, bajo acoplamiento y alta cohesión.

Aunque en ellos se permite el cacheo de otros componentes (por motivos de eficiencia) de la misma o de otras entidades las cuales necesitan para funcionar, por ejemplo el ModelRenderer necesita un Transform3D, un Model y un MaterialMap para poder funcionar. Para facilitar esta tarea se ha hecho uso de Custom Attributes y de Reflexión:

/// <summary>

/// <see cref="Model"/> to render.

/// </summary>

[RequiredComponent]

public Model Model;

 

/// <summary>

/// Materials used rendering the <see cref="Model"/>.

/// </summary>

[RequiredComponent]

public MaterialsMap MaterialMap;

 

/// <summary>

/// Transform of the <see cref="Model"/>.

/// </summary>

[RequiredComponent]

public Transform3D Transform;

Este código extraído directamente de la clase MeshRenderer es todo lo que se necesita para que este componente pueda empezar a leer propiedades de otros componentes en la entidad en la que se añada.

Además puede añadirse el parámetro opcional de RequiredComponent(false) para especificar si el componente que debe buscar es un Exact Type o un Given Type.

Cabe destacar que también es posible realizar esta conexión manualmente a través de los métodos ResolveDependencies y DeleteDependencies de un custom component. Además estas conexiones pueden actualizarse dinámicamente durante la ejecución del programa, es decir añadir o sustituir componentes de una entidad. Tras hacer las modificaciones precisas es necesario llamar al método RefreshDependencies de la entidad para que todos sus componentes se desconecten de los antiguos y se conecten a los nuevos automáticamente.

Por último hacer hincapié en la clase estática WaveServices, la cual facilita el acceso a todos los servicios del motor sin tener que recordar sus nombres:

– AssetsContainer, GraphicsDevice, InAppPurchases, Input, Layout, Microphone, MusicPlayer, Platform, Random, ScreenLayers, SoundPlayer, Storage, TaskScheduler, TimerFactory, TouchPanel,VideoPlayer

 

Si todo esto te ha resultado interesante te animo a probar la API, en ella podrás encontrar muchas más piezas interesante de software.

 

Te esperamos en la sección de Community en waveengine.net.