Procedural Clip

Overview

Procedural Clips are clips that can be placed in fragments and allow to execute custom code in synch with the rest of the fragment.

We have Procedural Clips that range all the way from playing a sound, controlling joints on a character, or procedurally aligning an entity to a location specified by the game.

The main interface functions that a procedural clip offers are OnEnter(blendTime, duration, params) Update(timePassed) and OnExit(blendTime).

The following diagram shows when the events are triggered in relation to the lifetime of a Procedural Clip.

   +-----+--------------------------+
   |    /|                          |\
   |   / |                          | \
   |  /  |                          |  \
   | /   |                          |   \
   |/    |                          |    \
   +-----+--------------------------+-----+
   [             Update             ]
   ^                                ^
OnEnter                           OnExit

Handling blend in or out times is responsibility of the implementation of the Procedural Clip.

Note that after a clip receives the OnExit callback it will stop receiving any Update callbacks, even if the blendTime parameter passed to OnExit is not zero.

The following is an example of a class that implements an empty Procedural Clip.

// In ProceduralClipEmpty.cpp:
struct SProceduralClipEmptyParams
: public SProceduralParams
{
float exampleParam;
};

class CProceduralClipEmpty
: public TProceduralClip<SProceduralClipEmptyParams>
{
public:

CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IProceduralClip)
CRYINTERFACE_END()

// 0xb61ead3b8e2f4763, 0xbf2d84d517f4978 is a GUID and should be generated using the CryExtension GUID macro.
CRYGENERATE_CLASS(CProceduralClipEmpty, "Empty", 0xb61ead3b8e2f4763, 0xbf2d84d517f4978)

virtual void OnEnter(float blendTime, float duration, const SProceduralClipEmptyParams& params) {}

virtual void OnExit(float blendTime) {}

virtual void Update(float timePassed) {}
};

CRYREGISTER_CLASS(CProceduralClipEmpty)

Registering a Procedural Clip so that it appears in the editor is currently done by editing the ProcDefs file located in Scripts/Mannequin/ProcDefs.xml and adding a new entry. An example entry for the previous example would look like this.

<Empty anim="false">
  <Params exampleParam="0"/>
</Empty>

NOTE As of this first release, the Procedural Clip Parameter support is limited. It has support for 2 strings, a crc value, and a series of floats. There is ongoing work to change the interface to support a more flexible parameter structure and remove the ProcDefs.xml step.

Procedural Context

Procedural Clips have a limited lifetime. By themselves they have no way to handle what to do when blending out on their own for example since they are not getting updated anymore. They also have no straightforward way to know what other Procedural Clips might be doing in other layers and resolve any conflicts between them if they should want to, or work together to combine with different Procedural Clips.

If there's no underlying system on the entity that can take care of this interactions, that's where Procedural Contexts come in handy. They provide a system the lifetime of which is tied to the ActionController and that Procedural Clips can easily communicate with.

To create a Procedural Context and ProceduralClips that can refer to that Procedural Context see the following code snippet:

// In ProceduralContextExample.h:
class CProceduralContextExample
: public IProceduralContext
{
public:
PROCEDURAL_CONTEXT(CProceduralContextExample, "ProceduralContextExample", 0xbd3b8e9b263b4768, 0x8e5444b453233fb7);

// IProceduralContext
virtual void Initialise(IEntity& entity, IActionController& actionController);
virtual void Update(float timePassedSeconds);
// \~IProceduralContext
};

...
// In ProceduralContextExample.cpp:
CRYREGISTER_CLASS(CProceduralContextExample);

void CProceduralContextExample::Initialise(IEntity& entity, IActionController& actionController)
{
IProceduralContext::Initialise(entity, actionController);
...
}

void CProceduralContextExample::Update(float timePassedSeconds)
{
...
}

...
// In ProceduralClipWithContextExample.cpp:
class CProceduralClipWithProceduralContext
: public TProceduralContextualClip<SProceduralParams, CProceduralContextExample>
{
public:

CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IProceduralClip)
CRYINTERFACE_END()

CRYGENERATE_CLASS(CProceduralClipWithProceduralContext, "ProceduralClipWithContext", 0x363d1a18481b4044, 0xa57f21a4cbcc8924)

virtual void OnEnter(float blendTime, float duration, const SProceduralParams& params){
    // The Procedural Context can be accessed through the m_context member
  }

virtual void OnExit(float blendTime) {}

virtual void Update(float timePassed) {}
};

CRYREGISTER_CLASS(CProceduralClipWithProceduralContext)