Introducción a OpenTK en Xamarin.Mac

OpenTK (Open Toolkit) es una biblioteca avanzada de C# de bajo nivel que facilita el trabajo con OpenGL, OpenCL y OpenAL. OpenTK se puede usar para juegos, aplicaciones científicas u otros proyectos que requieren gráficos 3D, audio o funcionalidad computacional. En este artículo se ofrece una breve introducción al uso de OpenTK en una aplicación de Xamarin.Mac.

An example app run

En este artículo, trataremos los conceptos básicos de OpenTK en una aplicación de Xamarin.Mac. Se recomienda encarecidamente trabajar primero en el artículo Hello, Mac, específicamente en las secciones Introducción a Xcode e Interface Builder y Salidas y acciones, ya que trata conceptos clave y técnicas que usaremos en este artículo.

Es posible que quiera echar un vistazo a la sección Exponer clases o métodos de C# a Objective-C del documento sobre Aspecto internos de Xamarin.Mac, ya que explica los comandos Register y Export que se usan para conectar las clases de C# a objetos Objective-C y elementos de la interfaz de usuario.

Acerca de OpenTK

Como se indicó anteriormente, OpenTK (The Open Toolkit) es una biblioteca avanzada de C# de bajo nivel que facilita el trabajo con OpenGL, OpenCL y OpenAL. El uso de OpenTK en una aplicación de Xamarin.Mac proporciona las siguientes características:

  • Desarrollo rápido: OpenTK proporciona tipos de datos sólidos y documentación insertada para mejorar su flujo de trabajo de codificación y detectar errores más fácil y rápidamente.
  • Integración sencilla: OpenTK se diseñó para integrarse fácilmente con aplicaciones .NET.
  • Licencia permisiva: OpenTK se distribuye bajo las licencias MIT/X11 y es totalmente gratuita.
  • Enlaces ricos y con seguridad de tipos: OpenTK es compatible con las últimas versiones de OpenGL, OpenGL|ES, OpenAL y OpenCL con carga automática de extensiones, comprobación de errores y documentación insertada.
  • Opciones de GUI flexibles: OpenTK proporciona la ventana de juegos nativa y de alto rendimiento diseñada específicamente para juegos y Xamarin.Mac.
  • Código totalmente administrado compatible con CLS: OpenTK admite versiones de 32 y 64 bits de macOS sin bibliotecas no administradas.
  • Kit de herramientas 3D Math OpenTK proporciona estructuras Vector, Matrix, Quaternion y Bezier a través de su Kit de herramientas 3D Math.

OpenTK se puede usar para juegos, aplicaciones científicas u otros proyectos que requieren gráficos 3D, audio o funcionalidad computacional.

Para más información, consulte el sitio web de The Open Toolkit.

Inicio rápido de OpenTK

Como introducción rápida al uso de OpenTK en una aplicación para Xamarin.Mac, vamos a crear una aplicación sencilla que abra una vista de juego, represente un triángulo sencillo en esa vista y adjunte la vista de juego a la ventana principal de la aplicación para Mac para mostrar el triángulo al usuario.

Iniciar un nuevo proyecto

Inicie Visual Studio para Mac y cree una nueva solución de Xamarin.Mac. Seleccione Mac>App>General>Cocoa App:

Adding a new Cocoa App

Escriba MacOpenTK para el Nombre del proyecto:

Setting the project name

Haga clic en el botón Crear para compilar el nuevo proyecto.

Inclusión de OpenTK

Para poder usar Open TK en una aplicación de Xamarin.Mac, debe incluir una referencia al ensamblado de OpenTK. En el Explorador de soluciones, haga clic con el botón derecho del ratón en la carpeta Referencias y seleccione Editar referencias....

Coloque una comprobación junto a OpenTK y haga clic en el botón Aceptar:

Editing the project references

Uso de OpenTK

Con el nuevo proyecto creado, haga doble clic en el archivo MainWindow.cs del Explorador de soluciones para abrirlo y editarlo. Haga que la clase MainWindow se parezca a lo siguiente:

using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform.MacOS;
using Foundation;
using AppKit;
using CoreGraphics;

namespace MacOpenTK
{
    public partial class MainWindow : NSWindow
    {
        #region Computed Properties
        public MonoMacGameView Game { get; set; }
        #endregion

        #region Constructors
        public MainWindow (IntPtr handle) : base (handle)
        {
        }

        [Export ("initWithCoder:")]
        public MainWindow (NSCoder coder) : base (coder)
        {
        }
        #endregion

        #region Override Methods
        public override void AwakeFromNib ()
        {
            base.AwakeFromNib ();

            // Create new Game View and replace the window content with it
            Game = new MonoMacGameView(ContentView.Frame);
            ContentView = Game;
            Game.OpenGLContext.View = Game;

            // Wire-up any required Game events
            Game.Load += (sender, e) =>
            {
                // TODO: Initialize settings, load textures and sounds here
            };

            Game.Resize += (sender, e) =>
            {
                // Adjust the GL view to be the same size as the window
                GL.Viewport(0, 0, Game.Size.Width, Game.Size.Height);
            };

            Game.UpdateFrame += (sender, e) =>
            {
                // TODO: Add any game logic or physics
            };

            Game.RenderFrame += (sender, e) =>
            {
                // Setup buffer
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
                GL.MatrixMode(MatrixMode.Projection);

                // Draw a simple triangle
                GL.LoadIdentity();
                GL.Ortho(-1.0, 1.0, -1.0, 1.0, 0.0, 4.0);
                GL.Begin(BeginMode.Triangles);
                GL.Color3(Color.MidnightBlue);
                GL.Vertex2(-1.0f, 1.0f);
                GL.Color3(Color.SpringGreen);
                GL.Vertex2(0.0f, -1.0f);
                GL.Color3(Color.Ivory);
                GL.Vertex2(1.0f, 1.0f);
                GL.End();

            };

            // Run the game at 60 updates per second
            Game.Run(60.0);
        }
        #endregion
    }
}

Vamos a repasar este código en detalle a continuación.

API necesarias

Se requieren varias referencias para usar OpenTK en una clase Xamarin.Mac. Al principio de la definición hemos incluido las siguientes instrucciones using:

using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform.MacOS;
using Foundation;
using CoreGraphics;

Este conjunto mínimo será necesario para cualquier clase que use OpenTK.

Adición de la Vista de juego

A continuación, necesitamos crear una Vista de juego para que contenga toda nuestra interacción con OpenTK y mostrar los resultados. Hemos usado el código siguiente:

public MonoMacGameView Game { get; set; }
...

// Create new Game View and replace the window content with it
Game = new MonoMacGameView(ContentView.Frame);
ContentView = Game;

Aquí hemos hecho que la vista del juego sea del mismo tamaño que la ventana principal de Mac y ha reemplazado la vista de contenido de la ventana por la nueva MonoMacGameView. Como hemos reemplazado el contenido de la ventana existente, nuestra vista dada cambiará de tamaño automáticamente cuando se cambie el tamaño de la ventana principal.

Respuesta a eventos

Hay varios eventos predeterminados a los que debería responder cada Vista de juego. En esta sección se tratarán los eventos principales necesarios.

Evento Load

En el evento Load se cargan recursos del disco como imágenes, texturas o música. Para nuestra sencilla aplicación de prueba, no estamos usando el evento Load, pero lo hemos incluido como referencia:

Game.Load += (sender, e) =>
{
    // TODO: Initialize settings, load textures and sounds here
};

Evento Resize

El evento Resize debería ser llamado cada vez que se cambie el tamaño de la Vista del juego. Para nuestra aplicación de ejemplo, estamos haciendo que GL Viewport tenga el mismo tamaño que nuestra Vista de juego (que la ventana principal de Mac cambia automáticamente de tamaño) con el siguiente código:

Game.Resize += (sender, e) =>
{
    // Adjust the GL view to be the same size as the window
    GL.Viewport(0, 0, Game.Size.Width, Game.Size.Height);
};

Evento UpdateFrame

El evento UpdateFrame se usa para controlar la entrada del usuario, actualizar las posiciones de los objetos, ejecutar cálculos físicos o de inteligencia artificial. Para nuestra sencilla aplicación de prueba, no usamos el evento UpdateFrame, pero lo hemos incluido como referencia:

Game.UpdateFrame += (sender, e) =>
{
    // TODO: Add any game logic or physics
};

Importante

La implementación de Xamarin.Mac de OpenTK no incluye la Input API, por lo que tendrá que usar las API proporcionadas por Apple para agregar compatibilidad con teclado y ratón. Opcionalmente puede crear una instancia personalizada de la MonoMacGameView e invalidar los métodos KeyDown y KeyUp.

Evento RenderFrame

El evento RenderFrame contiene el código que se usa para representar (dibujar) los gráficos. Para nuestra aplicación de ejemplo, estamos rellenando la Vista de juego con un triángulo simple:

Game.RenderFrame += (sender, e) =>
{
    // Setup buffer
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
    GL.MatrixMode(MatrixMode.Projection);

    // Draw a simple triangle
    GL.LoadIdentity();
    GL.Ortho(-1.0, 1.0, -1.0, 1.0, 0.0, 4.0);
    GL.Begin(BeginMode.Triangles);
    GL.Color3(Color.MidnightBlue);
    GL.Vertex2(-1.0f, 1.0f);
    GL.Color3(Color.SpringGreen);
    GL.Vertex2(0.0f, -1.0f);
    GL.Color3(Color.Ivory);
    GL.Vertex2(1.0f, 1.0f);
    GL.End();

};

Normalmente, el código de representación comenzará con una llamada a GL.Clear para eliminar cualquier elemento existente antes de dibujar los nuevos elementos.

Importante

Para la versión Xamarin.Mac de OpenTK no llame al método SwapBuffers de su instancia MonoMacGameView al final de su código de representación. Si lo hace, la vista del juego se mostrará rápidamente en lugar de mostrar la vista representada.

Ejecutar la vista de juego

Con todos los eventos necesarios definidos y la Vista de juego adjunta a la ventana principal de Mac de nuestra aplicación, estamos listos para ejecutar la Vista de juego y mostrar nuestros gráficos. Use el código siguiente:

// Run the game at 60 updates per second
Game.Run(60.0);

Pasamos la velocidad de fotogramas deseada a la que queremos que se actualice la Vista de juego, para nuestro ejemplo hemos elegido 60 fotogramas por segundo (la misma velocidad de actualización que la TV normal).

Vamos a ejecutar nuestra aplicación y ver la salida:

A sample of the apps output

Si cambiamos el tamaño de nuestra ventana, la Vista de juego también cambiará de tamaño y el triángulo también cambiará de tamaño y se actualizará en tiempo real.

¿Y ahora qué?

Una vez hechos los conceptos básicos para trabajar con OpenTk en una aplicación Xamarin.mac, aquí tiene algunas sugerencias de lo que puede probar a continuación:

  • Pruebe a cambiar el color del triángulo y el color de fondo de la Vista de juego en los eventos Load y RenderFrame.
  • Haga que el triángulo cambie de color cuando el usuario pulse una tecla en los eventos UpdateFrame y RenderFrame o cree su propia clase MonoMacGameView personalizada e invalide los métodos KeyUp y KeyDown.
  • Haga que el triángulo se mueva por la pantalla usando las teclas de reconocimiento en el evento UpdateFrame. Sugerencia: use el método Matrix4.CreateTranslation para crear una matriz de traducción y llame al método GL.LoadMatrix para cargarla en el evento RenderFrame.
  • Use un bucle for para representar varios triángulos en el evento RenderFrame.
  • Gire la cámara para dar una vista diferente del triángulo en el espacio 3D. Sugerencia: use el método Matrix4.CreateTranslation para crear una matriz de traducción y llamar al método GL.LoadMatrix para cargarlo. También puede usar las clases Vector2, Vector3, Vector4 y Matrix4 para manipulaciones de cámara.

Para obtener más ejemplos, consulte el repositorio Ejemplos de OpenTK de GitHub. Contiene una lista oficial de ejemplos de uso de OpenTK. Tendrá que adaptar estos ejemplos para usarlos con la versión de Xamarin.Mac de OpenTK.

Para obtener un ejemplo más complejo de Xamarin.Mac de una implementación de OpenTK, consulte nuestra muestra MonoMacGameView.

Resumen

En este artículo se ha echado un vistazo rápido al trabajo con OpenTK en una aplicación Xamarin.Mac. Hemos visto cómo crear una Ventana de juego, cómo unir la Ventana de juego a una Ventana de Mac y cómo representar una forma sencilla en la Ventana de juego.