#include <ComposableCameraSpiralNode.h>

Inherits: UComposableCameraCameraNodeBase

Positions the camera on a helical path around a pivot point.

Position-only node — rotation is left untouched, to be authored by a downstream LookAtNode (or similar). The position formula, evaluated each tick, is: P(t) = EffectivePivot + Axis * HeightCurve(NormalizedTime) + PerpDir(θ) * RadiusCurve(NormalizedTime)

EffectivePivot = ResolvedPivot + Forward * PivotOffset.X + Right * PivotOffset.Y + Axis * PivotOffset.Z

PerpDir(θ) = Forward * cos(θ) + Right * sin(θ) θ = InitialAngleDegrees + AngleCurve(NormalizedTime) Where the Spiral-Space basis (Up, Forward, Right) is resolved from RotationAxis and ReferenceDirection each tick — Forward is the ReferenceDirection vector projected onto the plane perpendicular to Axis and renormalized, and Right = Cross(Axis, Forward).

Curve authoring convention (Progress pattern, matching SplineNode's AutomaticMoveCurve): all three curves use X ∈ [0, 1] as normalized time within Duration; Y in absolute world units — Radius / Height in cm, AngleCurve in degrees. Direct curve evaluation means position at any arbitrary t is O(1) computable — no integration history, no accumulated state. A Loop-mode orbit typically authors AngleCurve as Y(0)=0, Y(1)=360·N for a seamless N-turn cycle; non-360 multiples produce an intentional retrace at the cycle seam.

Public Attributes

Return Name Description
EComposableCameraSpiralPivotSourceType PivotSourceType Whether the pivot comes from an Actor's location or a raw vector.
TObjectPtr< AActor > PivotActor Actor whose world location is used as the pivot. Typically driven by an upstream ReceivePivotActorNode's output, or set on the instance.
FVector PivotPosition Raw pivot position in world space. Typically driven by an upstream pivot-producing node via wire (PivotOffsetNode's output).
FVector PivotOffset Offset applied to the resolved pivot, expressed in Spiral Space: X = along Forward, Y = along Right, Z = along Axis (Up). Spiral Space is re-derived from RotationAxis and ReferenceDirection each tick, so this offset automatically tracks a pivot actor's orientation when Spiral Space is anchored to the actor.
EComposableCameraSpiralRotationAxis RotationAxis The axis around which the camera orbits. PivotActorUp silently falls back to WorldUp with a warning when PivotSourceType == FromVector.
FVector CustomAxis Custom rotation axis (world space). Normalized at runtime; falls back to WorldUp if near-zero.
EComposableCameraSpiralReferenceDirection ReferenceDirection The direction that anchors θ = 0 in the plane perpendicular to RotationAxis. PivotActorForward silently falls back to WorldX with a warning when PivotSourceType == FromVector.
FVector CustomDirection Custom θ = 0 direction (world space). Projected onto the plane perpendicular to the rotation axis at runtime.
float InitialAngleDegrees Starting angular offset applied to θ. Added to the value read from AngleCurve each tick, so the spiral can begin at any azimuth around the axis without re-authoring the curve.
TObjectPtr< UCurveFloat > RadiusCurve Radial distance from Axis over normalized time. X ∈ [0,1], Y in cm. A null curve is treated as a constant 0 radius (camera collapses onto Axis).
TObjectPtr< UCurveFloat > HeightCurve Signed distance along Axis from the pivot over normalized time. X ∈ [0,1], Y in cm (positive = along Axis, negative = against Axis). A null curve is treated as a constant 0 height.
TObjectPtr< UCurveFloat > AngleCurve Angular position (degrees, absolute) over normalized time. X ∈ [0,1], Y in degrees — positive = right-handed rotation around Axis. Progress pattern, same as SplineNode's AutomaticMoveCurve: θ at any instant is a direct curve read, not an integral of speed. A null curve is treated as a constant 0 angle.
float Duration Length of one "cycle" of the three curves, in seconds. Values at or below SMALL_NUMBER are treated as a degenerate duration — all three curves are sampled at NormalizedTime = 0 and the pose stays frozen at the initial frame.
EComposableCameraSpiralPlayMode PlayMode How the node behaves after the first cycle ends. See enum comment.

PivotSourceType

EComposableCameraSpiralPivotSourceType PivotSourceType {  }

Whether the pivot comes from an Actor's location or a raw vector.


PivotActor

TObjectPtr< AActor > PivotActor { nullptr }

Actor whose world location is used as the pivot. Typically driven by an upstream ReceivePivotActorNode's output, or set on the instance.


PivotPosition

FVector PivotPosition { FVector::ZeroVector }

Raw pivot position in world space. Typically driven by an upstream pivot-producing node via wire (PivotOffsetNode's output).


PivotOffset

FVector PivotOffset { FVector::ZeroVector }

Offset applied to the resolved pivot, expressed in Spiral Space: X = along Forward, Y = along Right, Z = along Axis (Up). Spiral Space is re-derived from RotationAxis and ReferenceDirection each tick, so this offset automatically tracks a pivot actor's orientation when Spiral Space is anchored to the actor.


RotationAxis

EComposableCameraSpiralRotationAxis RotationAxis {  }

The axis around which the camera orbits. PivotActorUp silently falls back to WorldUp with a warning when PivotSourceType == FromVector.


CustomAxis

FVector CustomAxis { FVector::UpVector }

Custom rotation axis (world space). Normalized at runtime; falls back to WorldUp if near-zero.


ReferenceDirection

EComposableCameraSpiralReferenceDirection ReferenceDirection {  }

The direction that anchors θ = 0 in the plane perpendicular to RotationAxis. PivotActorForward silently falls back to WorldX with a warning when PivotSourceType == FromVector.


CustomDirection

FVector CustomDirection { FVector::ForwardVector }

Custom θ = 0 direction (world space). Projected onto the plane perpendicular to the rotation axis at runtime.


InitialAngleDegrees

float InitialAngleDegrees { 0.f }

Starting angular offset applied to θ. Added to the value read from AngleCurve each tick, so the spiral can begin at any azimuth around the axis without re-authoring the curve.


RadiusCurve

TObjectPtr< UCurveFloat > RadiusCurve { nullptr }

Radial distance from Axis over normalized time. X ∈ [0,1], Y in cm. A null curve is treated as a constant 0 radius (camera collapses onto Axis).


HeightCurve

TObjectPtr< UCurveFloat > HeightCurve { nullptr }

Signed distance along Axis from the pivot over normalized time. X ∈ [0,1], Y in cm (positive = along Axis, negative = against Axis). A null curve is treated as a constant 0 height.


AngleCurve

TObjectPtr< UCurveFloat > AngleCurve { nullptr }

Angular position (degrees, absolute) over normalized time. X ∈ [0,1], Y in degrees — positive = right-handed rotation around Axis. Progress pattern, same as SplineNode's AutomaticMoveCurve: θ at any instant is a direct curve read, not an integral of speed. A null curve is treated as a constant 0 angle.


Duration

float Duration { 3.f }

Length of one "cycle" of the three curves, in seconds. Values at or below SMALL_NUMBER are treated as a degenerate duration — all three curves are sampled at NormalizedTime = 0 and the pose stays frozen at the initial frame.


PlayMode

EComposableCameraSpiralPlayMode PlayMode {  }

How the node behaves after the first cycle ends. See enum comment.

Public Methods

Return Name Description
UComposableCameraSpiralNode inline
void OnInitialize_Implementation virtual
void OnTickNode_Implementation virtual
void GetPinDeclarations_Implementation virtual const
void DrawNodeDebug virtual const Called each frame when the CCS.Debug.Viewport CVar is enabled, for every node on the currently running camera. Override to draw world-space debug gizmos via DrawDebugHelpers (DrawDebugSphere, DrawDebugLine, etc.) that visualise this node's runtime state — e.g. a pivot sphere for PivotOffsetNode, a look-at line for LookAtNode, the collision trace for CollisionPushNode, a sampled spline path for SplineNode.

UComposableCameraSpiralNode

inline

inline UComposableCameraSpiralNode()

OnInitialize_Implementation

virtual

virtual void OnInitialize_Implementation()

OnTickNode_Implementation

virtual

virtual void OnTickNode_Implementation(float DeltaTime, const FComposableCameraPose & CurrentCameraPose, FComposableCameraPose & OutCameraPose)

GetPinDeclarations_Implementation

virtual const

virtual void GetPinDeclarations_Implementation(TArray< FComposableCameraNodePinDeclaration > & OutPins) const

DrawNodeDebug

virtual const

virtual void DrawNodeDebug(UWorld * World, bool bViewerIsOutsideCamera) const

Called each frame when the CCS.Debug.Viewport CVar is enabled, for every node on the currently running camera. Override to draw world-space debug gizmos via DrawDebugHelpers (DrawDebugSphere, DrawDebugLine, etc.) that visualise this node's runtime state — e.g. a pivot sphere for PivotOffsetNode, a look-at line for LookAtNode, the collision trace for CollisionPushNode, a sampled spline path for SplineNode.

Access the owning camera via OwningCamera and current-frame pin values via the usual GetInputPinValue<T>() / member-read path — this hook fires AFTER TickNode, so pin-backed UPROPERTYs still hold the resolved values from the most recent evaluation.

bViewerIsOutsideCamera mirrors the ticker's frustum-draw flag: true when the viewer is observing the camera from outside (F8 eject, SIE, or CCS.Debug.Viewport.AlwaysShow), false when the player is looking through the camera. Most gizmos (pivot spheres at distant characters, lines to look-at targets, spline polylines far in the world) can ignore this and draw unconditionally. Gizmos that sit AT the camera's own position (e.g. CollisionPushNode's self-collision sphere) should gate on this bool so they don't hermetically seal the player inside the wireframe during live gameplay.

Default implementation does nothing. Compiled out in shipping builds.

Private Attributes

Return Name Description
float ElapsedTime Seconds elapsed since OnInitialize. Drives NormalizedTime. Accumulated unbounded — Fmod inside OnTickNode handles the wrap. Float precision on the ElapsedTime input to Fmod remains acceptable for realistic gameplay durations (hours at 60 fps); if this node is ever used for multi-day always-on installations, wrap ElapsedTime once per cycle.
FVector CapturedInitialForward Camera forward vector captured on the first tick. Used only when ReferenceDirection == CameraInitialForward.
bool bHasCapturedInitialForward True once CapturedInitialForward has been written. Cleared in OnInitialize so re-activation captures a fresh forward.
FVector DebugEffectivePivot
FVector DebugAxis
FVector DebugForward
FVector DebugRight
float DebugCurrentAngleDegrees
FVector DebugCurrentPosition

ElapsedTime

float ElapsedTime { 0.f }

Seconds elapsed since OnInitialize. Drives NormalizedTime. Accumulated unbounded — Fmod inside OnTickNode handles the wrap. Float precision on the ElapsedTime input to Fmod remains acceptable for realistic gameplay durations (hours at 60 fps); if this node is ever used for multi-day always-on installations, wrap ElapsedTime once per cycle.


CapturedInitialForward

FVector CapturedInitialForward { FVector::ForwardVector }

Camera forward vector captured on the first tick. Used only when ReferenceDirection == CameraInitialForward.


bHasCapturedInitialForward

bool bHasCapturedInitialForward { false }

True once CapturedInitialForward has been written. Cleared in OnInitialize so re-activation captures a fresh forward.


DebugEffectivePivot

FVector DebugEffectivePivot { FVector::ZeroVector }

DebugAxis

FVector DebugAxis { FVector::UpVector }

DebugForward

FVector DebugForward { FVector::ForwardVector }

DebugRight

FVector DebugRight { FVector::RightVector }

DebugCurrentAngleDegrees

float DebugCurrentAngleDegrees { 0.f }

DebugCurrentPosition

FVector DebugCurrentPosition { FVector::ZeroVector }

Private Methods

Return Name Description
bool ResolvePivot const Resolve the pivot location from PivotSourceType + PivotActor/PivotPosition. Returns false and logs an error when the chosen source is invalid (null actor). Output is written to OutPivot.
void ResolveSpiralBasis const Resolve the Spiral-Space basis (Axis, Forward, Right). Forward is the chosen ReferenceDirection projected onto the plane perpendicular to Axis and renormalized. Falls back to WorldUp / WorldX on degeneracy.

ResolvePivot

const

bool ResolvePivot(FVector & OutPivot) const

Resolve the pivot location from PivotSourceType + PivotActor/PivotPosition. Returns false and logs an error when the chosen source is invalid (null actor). Output is written to OutPivot.


ResolveSpiralBasis

const

void ResolveSpiralBasis(FVector & OutAxis, FVector & OutForward, FVector & OutRight) const

Resolve the Spiral-Space basis (Axis, Forward, Right). Forward is the chosen ReferenceDirection projected onto the plane perpendicular to Axis and renormalized. Falls back to WorldUp / WorldX on degeneracy.