Gameplay Camera System - Clarification on Base, Global, and Visual persistent rigs

Hello!

We’re currently on UE5.6 and we’re using the gameplay camera system plugin. While we know it’s experimental, and that it’s in flux, the shift from 5.5 to 5.6 was quite big. As a result, the previous working version we had does not work anymore. We’ve been able to resolve some issues, but one of the main ones is getting our global and base rig running properly.

Currently, we’ve found that our camera is stuck in the center of the pawn. This isn’t correct, as in the screenshot, you can see there’s boom arm processes and offsets that make it so it should be moved but it isn’t. (The screenshot is our current camera debug stack)

I’m wondering if we could get some insight in to how we’re using the plugin, and what the purpose of the base, global, and visual persistent rigs are meant to do. For example, is the base and global not meant to do any camera movement logic? Are they not meant to blend or use the active contexts evaluation result and then additively update the context based on the rig nodes?

Or perhaps we were using the system in a way that worked for 5.5 but does not for 5.6. Are we meant to add basic transitions for the base and global rigs to ensure the evaluation context carries over?

In some recent tests, I placed a log at the end of FBoomArmCameraNodeEvaluator::OnRun to log the final location of the FCameraNodeEvaluationResult. And then at the beginning of FCollisionPushCameraNodeEvaluator::OnRun. I confirmed the values aren’t the same, despite the collision push node being next in the chain. I suspect the added FPopBlendCameraNode is overriding it incorrectly. As this node wasn’t present between the main rig and the global rig in our 5.5 version.

Kind regards,

Darien

Hi! Thanks for checking out the Gameplay Cameras plugin.

For example, is the base and global not meant to do any camera movement logic? Are they not meant to blend or use the active contexts evaluation result and then additively update the context based on the rig nodes?

So yes, all the evaluation layers are meant to do camera movement logic, but you have a problem here (that actually should have been the same in 5.5, I’m surprised it worked back then) : the global layer is effectively erasing all the work done by the lower layers. Whatever the base and main layers did, the global layer is currently throwing that all away and attaching the camera to the pawn (probably at the origin, between its feet if it’s a humanoid character).

The evaluation layers simply run one after the other. First the base one, then the main one, then the global and visual ones. The base, global, and visual layers are “persistent additive blend stacks”, that is, they are blend stacks where each camera rig runs its logic using the output of the previous camera rig. By contrast, the main layer is a “transient isolated blend stack”, where each camera rig runs in isolation and then their results are blended together.

Generally speaking:

  • The base layer is meant to setup some defaults for the game, such as the default FOV/focal-length, default post-FX, clipping planes, etc.
  • Then the output of that is fed into the main layer. So each “camera mode” in your game gets these default values without having to specify them.
  • Each camera rig running in the blend stack starts with the output of the base stack, and then does its work (attach, boom, offset, etc.)
  • If we are currently blending between 2 “camera modes”, then there are 2 camera rigs in the main layer’s blend stack, and their outputs are blended together to make up the final output of the main layer.
  • Then the global layer runs with the main layer’s output. So again if we were blending between 2 camera rigs, the global layer runs with that blended result.
  • Generally, the global layer is for post-blend stuff (by definition) such as collision, occlusion, ray-casts, and so on.
  • The visual layer is technically redundant with the global layer (it runs camera rigs additively on top of the already additively-run rigs in the global layer) but I chose to add that one to make it easier to separate between “gameplay-affecting camera behaviour” and “purely visual camera behaviour”. So the visual layer typically runs stuff like camera shakes.

You can add stuff like attaching the camera to the pawn inside the base layer if you want, but I usually completely skip it because if your GameplayCameraComponent is inside your pawn, it already provides a starting evaluation context where the camera is located where the owning actor is. You may want an attach node however if, say, you want to attach to a specific socket or bone, rather than the default actor location.

The best example use of the plugin at the moment is the Game Animation Sample Project, which is available on FAB. More info here: https://dev.epicgames.com/documentation/en\-us/unreal\-engine/game\-animation\-sample\-project\-in\-unreal\-engine

> the global rig should in theory have the context of the final offset if it is not adjusted within the global rig right?. Ex: Attaching to the pawn.

I’m not sure what you mean by “context of the final offset”… but if your main layer rigs somehow take the camera and move it 500 units behind the character’s neck, then the global layer rigs will start from there. If you had only one global layer rig and all it did was move the camera forwards by 10 units, you would end up at the end with the camera at 490 units behind the character’s neck.

> As I understand it, the main rig needs “Activate Camera Rig” to be called every frame from the camera director.

You don’t actually need to call “Activate Camera Rig” every frame, you could just call it when something changes if you want… (if nothing is activated, the system just keeps running the same main layer rigs) I just like calling it every frame to be more explicit, a bit like a behaviour tree.

> Is that the case for the base and global rig? Or can they be a one off?

Base, global, and visual rigs are “persistent” so they stay until explicitly removed. That’s why in 5.6 I renamed those functions to “Activate *Persistent* Base/Global/Visual Camera Rig”.

> When we upgraded to 5.6, the global rig would only run if it was activated on RunCameraDirector.

I think there was indeed a bug where if camera rigs were activated during startup, they would be ignored. I’m actually looking into that bug today for 5.7...

> but when we upgraded, suddenly the transformations made by the main rig were essentially forgotten by the time the global rig was executed.

This might have been caused by something else… I suspect that it was caused by running the global camera rig using the same evaluation context as the the main camera rig. In 5.5 and 5.6 the base/global/visual blend stacks would take the evaluation context and apply any written properties from its InitialResult camera pose onto camera rigs in those blend stacks. Since the evaluation context of a GPC component writes the transform of that component into the InitialResult camera pose, it means any camera rigs running in that context would get that camera pose location overwritten. This is desirable for the main blend stack (so the cameras are “naturally attached” to the component that creates them), but less so for the other blend stacks since it indeed erases what came before. I used to solve this by using different evaluation contexts, but I think from 5.5 to 5.6 there might have been a change that the utility BP functions to push base/global/visual rigs changed the context it’s using… so I probably caused your troubles that way. Anyway, in 5.7 the base/global/visual blend stacks won’t apply the camera pose transform from the context anymore -- just the variables and other parameter values. This simplifies things a bit (only one context needed in most cases) and fixes some bugs (for people wanting to pass variable values to base/global/visual rigs). Sorry about the trouble!

> If you resolve it, are you able to share the CL here so we can backport it?

I don’t know how “isolated” that fix it but it’s this: https://github.com/EpicGames/UnrealEngine/commit/01f1bb73464d0d08558923d2c798d3b4b4ea8716

Note that because I’m working with external studios that are still on 5.6, I actually made the whole GameplayCameras plugin backwards compatible -- so in theory you can drop the entire head-revision (give or take a changeset or two) of the GameplayCameras directory into your 5.6 codebase and it would run fine, with all the new features and fixes.

Thanks! Have fun.

Hello [mention removed]​ !

Thank you for the detailed breakdown. I’ll look in to the game animation sample project as reference.

  • Generally, the global layer is for post-blend stuff (by definition) such as collision, occlusion, ray-casts, and so on.

Gotcha gotcha, in that case, the global rig should in theory have the context of the final offset if it is not adjusted within the global rig right?. Ex: Attaching to the pawn.

Perhaps there is an issue with how we’re activating the global and base rig then. As I understand it, the main rig needs “Activate Camera Rig” to be called every frame from the camera director. Is that the case for the base and global rig? Or can they be a one off?

When we upgraded to 5.6, the global rig would only run if it was activated on RunCameraDirector. I’ll check the sample project to confirm this, but also wanted to ask here just in case :slight_smile:

Kind regards,

Darien

Hello [mention removed]​ !

Thank you for the quick reply!

> I’m not sure what you mean by “context of the final offset”… but if your main layer rigs somehow take the camera and move it 500 units behind the character’s neck, then the global layer rigs will start from there. If you had only one global layer rig and all it did was move the camera forwards by 10 units, you would end up at the end with the camera at 490 units behind the character’s neck.

Apologies, didn’t explain that the best. By context I mean the FCameraPose from the FCameraNodeEvaluationResult. That’s the behaviour we were expecting when we were on 5.5, but when we upgraded, suddenly the transformations made by the main rig were essentially forgotten by the time the global rig was executed. This resulted in our collision node not colliding with anything because it think’s it’s at the pawn position. (We’ve tried using different safe position settings but this radically changed how the camera worked. Even without the attach to pawn node, this would happen)

> You don’t actually need to call “Activate Camera Rig” every frame, you could just call it when something changes if you want… (if nothing is activated, the system just keeps running the same main layer rigs) I just like calling it every frame to be more explicit, a bit like a behaviour tree.

Ah I see, that makes sense as a pattern. Thank you for clarifying!

> I think there was indeed a bug where if camera rigs were activated during startup, they would be ignored. I’m actually looking into that bug today for 5.7...

Ah gotcha gotcha, this might be what was causing the confusion. I thought it might have been intended behaviour. We were doing it when the camera director was activated, so it’s likely the same bug. If you resolve it, are you able to share the CL here so we can backport it?

Kind regards,

Darien

Hey [mention removed]​ !

Cheers for the follow up. That clears things up. I’ll keep in mind checking the head branch periodically (assuming you mean on Git as I’ve been checking main for updates but the changes there seem more widespread). And no worries! We understand the plugin is experimental and appreciate the support as we’re working with it!

Thanks again for the support, should be good to close out this question.

Kind regards,

Darien