@@ -10,47 +10,68 @@ import simd
1010import SwiftUI
1111import UntoldEngine
1212
13+ // CameraFollowComponent defines how a camera should follow a target entity.
14+ // It stores the name of the target and the offset from that target.
15+ // This makes it possible to create a third-person or top-down camera easily.
1316public class CameraFollowComponent : Component , Codable {
14- var targetName : String = " nil "
15- var offset : simd_float3 = . zero
17+ var targetName : String = " nil " // The name of the entity the camera should follow
18+ var offset : simd_float3 = . zero // The offset position relative to the target
1619 public required init ( ) { }
1720}
1821
22+ // -----------------------------------------------------------------------------
23+ // Helper functions for accessing and modifying CameraFollowComponent values
24+ // -----------------------------------------------------------------------------
25+
1926func getOffsetTarget( entityId: EntityID ) -> simd_float3 {
2027 guard let cameraFollowComponent = scene. get ( component: CameraFollowComponent . self, for: entityId) else { return . zero }
21-
2228 return cameraFollowComponent. offset
2329}
2430
2531func setOffsetTarget( entityId: EntityID , offset: simd_float3 ) {
2632 guard let cameraFollowComponent = scene. get ( component: CameraFollowComponent . self, for: entityId) else { return }
27-
2833 cameraFollowComponent. offset = offset
2934}
3035
3136func getTargetName( entityId: EntityID ) -> String ? {
3237 guard let cameraFollowComponent = scene. get ( component: CameraFollowComponent . self, for: entityId) else { return nil }
33-
3438 return cameraFollowComponent. targetName
3539}
3640
3741func setTargetName( entityId: EntityID , name: String ) {
3842 guard let cameraFollowComponent = scene. get ( component: CameraFollowComponent . self, for: entityId) else { return }
39-
4043 cameraFollowComponent. targetName = name
4144}
4245
46+ // -----------------------------------------------------------------------------
47+ // Camera Follow System
48+ // Updates all entities with a CameraFollowComponent once per frame.
49+ // Calls the cameraFollow function to smoothly track the target entity.
50+ // -----------------------------------------------------------------------------
51+
4352public func cameraFollowUpdate( deltaTime _: Float ) {
4453 let customId = getComponentId ( for: CameraFollowComponent . self)
4554 let entities = queryEntitiesWithComponentIds ( [ customId] , in: scene)
4655
4756 for entity in entities {
4857 guard let cameraFollowComponent = scene. get ( component: CameraFollowComponent . self, for: entity) else { continue }
4958
50- cameraFollow ( entityId: entity, targetName: cameraFollowComponent. targetName, offset: cameraFollowComponent. offset, smoothSpeed: 0.5 , alignWithOrientation: false )
59+ cameraFollow (
60+ entityId: entity,
61+ targetName: cameraFollowComponent. targetName,
62+ offset: cameraFollowComponent. offset,
63+ smoothSpeed: 0.5 ,
64+ alignWithOrientation: false
65+ )
5166 }
5267}
5368
69+ // -----------------------------------------------------------------------------
70+ // Core camera follow logic
71+ // Moves the camera smoothly to follow the target and optionally aligns its
72+ // orientation with the target’s forward direction.
73+ // -----------------------------------------------------------------------------
74+
5475func cameraFollow(
5576 entityId: EntityID ,
5677 targetName: String ,
@@ -61,78 +82,78 @@ func cameraFollow(
6182 lockZAxis: Bool = false ,
6283 alignWithOrientation: Bool = true
6384) {
85+ // Ensure the entity has a CameraComponent
6486 guard let cameraComponent = scene. get ( component: CameraComponent . self, for: entityId) else {
6587 return
6688 }
6789
90+ // Find the target entity by name
6891 guard let targetId: EntityID = findEntity ( name: targetName) else {
6992 return
7093 }
7194
7295 // Get the target's position and orientation
7396 var targetPosition : simd_float3 = UntoldEngine . getPosition ( entityId: targetId)
7497 let targetOrientation : simd_float3x3 = UntoldEngine . getOrientation ( entityId: targetId) // Rotation matrix
75-
7698 var cameraPosition = cameraComponent. localPosition
7799
78- // Apply axis locking to the target position
100+ // Apply axis locking (keeps the camera fixed on chosen axes)
79101 if lockXAxis { targetPosition. x = cameraPosition. x }
80102 if lockYAxis { targetPosition. y = cameraPosition. y }
81103 if lockZAxis { targetPosition. z = cameraPosition. z }
82104
83- // Calculate the offset relative to the entity's orientation
105+ // Adjust offset based on orientation if requested
84106 var adjustedOffset = offset
85107 if alignWithOrientation {
86- // Rotate the offset vector using the 3x3 rotation matrix
108+ // Rotate the offset by the target's orientation matrix
87109 adjustedOffset = targetOrientation * offset
88110 }
89111
90- // Calculate the desired position by applying the adjusted offset
112+ // Calculate where the camera *wants* to be
91113 let desiredPosition : simd_float3 = targetPosition + adjustedOffset
92114
93- // Smoothly interpolate the camera position towards the desired position
115+ // Smoothly interpolate from the current camera position to the desired position
94116 cameraPosition = lerp ( start: cameraPosition, end: desiredPosition, t: smoothSpeed)
95117
96- // Move the camera to the new position
118+ // Move the camera to its new position
97119 moveCameraTo ( entityId: entityId, cameraPosition. x, cameraPosition. y, cameraPosition. z)
98120
99- // Align the camera's view direction using lookAt
121+ // Orient the camera towards the target if enabled
100122 if alignWithOrientation {
101- // Calculate the forward direction of the entity
102- let _: simd_float3 = targetOrientation * simd_float3( 0 , 0 , 1 ) // Forward direction in local space
123+ // Forward direction of the target
124+ let _: simd_float3 = targetOrientation * simd_float3( 0 , 0 , 1 )
103125
104- // Determine the "up" vector (you might need to adjust this based on your engine's coordinate system)
105- let up : simd_float3 = targetOrientation * simd_float3( 0 , 1 , 0 ) // Transform the local up vector by the orientation
126+ // Up vector (adjust depending on coordinate system)
127+ let up : simd_float3 = targetOrientation * simd_float3( 0 , 1 , 0 )
106128
107- // Use lookAt to orient the camera towards the target's forward direction
129+ // Look at the target from the camera’s position
108130 cameraLookAt ( entityId: entityId, eye: cameraPosition, target: targetPosition, up: up)
109131 }
110132}
111133
134+ // -----------------------------------------------------------------------------
135+ // Editor Integration
136+ // Makes CameraFollowComponent visible in the Editor (Inspector panel).
137+ // -----------------------------------------------------------------------------
138+
112139var CameraFollowComponent_Editor : ComponentOption_Editor = . init(
113140 id: getComponentId ( for: CameraFollowComponent . self) ,
114141 name: " Camera Follow " ,
115142 type: CameraFollowComponent . self,
116143 view: makeEditorView ( fields: [
144+ // Editable field for selecting the target entity by name
117145 . text( label: " Target Name " ,
118146 placeholder: " Entity name " ,
119- get: { entityId in
120- getTargetName ( entityId: entityId) ?? " None "
121- } ,
122- set: { entityId, targetName in
123- setTargetName ( entityId: entityId, name: targetName)
124- } ) ,
147+ get: { entityId in getTargetName ( entityId: entityId) ?? " None " } ,
148+ set: { entityId, targetName in setTargetName ( entityId: entityId, name: targetName) } ) ,
125149
150+ // Editable field for the follow offset
126151 . vector3( label: " Offset " ,
127- get: { entityId in
128- getOffsetTarget ( entityId: entityId)
129- } ,
130- set: { entityId, newOffset in
131- setOffsetTarget ( entityId: entityId, offset: newOffset)
132- } ) ,
133-
152+ get: { entityId in getOffsetTarget ( entityId: entityId) } ,
153+ set: { entityId, newOffset in setOffsetTarget ( entityId: entityId, offset: newOffset) } ) ,
134154 ] ) ,
135155 onAdd: { entityId in
156+ // When added in the Editor, register this component with the entity
136157 registerComponent ( entityId: entityId, componentType: CameraFollowComponent . self)
137158 }
138159)
0 commit comments