Version:

第12章 Gem是什么

第12章 Gem是什么

介绍

本章包括以下主题:

  • 什么是 Gem?
  • 创建新 Gem
  • Gem 的结构

开发 Gem 系统是为了便于在项目之间共享代码。Gem 是模块代码和/或资产的可重用包,可以轻松地添加到 O3DE 项目中或从中删除。

O3DE 引擎是模块化的。这是通过使用 Gem 来实现的。Gem 是代码和资产的集合(或仅是没有任何本机 C++ 代码的资产)。Gem 系统的目的是让开发人员有一个 Gem 集合可供选择,并且能够编写自己的 Gem 以供他们在项目中使用的 Gem。Gem 是包含资产的独立代码段,可以在多个项目中重复使用。

例如,如果要编写一个 Gem 来定位在启动时定位游戏窗口,那么您可以在不同的项目中重复使用此功能,而无需复制粘贴代码。事实上,第 13 章 Gem:设置窗口位置 将编写这样一个 gem!

创建新 Gem

注意:
本章的代码可以在 GitHub 上找到,网址为: https://github.com/AMZN-Olex/O3DEBookCode2111/tree/ch12_new_gem

O3DE 有很多宝藏。它们都位于您的 O3DE 安装路径或 GitHub 克隆下。这是我的安装位置:C:\O3DE\21.11.2\Gems。例如,有 EMotionFX、Multiplayer、NvCloth gems 和许多其他 gems。这些是核心 O3DE gem。如果你要编写自己的 gem,我建议把它放在你的项目旁边。到目前为止,我们已经在 C:\git\book\MyProject 创建了一个项目。

一个不错的选择是将我的 Gems 放在 C:\git\book\Gems 下。例如,在本章中,我将创建一个名为 MyGem 的 Gem,并将其放置在 C:\git\book\Gems\MyGem 中。宝石的具体位置完全取决于您。我将向您展示如何配置您的项目,以便从系统上的任何位置查看 gem。

注意:
有关创建新 Gem 的官方 O3DE 文档,请访问: https://www.o3de.org/docs/user-guide/programming/gems/

让我们创建一个全新的 gem,看看我们从盒子里得到了什么以及它的整体结构。要创建新的 Gem,我将使用 scripts\o3de.bat 的命令行界面:

C:\O3DE\21.11.2\scripts\o3de.bat create-gem -gp C:\git\book\Gems\MyGem
提示:

该脚本足够智能,可以确定游戏的名称应该是 MyGem。但是,您也可以自行指定名称。使用 -h switch 运行命令以查看所有可用选项。

C:\O3DE\21.11.2\scripts\o3de.bat create-gem -h

Gem 名称可以选择使用 -gn 指定。

将 Gem 添加到项目中

您可以使用 O3DE Project Manager 添加和删除 Gem。您可以在 O3DE 安装中找到它,例如:C:\O3DE\21.11.2\bin\Windows\profile\Default\o3de.exe。有关使用 O3DE 用户界面添加和删除 Gem 的官方指南,请访问: https://www.o3de.org/docs/user-guide/project-config/add-remove-gems/

但是,了解幕后发生的事情是有用的。需要修改两个配置文件才能将新 Gem 添加到项目中。

  1. 第一个文件是位于 C:\git\book\MyProject\project.json 的项目配置文件。

   {
     "project_name": "MyProject",
     "origin": "...",
     "license": "...",
     "display_name": "MyProject",
     "summary": "A short description of MyProject.",
     "canonical_tags": [
        "Project"
     ],
     "user_tags": [
        "MyProject"
     ],
     "icon_path": "preview.png",
     "engine": "o3de-sdk",
        "external_subdirectories": [
     ]
   }

Property 数组external_subdirectories负责使 Gem 对项目可见。以下示例将 MyGem 的路径添加到项目中。

   "external_subdirectories": [
      "C:/git/book/Gems/MyGem"
   ]

现在,如果您在 Visual Studio 中构建ZERO_TARGET,此 Gem 将显示在解决方案中。

注意:
这暂时不会启用 gem!现在,项目可以看到 Gem 并将构建,但还剩下一个步骤。
  1. 第二个文件在项目文件中按其名称启用 Gem: C:\git\book\MyProject\Code\enabled_gems.cmake。
   set(ENABLED_GEMS
     MyProject
     ...
     MyGem       # new
   )

MyGem 这个名字从何而来?它来自 Gem 的 json 配置文件“gem_name”属性,位于 C:\git\book\Gems\MyGem\gem.json。

   {
     "gem_name": "MyGem",
     ...
   }

通过这些更改,新 Gem 将作为项目的一部分进行编译,并将在运行时启用。

提示:
您可以通过在 MyGemSystemComponent::Activate 的 C:\git\book\Gems\MyGem\Code\Source\MyGemSystemComponent.cpp 中放置一个断点来确认 Gem 已启用。如果启动 Editor 并命中断点,则表示已成功启用 Gem。

项目在何处列出它使用的 Gem?

我们项目启用的 Gem 的完整列表位于 C:\git\book\MyProject\Code\en abled_gems.cmake 中。

例 12.1.enabled_gems.cmake

set(ENABLED_GEMS
  MyProject
  Atom
  AudioSystem
  AWSCore
  CameraFramework
  DebugDraw
  EditorPythonBindings
  EMotionFX
  GameState
  ImGui
  LandscapeCanvas
  LyShine
  Multiplayer
  PhysX
  PrimitiveAssets
  SaveData
  ScriptCanvasPhysics
  ScriptEvents
  StartingPointInput
  TextureAtlas
  WhiteBox
  MyGem       # new
)

这些是当您使用默认设置创建项目时启用的默认 Gem。MyProject Gem 是项目 Gem,其中包括位于 C:\git\book\MyProject\Code 中的项目代码。然后是各种核心 O3DE gem 的列表。在本章中,我们只在最后一行添加了 MyGem。

Gem 的文件结构

让我们回顾一下我们在 C:\git\book\Gems\MyGem 中创建的 Gem 的文件结构。

  • Assets 文件夹
    • Gem 可以为您的项目提供资源。如果有的话,你可以把它们放在这里。这些资源可以是脚本、预制件和其他类型的资源。由于这是一个新的 Gem,因此此文件夹为空。
  • Code 文件夹
    • 这是 Gem 的 C++ 源代码所在的位置。我们稍后将详细介绍。
  • preview.png icon 文件
    • 这是 Gem 的默认图标。此图标是 O3DE Project Manager 显示在 Gem 旁边的图标。您可以更改它并根据自己的喜好对其进行自定义。
  • CMakeLists.txt 是 Gem 的入口构建文件。我们不需要担心。Gem 的重要构建文件位于 Code 文件夹下的 C:\git\book\Gems\MyGem\Code\CMakeLists.txt。
  • gem.json 配置文件。对我们来说,这个文件唯一重要的就是 gem_name 属性下的 gem 名称。

Gem 的 C++ 代码

图 12.1.构建 Gem 的结构

概括地说,Gem 由两个基本构建目标组成:

  1. MyGem.Static 是包含组件的静态库。

  2. MyGem 模块,链接 MyGem.Static 库在 Windows 上创建 C:\git\book\build\bin\profile\MyGem.dll。如果项目启用了此 Gem,则此二进制文件由 Editor、Asset Processor 和游戏启动器加载。

    注意:
    这是 Gem 唯一必需的构建目标。您实际上可以将静态库合并到 gem 模块中,但将组件放入静态库非常有用,因为它也将用于其他一些构建目标。

  3. (可选)MyGem.Tests 模块链接到 MyGem.Static,并包含您可能添加的任何单元测试。

  4. (可选)MyGem.Editor.Static、MyGem.Editor.Tests 和 MyGem.Editor 是可选的 如果要构建特定于 Editor 的设计逻辑。本章不会在这里介绍它们,因为它们不是开始使用 O3DE 编写游戏的必要条件。

    图 12.2.Gem 代码结构

让我们更深入地研究一下 C:\git\book\Gems\MyGem\Code 中的 scripts/o3de.bat 默认为我们创建的 Gem 的代码结构。

  • Include\MyGem 是 Gem 的公共 C++ API 的位置,该 API 应主要由头文件组成,例如事件总线头文件和简单的平面数据结构。
  • Platform文件夹包含适用于特定平台 (如 Windows、Linux 等) 的各种构建和源文件。
  • Source 文件夹是内部 Gem 代码 (例如自定义组件) 的位置。
  • Tests 文件夹包含基本单元测试,供您开始测试 Gem 的代码。
  • CMakeLists.txt 是 Gem 的主构建文件。它定义了所有构建目标,例如 Gem 的静态库、Gem 的主模块、单元测试构建目标等。
  • mygem_editor_files.cmake、mygem_editor_shared_files.cmake mygem_editor_tests_files.cmake 是特定于 Editor 的构建目标的文件列表,我不会在本章中介绍。
注意:
以下是有关 Editor 特定目标的快速摘要。O3DE 允许您将特定于编辑器的逻辑与特定于游戏运行时的代码分开。例如,您可能希望在 Editor 中创建一些设计工具,以保存其输出以供在游戏中使用。在这种情况下,您可能决定将该设计代码与游戏代码分开。然后,设计组件将进入 Editor 构建目标,而最终游戏组件将进入常规组件。但是,这是可选的。首先了解常规非 Editor 构建目标的工作原理非常重要。
  • mygem_files.cmake 列出了要进入静态库 MyGem.Static 的文件。
  set(FILES
    Include/MyGem/MyGemBus.h
    Source/MyGemModuleInterface.h
    Source/MyGemSystemComponent.cpp
    Source/MyGemSystemComponent.h
  )
注意:
这将列出头文件和源文件。在 Visual Studio 中,它会自动将它们放入不同的过滤器中。
  • mygem_shared_files.cmake 列出了特定于构建 MyGem 共享库的文件。
  set(FILES
    Source/MyGemModule.cpp
  )

正如预期的那样,它只包含模块源文件。所有其他组件类将来自静态库 MyGem.Static。

  • mygem_tests_files.cmake 是单元测试文件。
  set(FILES
    Tests/MyGemTest.cpp
  )
提示:
如果您只添加新组件,则只需担心更新 mygem_files.cmake 以添加新组件文件。其他文件的内容很少会更改

Gem 的公共接口

Project Manager 脚本在 C:\git\book\Gems\MyGem\Code\Include\MyGem\MyGemBus.h 中为我们生成了一个带有 AZ::Interface 的空事件总线。它是 Gem 的公共接口的起点。

注意:
AZ::Interface 在第 6 章 什么是 AZ::Interface 中介绍?事件总线是 O3DE 中的唯一系统,有关详细信息,请参阅第 8 章 什么是 AZ::EBus?

您可以在 Gem 中包含任意数量的事件总线。您可以重命名 MyGemBus.h 或将其完全删除。

例 12.2. MyGemBus.h

#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
namespace MyGem
{
  class MyGemRequests
  {
  public:
    AZ_RTTI(MyGemRequests, "{45ec313a-31a7-41ca-9e19-0f3f9351e328}");
    virtual ~MyGemRequests() = default;
    // Put your public methods here
  };
  class MyGemBusTraits
  :  public AZ::EBusTraits
  {
  public:
    // EBusTraits overrides
    static constexpr AZ::EBusHandlerPolicy HandlerPolicy =
    AZ::EBusHandlerPolicy::Single;
    static constexpr AZ::EBusAddressPolicy AddressPolicy =
    AZ::EBusAddressPolicy::Single;
  };
  using MyGemRequestBus = AZ::EBus<MyGemRequests, MyGemBusTraits>;
  using MyGemInterface = AZ::Interface<MyGemRequests>;
} // namespace MyGem

内部 Gem 代码

Gem 的所有内部实现都应该放在 MyGem\Code\Source 下。以下是来自 Gem 模板的文件。

PS C:\git\book\Gems\MyGem\Code\Source> ls -Name
MyGemEditorModule.cpp
MyGemEditorSystemComponent.cpp
MyGemEditorSystemComponent.h
MyGemModule.cpp
MyGemModuleInterface.h
MyGemSystemComponent.cpp
MyGemSystemComponent.h

MyGemEditorModule.cpp、MyGemEditorSystemComponent.cpp 和 MyGemEditorSystemComponent.h 是开始仅为 Editor 模式构建自定义代码的基础知识,出于空间或安全考虑,您不希望将其暴露给游戏运行时。

MyGemModule.cpp 是声明 Gem 的主类。目前,它只注册一个系统组件。

MyGemModuleInterface.h 是 Gem 的 Editor 和 Game 风格的通用基础。它列出了当前向引擎注册的组件。

例 12.3. MyGemModuleInterface

MyGemModuleInterface()
{
  m_descriptors.insert(m_descriptors.end(), {
    MyGemSystemComponent::CreateDescriptor(),

MyGemSystemComponent 是为您生成的一个空系统组件。Gem 不必具有 system 组件。如果您愿意,您可以删除。

注意:
系统组件是在初始化引擎时激活的特殊组件。它不依赖于任何级别,并且会一直持续到游戏启动器或 Editor 退出。它附加到唯一的系统实体。系统组件非常适合处理不依赖于级别的系统范围服务。

如果您重命名其中任何文件或添加新文件,请务必更新 mygem_files.cmake。

图 12.3. mygem_files.cmake

set(FILES
  Include/MyGem/MyGemBus.h
  Source/MyGemModuleInterface.h
  Source/MyGemSystemComponent.cpp
  Source/MyGemSystemComponent.h
)

小结

我们已经介绍了 Gem 系统的基础知识,以及当 O3DE Project Manager 脚本为我们创建一个新 Gem 时我们得到什么。在后面的章节中,我们将看一些 gem 的例子。这将使我们能够了解 Gem 的工作原理,以及如何使用它们来帮助您构建游戏代码。

注意:
您可以在 GitHub 上找到本章的代码: https://github.com/AMZN-Olex/O3DEBookCode2111/tree/ch12_new_gem