Xamarin.Mac 中的 OpenTK 简介

OpenTK (Open Toolkit) 是高级的低级别 C# 库,可更轻松地使用 OpenGL、OpenCL 和 OpenAL。 OpenTK 可用于需要 3D 图形、音频或计算功能的游戏、科学应用程序或其他项目。 本文将简要介绍如何在 Xamarin.Mac 应用中使用 OpenTK。

示例应用运行

本文将介绍在 Xamarin.Mac 应用程序中使用 OpenTK 的基础知识。 强烈建议先阅读了解 Mac 一文,特别是 Xcode 和 Interface Builder 简介输出口和操作部分,因为其中介绍了我们将在本文中使用的关键概念和技术。

你可能还需要查看 Xamarin.Mac 内部机制文档的向 Objective-C 公开 C# 类/方法部分,因为其中介绍了用于将 C# 类连接到 Objective-C 对象和 UI 元素的 RegisterExport 命令。

关于 OpenTK

如上所述,OpenTK (Open Toolkit) 是高级的低级别 C# 库,可更轻松地使用 OpenGL、OpenCL 和 OpenAL。 在 Xamarin.Mac 应用中使用 OpenTK 可提供以下功能:

  • 快速开发 - OpenTK 提供强大的数据类型和内联文档,有助于改进编码工作流并更轻松、更快地捕获错误。
  • 轻松集成 - OpenTK 旨在轻松与 .NET 应用程序集成。
  • 宽松式许可证 - OpenTK 根据 MIT/X11 许可证进行分发,并且完全免费。
  • 丰富的类型安全绑定 - OpenTK 支持最新版本的 OpenGL、OpenGL|ES、OpenAL 和 OpenCL 以及自动扩展加载、错误检查和内联文档。
  • 灵活的 GUI 选项 - OpenTK 提供专为游戏和 Xamarin.Mac 设计的本机高性能游戏窗口。
  • 完全托管、符合 CLS 的代码 - OpenTK 支持 32 位和 64 位版本的 macOS,没有非托管库。
  • 3D 数学工具包 - OpenTK 通过其 3D 数学工具包提供 VectorMatrixQuaternionBezier 结构。

OpenTK 可用于需要 3D 图形、音频或计算功能的游戏、科学应用程序或其他项目。

有关详细信息,请参阅 Open Toolkit 网站。

OpenTK 快速入门

为简要介绍如何在 Xamarin.Mac 应用中使用 OpenTK,我们将创建一个简单的应用程序,可在其中打开“游戏视图”,在该视图中渲染一个简单的三角形,并将“游戏视图”附加到 Mac 应用的“主窗口”以向用户显示该三角形。

启动一个新项目

启动 Visual Studio for Mac 并创建新的 Xamarin.Mac 解决方案。 选择 Mac >“应用”>“常规”>“Cocoa 应用”

添加新的 Cocoa 应用

在“项目名称”中输入 MacOpenTK

设置项目名称

单击“创建”按钮以生成新项目

包括 OpenTK

必须在 Xamarin.Mac 应用程序中包括对 OpenTK 程序集的引用,才能在其中使用 Open TK。 在 Solution Pad 中,右键单击 References 文件夹,然后选择“编辑首选项...”

通过 OpenTK 进行检查,然后单击“确定”按钮

编辑项目引用

使用 OpenTK

创建新项目后,在“解决方案资源管理器”中,双击打开 MainWindow.cs 文件进行编辑。 使 MainWindow 类如下所示:

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
    }
}

下面让我们详细讨论一下这段代码。

必需的 API

在 Xamarin.Mac 类中使用 OpenTK 需要几个引用。 在定义的开头部分,我们已包含以下 using 语句:

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

使用 OpenTK 的任何类都需要此最小集。

添加游戏视图

接下来,我们需要创建一个游戏视图,以包含与 OpenTK 的所有交互并显示结果。 我们采用的代码如下:

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

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

在这里,我们使游戏视图的大小与 Mac 主窗口相同,并将窗口的内容视图替换为了新的 MonoMacGameView。 由于我们替换了现有窗口内容,因此在调整主窗口大小时,将自动调整游戏视图的大小。

对事件作出响应

每个游戏视图应响应多个默认事件。 本部分将介绍所需的主要事件。

Load 事件

Load 事件是从磁盘(如图像、纹理或音乐)加载资源的位置。 在简单的测试应用中,我们不会使用 Load 事件,但已包含它以供参考:

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

Resize 事件

每次调整游戏视图大小时都应调用 Resize 事件。 在示例应用中,我们使用以下代码将 GL 视区的大小设置为与游戏视图相同的大小(由 Mac 主窗口自动调整大小):

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);
};

UpdateFrame 事件

UpdateFrame 事件用于处理用户输入、更新对象位置、运行物理或 AI 计算。 在简单的测试应用中,我们不会使用 UpdateFrame 事件,但已包含它以供参考:

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

重要

OpenTK 的 Xamarin.Mac 实现不包括 Input API,因此需要使用 Apple 提供的 API 添加键盘和鼠标支持。 (可选)可以创建 MonoMacGameView 的自定义实例,并替代 KeyDownKeyUp 方法。

RenderFrame 事件

RenderFrame 事件包含用于渲染(绘制)图形的代码。 在示例应用中,我们将用一个简单的三角形填充游戏视图:

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();

};

通常,渲染代码将调用 GL.Clear 以在绘制新元素之前删除任何现有元素。

重要

对于 OpenTK 的 Xamarin.Mac 版本,请勿在渲染代码末尾调用 MonoMacGameView 实例的 SwapBuffers 方法。 否则将导致游戏视图快速闪烁,而不是显示渲染的视图。

运行游戏视图

定义了所有必需的事件并将游戏视图附加到应用的 Mac 主窗口后,接下来即可运行游戏视图并显示图形。 使用以下代码:

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

我们传入游戏视图更新时所需的帧速率,在我们的示例中,我们选择了每秒 60 帧(与普通电视的刷新率相同)。

运行应用并查看输出:

应用输出示例

如果我们调整窗口大小,游戏视图也将驻留,三角形也将进行大小调整并实时更新。

接下来怎么做?

完成在 Xamarin.mac 应用程序中使用 OpenTk 的基础知识后,接下来可以尝试进行以下操作:

  • 尝试在 LoadRenderFrame 事件中更改三角形的颜色和游戏视图的背景色。
  • 使三角形在用户在 UpdateFrameRenderFrame 事件中按某个键或创建自己的自定义 MonoMacGameView 类并重写 KeyUpKeyDown 方法时能够改变颜色。
  • 使用 UpdateFrame 事件中的感知键在屏幕上移动三角形。 提示:使用 Matrix4.CreateTranslation 方法创建平移矩阵,并调用 GL.LoadMatrix 方法将其加载到 RenderFrame 事件中。
  • 使用 for 循环在 RenderFrame 事件中渲染多个三角形。
  • 旋转相机以在 3D 空间中提供三角形的不同视图。 提示:使用 Matrix4.CreateTranslation 方法创建平移矩阵并调用 GL.LoadMatrix 方法加载它。 还可以将 Vector2Vector3Vector4Matrix4 类用于相机操作。

有关更多示例,请参阅 OpenTK 示例 GitHub 存储库。 它包含使用 OpenTK 的示例的官方列表。 必须改编这些示例,以便与 Xamarin.Mac 版本的 OpenTK 一起使用。

总结

本文简要介绍了如何在 Xamarin.Mac 应用程序中使用 OpenTK。 我们了解了如何创建游戏窗口、如何将游戏窗口附加到 Mac 窗口以及如何在游戏窗口中渲染简单形状。