Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
English

Implementing a New Game Object Extension in C++

Using the Helper Template

The CGameObjectExtensionHelper template can be used to simplify the implementation of a GameObjectExtension. This helper template will handle the RMI functionality of the extension interface.

This template is included inside of the header file IGameObject.h. To use the helper class, the declaration needs to follow this example:

Code Block
class CNewGameObjectExt
 : public CGameObjectExtensionHelper <CNewGameObjectExt, IGameObjectExtension>
{
  //...
}
Note

A third CGameObjectExtensionHelper parameter is available, and denotes the maximum number of RMIs supported by this game object.

 

The important functions to implement the most basic game object extension are the following:

Function

Description

IGameObjectExtension::Init()

The Init function should set the member values and validate with the return value if the initialization is successful. If needed, resources should be allocated during the execution of the Init.

IGameObjectExtension::PostInit()

The ideal location to set the initial update slot state (more information can be found in IGameObject::EnableUpdateSlot()).

IGameObjectExtension::Release()

This function should release any resource previously allocated by the extension. Additionally, the Release function need to free the object instance.

IGameObjectExtension::Serialize()

Any critical state variables needs to be serialized, both for disc serialization (ie: save games) or network serialization.

IGameObjectExtension::Update()

Any frame rate dependent updates should be implemented as part of this function.

Registering the New Extension

A new GameObjectExtension should register itself in IGameFramework before it's possible to spawn new instances of its class.

The REGISTER_FACTORY macro defined inside IGameFramework.h is used to simplify the registration.

Code Block
IGameFramework *pFramework = gEnv->pGame->GetIGameFramework();
REGISTER_FACTORY(pFramework, "NewGameObject", CNewGameObjectExt, false);

The GameDLL initialization is the perfect location for the GameObjectExtension registrations.

GameObject Update Policies

CGameObject is the responsible class for enabling or disabling an entity that is executing game code. Since there are many disparate systems that may need to be updated at different times, the class employs a voting mechanism to arbitrate between things. Effectively if anything says it needs an update, the entity is activated, and those parts of the system that need it receive a tick.

For the game code, there are four update slots for each game object extension, and each can be governed by a built in rule. This rule allows the game object itself to decide activation state without having to call into external code (in an effort to avoid memory thrashing.) The rules are detailed in EUpdateEnableCondition. Part of these rules is whether the update condition depends on the AI state or not – usually it does, but for some update slots we want that it gets updated separately (for example the GameRules).

Conditions in EUpdateEnableCondition

eUEC_Never

eUEC_Always

eUEC_Visible

eUEC_InRange

eUEC_VisibleAndInRange

eUEC_VisibleOrInRange

eUEC_VisibleOrInRangeIgnoreAI

eUEC_VisibleIgnoreAI

eUEC_WithoutAI

To enable or disable AI, we have a separate rule that gets configured on the CGameObject. The EGameObjectAIActivationMode is available and set from the AIProxy. It has three values (Never, Always, and VisibleOrInRange – the default.) Game Object uses this value to decide whether the AI should be activated or not. It is important to note that whenever the entity is activated the AI is informed however, so that it can maintain state synchronization with the entity.

Finally, there's a third configurable rule for when the pre-physics update event should be sent to an entity, EPrePhysicsUpdate. We use this pre-physics call to perform some work in AnimatedCharacter.

To see how this system is working, you can always use g_showUpdateState 2. It's showing all of the details about why a particular entity has been enabled.
In order to do the visibility and range checks to evaluate the update rules, the following is done:

  • For Visibility: when an entity is invisible, it registers for an event from the Renderer that it has become visible again, at which point it 'becomes visible' and update rules are re-evaluated. When it is visible, in order to avoid too much entity system event spam, the entity only periodically registers for this event, so an entity may take 5 or 10 seconds to become invisible once you look away.
  • For Distance checking: there is a moderately large proximity trigger located around the player. This trigger is snapped to two meter grid coordinates and we try to move it around only rarely (once every second currently) in order to prevent too much thrashing in the proximity trigger system. As entities move in and out of this region they are updated as being 'faraway' or 'close.' The proximity trigger is needed in order to activate entities immediately when they come into range – at the time I wrote it it was the only way this was possible to do this efficiently, and I suspect it still is.

There is a small table-driven state machine driving this process in CGameObject::UpdateTransitions.

 

Chinese

在 C++ 中实现新的游戏对象扩展

使用助手模板

可以使用 CGameObjectExtensionHelper 模板简化 GameObjectExtension 的实现。该助手模板将处理扩展界面的 RMI 函数。

IGameObject.h. 头文件包含该模板。若要使用助手类,需按以下示例进行声明:

Code Block
class CNewGameObjectExt
 : public CGameObjectExtensionHelper <CNewGameObjectExt, IGameObjectExtension>
{
  //...
}

 

以下所示的是实现最基本的游戏对象扩展的重要函数:

函数描述
IGameObjectExtension::Init()若初始化成功,初始化函数应设置成员值并用返回值进行验证。若需要,应在执行初始化期间对资源进行分配。
IGameObjectExtension::PostInit()设置初始更新槽状态的理想位置\(更多信息,请参阅 IGameObject::EnableUpdateSlot()\)。
IGameObjectExtension::Release()该函数应释放扩展先前分配的的任意资源。此外,释放函数需释放对象实例。
IGameObjectExtension::Serialize()需要使任意临界状态的变量序列化,光盘序列化\(例如:保存游戏\)或网络序列化。
IGameObjectExtension::Update()任意帧率相关更新应作为该函数的一部分来实现。

注册新的扩展

在该类的新实例可能出生之前,应在 IGameFramework 中注册新的 GameObjectExtension 本身。

IGameFramework.h 中所定义的 REGISTER_FACTORY 宏可用于简化该注册。

Code Block
IGameFramework *pFramework = gEnv->pGame->GetIGameFramework();
REGISTER_FACTORY(pFramework, "NewGameObject", CNewGameObjectExt, false);
 

GameDLL 初始化是注册 GameObjectExtension 的极佳位置。

游戏对象更新策略

CGameObject 是负责启用或禁用实体的类,该实体执行游戏代码。由于不同的系统可能需要在不同的时间进行更新,该类采用表决机制以仲裁要启用或禁用的实体。若需要更新任意实体,将有效激活实体,并且系统的这些部分需要该系统接收标记。

对于游戏代码,每个游戏对象扩展有四个更新槽,并且每个游戏对象扩展都由内置规则控制。这个规则能够让游戏对象本身在未调用外部代码的情况下决定激活状态(旨在避免内存抖动。)EUpdateEnableCondition 中有关于该规则的详述。这些规则的一部分是更新条件是否取决于 AI 状态 – 通常情况下是肯定的,但对于一些更新槽而言我们所期望的是其能够单独进行更新(例如 GameRules)。

EUpdateEnableCondition 中的条件
eUEC_Never
eUEC_Always
eUEC_Visible
eUEC_InRange
eUEC_VisibleAndInRange
eUEC_VisibleOrInRange
eUEC_VisibleOrInRangeIgnoreAI
eUEC_VisibleIgnoreAI
eUEC_WithoutAI

若要启用或禁用 AI,我们可以使用单独的规则在 CGameObject 上进行配置。也可以使用从 AIProxy 上对其进行设置的 EGameObjectAIActivationMode。它有三种值(从不、总是和默认的 VisibleOrInRange。)游戏对象根据这个值来决定是否激活 AI。需要重点注意的是无论何时激活实体,都会通知 AI,因此 AI 能够与该实体保持状态同步。

最后,发送准物理更新事件至实体、EPrePhysicsUpdate 时有第三个可配置规则。我们可以使用这个准物理调用以在 AnimatedCharacter 中执行一些工作。

若要了解该系统如何运行,您可以总是使用 g_showUpdateState 2。它显示了启用特定实体的所有细节。
若要进行可见性和范围检查以评估更新规则,可按照以下步骤来完成: