From 8a299eea99976b8e482d19e363ff2cba9f254959 Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Tue, 26 May 2026 16:58:01 +0100 Subject: [PATCH] add camera boundary --- Assets/Scenes/5. Gameplay.unity | 453 +++++++++++++++++- .../Core/Camera/CameraBoundarTrigger.cs | 36 ++ .../Core/Camera/CameraBoundarTrigger.cs.meta | 3 + .../Intrepid/Core/Camera/CameraBoundary.cs | 27 ++ .../Core/Camera/CameraBoundary.cs.meta | 3 + .../Core/Camera/CameraBoundaryManager.cs | 289 +++++++++++ .../Core/Camera/CameraBoundaryManager.cs.meta | 3 + ...CameraGroundProjectionConfinerExtension.cs | 254 ++++++++++ ...aGroundProjectionConfinerExtension.cs.meta | 3 + .../Intrepid/Core/Camera/CameraSystem.cs | 6 +- .../Camera/Events/CameraBoundaryEnterEvent.cs | 9 + .../Events/CameraBoundaryEnterEvent.cs.meta | 3 + .../Camera/Events/CameraBoundaryExitEvent.cs | 9 + .../Events/CameraBoundaryExitEvent.cs.meta | 3 + 14 files changed, 1086 insertions(+), 15 deletions(-) create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs.meta create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs.meta create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs.meta create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs create mode 100644 Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs.meta create mode 100644 Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs create mode 100644 Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs.meta create mode 100644 Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs create mode 100644 Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs.meta diff --git a/Assets/Scenes/5. Gameplay.unity b/Assets/Scenes/5. Gameplay.unity index f4a9ab3..0936b08 100644 --- a/Assets/Scenes/5. Gameplay.unity +++ b/Assets/Scenes/5. Gameplay.unity @@ -1379,6 +1379,7 @@ GameObject: - component: {fileID: 744497487} - component: {fileID: 744497486} - component: {fileID: 744497485} + - component: {fileID: 744497488} m_Layer: 0 m_Name: Cube (28) m_TagString: Untagged @@ -1447,7 +1448,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 9452ae1262a74094f8a68013fbcd1834, type: 2} + - {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -1479,6 +1480,20 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 744497483} m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &744497488 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 744497483} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0a0ba32e2ebe4f2eb4f2e18c11be758d, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::__Prototype.Scripts.MeshColorSetter + meshRenderer: {fileID: 744497486} + color: {r: 0.6156997, g: 0.2946778, b: 0.8113208, a: 1} --- !u!1 &783026646 GameObject: m_ObjectHideFlags: 0 @@ -1842,13 +1857,13 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 873481847} serializedVersion: 2 - m_LocalRotation: {x: 0.38268343, y: 0, z: 0, w: 0.92387956} - m_LocalPosition: {x: 0, y: 12, z: -5} + m_LocalRotation: {x: 0.38268346, y: 0, z: 0, w: 0.9238795} + m_LocalPosition: {x: 0, y: 10.606603, z: -10.606601} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1761661293} - m_LocalEulerAnglesHint: {x: 45, y: 0, z: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &873481849 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1952,15 +1967,15 @@ Camera: m_LensShift: {x: 0, y: 0} m_NormalizedViewPortRect: serializedVersion: 2 - x: 0.123447776 + x: 0 y: 0 - width: 1.0609529 - height: 1.061039 - near clip plane: 0.3 - far clip plane: 1000 - field of view: 60 + width: 1 + height: 1 + near clip plane: 0.1 + far clip plane: 5000 + field of view: 40 orthographic: 1 - orthographic size: 6.5 + orthographic size: 7 m_Depth: 0 m_CullingMask: serializedVersion: 2 @@ -2360,6 +2375,167 @@ Transform: - {fileID: 1809909214} m_Father: {fileID: 2054429927} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1223120086 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1223120087} + - component: {fileID: 1223120090} + - component: {fileID: 1223120092} + - component: {fileID: 1223120091} + - component: {fileID: 1223120093} + m_Layer: 0 + m_Name: Follow + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1223120087 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223120086} + serializedVersion: 2 + m_LocalRotation: {x: 0.38268346, y: 0, z: 0, w: 0.9238795} + m_LocalPosition: {x: 0, y: 10.606603, z: -10.606601} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1710245337} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1223120090 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223120086} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f9dfa5b682dcd46bda6128250e975f58, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.Cinemachine::Unity.Cinemachine.CinemachineCamera + Priority: + Enabled: 1 + m_Value: 10 + OutputChannel: 1 + StandbyUpdate: 2 + m_StreamingVersion: 20241001 + m_LegacyPriority: 0 + Target: + TrackingTarget: {fileID: 1478155984} + LookAtTarget: {fileID: 0} + CustomLookAtTarget: 0 + Lens: + FieldOfView: 40 + OrthographicSize: 7 + NearClipPlane: 0.1 + FarClipPlane: 5000 + Dutch: 0 + ModeOverride: 0 + PhysicalProperties: + GateFit: 2 + SensorSize: {x: 21.946, y: 16.002} + LensShift: {x: 0, y: 0} + FocusDistance: 10 + Iso: 200 + ShutterSpeed: 0.005 + Aperture: 16 + BladeCount: 5 + Curvature: {x: 2, y: 11} + BarrelClipping: 0.25 + Anamorphism: 0 + BlendHint: 0 +--- !u!114 &1223120091 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223120086} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 62d54399b04b4fa48a73e121d4937dd4, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.Cinemachine::Unity.Cinemachine.CinemachinePanTilt + ReferenceFrame: 0 + RecenterTarget: 0 + PanAxis: + Value: 0 + Center: 0 + Range: {x: -180, y: 180} + Wrap: 1 + Recentering: + Enabled: 0 + Wait: 1 + Time: 2 + Restrictions: 0 + TiltAxis: + Value: 45 + Center: 0 + Range: {x: -70, y: 70} + Wrap: 0 + Recentering: + Enabled: 0 + Wait: 1 + Time: 2 + Restrictions: 0 +--- !u!114 &1223120092 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223120086} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 886251e9a18ece04ea8e61686c173e1b, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.Cinemachine::Unity.Cinemachine.CinemachinePositionComposer + CameraDistance: 15 + DeadZoneDepth: 0 + Composition: + ScreenPosition: {x: 0, y: 0} + DeadZone: + Enabled: 0 + Size: {x: 0.2, y: 0.2} + HardLimits: + Enabled: 0 + Size: {x: 0.8, y: 0.8} + Offset: {x: 0, y: 0} + CenterOnActivate: 1 + TargetOffset: {x: 0, y: 0, z: 0} + Damping: {x: 1, y: 0, z: 1} + Lookahead: + Enabled: 1 + Time: 0.5 + Smoothing: 15 + IgnoreY: 1 +--- !u!114 &1223120093 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223120086} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c34046a3bb0046209d0489e839c1f8ba, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Camera.CameraGroundProjectionConfinerExtension + confineX: 1 + confineZ: 1 + fallbackProjectionY: 0 + useFollowTargetY: 1 + projectionYSmoothTime: 0.08 + drawDebugProjection: 1 --- !u!1 &1264271869 GameObject: m_ObjectHideFlags: 0 @@ -2632,6 +2808,87 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1286810257} m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1372692861 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1372692862} + - component: {fileID: 1372692865} + - component: {fileID: 1372692864} + - component: {fileID: 1372692863} + m_Layer: 0 + m_Name: BoundaryA + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1372692862 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1372692861} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1943926653} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1372692863 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1372692861} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8141d03c55254ee5a7409fbf76ddff16, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Camera.CameraBoundaryTrigger + boundary: {fileID: 1372692864} +--- !u!114 &1372692864 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1372692861} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0b288204b12348cbbba504fb13d2a8c1, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Camera.CameraBoundary + boxCollider: {fileID: 1372692865} +--- !u!65 &1372692865 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1372692861} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 1 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 24.054745, y: 1, z: 16.044468} + m_Center: {x: -0.014335632, y: 0, z: 3.9807591} --- !u!1 &1434619732 GameObject: m_ObjectHideFlags: 0 @@ -2898,6 +3155,87 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Entities.EntityMotorSmoothRotation turnSpeed: 720 +--- !u!1 &1524378542 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1524378543} + - component: {fileID: 1524378546} + - component: {fileID: 1524378545} + - component: {fileID: 1524378544} + m_Layer: 0 + m_Name: BoundaryA (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1524378543 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1524378542} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -18, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1943926653} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1524378544 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1524378542} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8141d03c55254ee5a7409fbf76ddff16, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Camera.CameraBoundaryTrigger + boundary: {fileID: 1524378545} +--- !u!114 &1524378545 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1524378542} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0b288204b12348cbbba504fb13d2a8c1, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Camera.CameraBoundary + boxCollider: {fileID: 1524378546} +--- !u!65 &1524378546 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1524378542} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 1 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 7.999547, y: 4.956458, z: 34.4506} + m_Center: {x: 17.990416, y: 1.978229, z: 13.269833} --- !u!1 &1532299328 GameObject: m_ObjectHideFlags: 0 @@ -3262,6 +3600,10 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: + - target: {fileID: 1110237080051008694, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} - target: {fileID: 6646150498868371415, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3} propertyPath: m_LocalPosition.x value: 0 @@ -3311,6 +3653,38 @@ PrefabInstance: m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 774b83ee4bf84c4c9b531c2a6350b8a5, type: 3} +--- !u!1 &1710245336 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1710245337} + m_Layer: 0 + m_Name: Cameras + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1710245337 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1710245336} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1223120087} + m_Father: {fileID: 2054429927} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!137 &1750149050 stripped SkinnedMeshRenderer: m_CorrespondingSourceObject: {fileID: -2717395650310496025, guid: 2faa610713d3b3c439473daa55e8c60a, type: 3} @@ -3346,8 +3720,9 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Camera.CameraSystem subscriptionId: a1f48773fed94f9385fd09875081cc76 - mainCamera: {fileID: 0} - cinemachineBrain: {fileID: 0} + mainCamera: {fileID: 873481852} + cinemachineBrain: {fileID: 873481849} + cameraBoundaryManager: {fileID: 2059090170} --- !u!4 &1761661293 Transform: m_ObjectHideFlags: 0 @@ -3362,6 +3737,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 873481848} + - {fileID: 2059090169} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1777983739 @@ -3744,6 +4120,8 @@ Transform: - {fileID: 1031327464} - {fileID: 1552570365} - {fileID: 744497484} + - {fileID: 1372692862} + - {fileID: 1524378543} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!54 &1943926654 @@ -4410,9 +4788,58 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1179841130} + - {fileID: 1710245337} - {fileID: 1478155984} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2059090168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2059090169} + - component: {fileID: 2059090170} + m_Layer: 0 + m_Name: BoundaryManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2059090169 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2059090168} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1761661293} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2059090170 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2059090168} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f46a8ced4ba84673b632d5ab60e27d51, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Core.Camera.CameraBoundaryManager + subscriptionId: 1053a94ff61f4bbab2371b708c5725b4 + enterBlendTime: 1 + boundsBlendTime: 0.75 + exitBlendTime: 0.5 --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs new file mode 100644 index 0000000..e0b734b --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs @@ -0,0 +1,36 @@ +using Intrepid.Core.Camera.Events; +using Intrepid.GameManagers; +using Intrepid.Utilities; +using UnityEngine; + +namespace Intrepid.Core.Camera +{ + [RequireComponent(typeof(Collider))] + public sealed class CameraBoundaryTrigger : MonoBehaviour + { + [GroupInjections, SerializeField] private CameraBoundary boundary; + + private void Reset() + { + boundary = GetComponentInParent(); + + var col = GetComponent(); + col.isTrigger = true; + } + + private void OnTriggerEnter(Collider other) + { + Proxy.Instance.EventBus.Raise(new CameraBoundaryEnterEvent + { + Boundary = boundary + }); + } + + private void OnTriggerExit(Collider other) + { + Proxy.Instance.EventBus.Raise(new CameraBoundaryExitEvent + { + Boundary = boundary + }); + } + }} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs.meta b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs.meta new file mode 100644 index 0000000..6df8095 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundarTrigger.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8141d03c55254ee5a7409fbf76ddff16 +timeCreated: 1779803052 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs new file mode 100644 index 0000000..1ca6901 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs @@ -0,0 +1,27 @@ +using Intrepid.Utilities; +using UnityEngine; + +namespace Intrepid.Core.Camera +{ + [DisallowMultipleComponent] + [RequireComponent(typeof(BoxCollider))] + public sealed class CameraBoundary : MonoBehaviour + { + [GroupInjections, SerializeField] private BoxCollider boxCollider; + + public Bounds WorldBounds => boxCollider.bounds; + +#if UNITY_EDITOR + private void OnDrawGizmos() + { + var b = boxCollider.bounds; + + Gizmos.color = new Color(0f, 1f, 1f, 0.25f); + Gizmos.DrawCube(b.center, b.size); + + Gizmos.color = Color.cyan; + Gizmos.DrawWireCube(b.center, b.size); + } +#endif + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs.meta b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs.meta new file mode 100644 index 0000000..a3ba35a --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundary.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0b288204b12348cbbba504fb13d2a8c1 +timeCreated: 1779802832 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs new file mode 100644 index 0000000..548735b --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs @@ -0,0 +1,289 @@ +using System; +using System.Collections.Generic; +using Intrepid.Core.Camera.Events; +using Intrepid.SimpleEvents; +using Intrepid.Utilities; +using UnityEngine; + +namespace Intrepid.Core.Camera +{ + public class CameraBoundaryManager : EventListener + { + [GroupConfigurations, SerializeField] private float enterBlendTime = 1f; + [GroupConfigurations, SerializeField] private float boundsBlendTime = 0.75f; + [GroupConfigurations, SerializeField] private float exitBlendTime = 0.5f; + + private readonly List activeBoundaries = new(); + + private Bounds currentBounds; + private Bounds targetBounds; + private Bounds blendStartBounds; + private Bounds fadingOutBounds; + + private bool hasCurrentBounds; + private bool hasTargetBounds; + private bool hasFadingOutBounds; + + private float targetWeight; + private float boundsBlend = 1f; + + public float BoundaryWeight { get; private set; } + + public float SmoothBoundaryWeight => SmoothStep(BoundaryWeight); + + public bool HasActiveBoundary => activeBoundaries.Count > 0; + + protected override void InitializeVariables() + { + } + + protected override void OnBootstrap() + { + + } + + protected override void Subscriptions() + { + Subscribe(e => Enter(e.Boundary)); + Subscribe(e => Exit(e.Boundary)); + } + + private void Enter(CameraBoundary boundary) + { + if (boundary.IsNull()) + { + return; + } + + if (activeBoundaries.Contains(boundary)) + { + return; + } + + activeBoundaries.Add(boundary); + + hasFadingOutBounds = false; + targetWeight = 1f; + + RefreshTargetBounds(); + } + + private void Exit(CameraBoundary boundary) + { + if (boundary.IsNull()) + { + return; + } + + if (!activeBoundaries.Contains(boundary)) + { + return; + } + + var hadCameraBounds = TryGetCameraBounds(out var previousCameraBounds); + + activeBoundaries.Remove(boundary); + + if (activeBoundaries.Count == 0) + { + if (hadCameraBounds) + { + fadingOutBounds = previousCameraBounds; + hasFadingOutBounds = true; + } + + hasTargetBounds = false; + hasCurrentBounds = false; + + targetWeight = 0f; + return; + } + + hasFadingOutBounds = false; + targetWeight = 1f; + + RefreshTargetBounds(); + } + + private void Update() + { + UpdateWeight(); + UpdateBoundsBlend(); + } + + private void UpdateWeight() + { + var blendTime = targetWeight > BoundaryWeight + ? enterBlendTime + : exitBlendTime; + + if (blendTime <= 0f) + { + BoundaryWeight = targetWeight; + + if (BoundaryWeight <= 0f) + { + hasFadingOutBounds = false; + } + + return; + } + + var speed = 1f / blendTime; + + BoundaryWeight = Mathf.MoveTowards( + BoundaryWeight, + targetWeight, + speed * Time.deltaTime + ); + + if (BoundaryWeight <= 0f && targetWeight <= 0f) + { + BoundaryWeight = 0f; + hasFadingOutBounds = false; + } + } + + private void UpdateBoundsBlend() + { + if (!hasTargetBounds) + { + return; + } + + if (boundsBlend >= 1f) + { + currentBounds = targetBounds; + hasCurrentBounds = true; + return; + } + + if (boundsBlendTime <= 0f) + { + boundsBlend = 1f; + currentBounds = targetBounds; + hasCurrentBounds = true; + return; + } + + boundsBlend += Time.deltaTime / boundsBlendTime; + boundsBlend = Mathf.Clamp01(boundsBlend); + + var t = SmoothStep(boundsBlend); + + currentBounds = LerpBounds(blendStartBounds, targetBounds, t); + hasCurrentBounds = true; + } + + public bool TryGetCameraBounds(out Bounds bounds) + { + if (hasCurrentBounds) + { + bounds = currentBounds; + return true; + } + + if (hasFadingOutBounds) + { + bounds = fadingOutBounds; + return true; + } + + bounds = default; + return false; + } + + private void RefreshTargetBounds() + { + if (!TryCalculateEffectiveBounds(out var newTargetBounds)) + { + hasTargetBounds = false; + return; + } + + if (!hasCurrentBounds) + { + currentBounds = newTargetBounds; + targetBounds = newTargetBounds; + blendStartBounds = newTargetBounds; + + hasCurrentBounds = true; + hasTargetBounds = true; + + boundsBlend = 1f; + return; + } + + blendStartBounds = currentBounds; + targetBounds = newTargetBounds; + + hasTargetBounds = true; + boundsBlend = 0f; + } + + private bool TryCalculateEffectiveBounds(out Bounds bounds) + { + if (activeBoundaries.Count == 0) + { + bounds = default; + return false; + } + + bounds = activeBoundaries[0].WorldBounds; + + for (var i = 1; i < activeBoundaries.Count; i++) + { + bounds = Intersect(bounds, activeBoundaries[i].WorldBounds); + } + + return true; + } + + private static Bounds Intersect(Bounds a, Bounds b) + { + var min = Vector3.Max(a.min, b.min); + var max = Vector3.Min(a.max, b.max); + + if (min.x > max.x) + { + var x = (min.x + max.x) * 0.5f; + min.x = x; + max.x = x; + } + + if (min.y > max.y) + { + var y = (min.y + max.y) * 0.5f; + min.y = y; + max.y = y; + } + + if (min.z > max.z) + { + var z = (min.z + max.z) * 0.5f; + min.z = z; + max.z = z; + } + + var result = new Bounds(); + result.SetMinMax(min, max); + return result; + } + + private static Bounds LerpBounds(Bounds from, Bounds to, float t) + { + var min = Vector3.Lerp(from.min, to.min, t); + var max = Vector3.Lerp(from.max, to.max, t); + + var result = new Bounds(); + result.SetMinMax(min, max); + return result; + } + + private static float SmoothStep(float t) + { + t = Mathf.Clamp01(t); + return t * t * (3f - 2f * t); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs.meta b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs.meta new file mode 100644 index 0000000..99b1efc --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraBoundaryManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f46a8ced4ba84673b632d5ab60e27d51 +timeCreated: 1779802949 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs b/Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs new file mode 100644 index 0000000..d65c513 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs @@ -0,0 +1,254 @@ +using Intrepid.Utilities; +using Unity.Cinemachine; +using UnityEngine; + +namespace Intrepid.Core.Camera +{ + [DisallowMultipleComponent] + public sealed class CameraGroundProjectionConfinerExtension : CinemachineExtension + { + //[GroupConfigurations, SerializeField] private float groundY; + [GroupConfigurations, SerializeField] private bool confineX = true; + [GroupConfigurations, SerializeField] private bool confineZ = true; + [GroupConfigurations, SerializeField] private float fallbackProjectionY; + [GroupConfigurations, SerializeField] private bool useFollowTargetY = true; + [GroupConfigurations, SerializeField] private float projectionYSmoothTime = 0.08f; + + [GroupDebug, SerializeField] private bool drawDebugProjection = true; + + private float currentProjectionY; + private float projectionYVelocity; + private bool hasProjectionY; + private readonly Vector3[] groundCorners = new Vector3[4]; + + protected override void PostPipelineStageCallback( + CinemachineVirtualCameraBase vcam, + CinemachineCore.Stage stage, + ref CameraState state, + float deltaTime) + { + // Use Finalize so Body, Aim and Noise have already done their work. + if (stage != CinemachineCore.Stage.Finalize) return; + + var lens = state.Lens; + if (!lens.Orthographic) return; + + var manager = CameraSystem.Instance?.CameraBoundaryManager; + if (manager is null) return; + + if (!manager.TryGetCameraBounds(out var bounds)) return; + + var weight = manager.SmoothBoundaryWeight; + if (weight <= 0f) return; + + var cameraPosition = state.RawPosition + state.PositionCorrection; + var cameraRotation = state.RawOrientation; + var projectionY = GetProjectionY(vcam, deltaTime); + CalculateOrthographicGroundCorners(cameraPosition, cameraRotation, lens, projectionY, groundCorners); + var correction = CalculateCorrection(groundCorners, bounds); + state.PositionCorrection += correction * weight; + } + + private float GetProjectionY(CinemachineVirtualCameraBase vcam, float deltaTime) + { + var targetY = fallbackProjectionY; + + if (useFollowTargetY && vcam.Follow is not null) + { + targetY = vcam.Follow.position.y; + } + + if (!hasProjectionY || deltaTime < 0f || projectionYSmoothTime <= 0f) + { + currentProjectionY = targetY; + projectionYVelocity = 0f; + hasProjectionY = true; + return currentProjectionY; + } + + currentProjectionY = Mathf.SmoothDamp( + currentProjectionY, + targetY, + ref projectionYVelocity, + projectionYSmoothTime, + Mathf.Infinity, + deltaTime + ); + + return currentProjectionY; + } + + private static void CalculateOrthographicGroundCorners( + Vector3 cameraPosition, + Quaternion cameraRotation, + LensSettings lens, + float groundY, + Vector3[] results) + { + var halfHeight = lens.OrthographicSize; + var halfWidth = lens.OrthographicSize * lens.Aspect; + + var right = cameraRotation * Vector3.right; + var up = cameraRotation * Vector3.up; + var forward = cameraRotation * Vector3.forward; + + // Ordem: + // 0 = bottom-left + // 1 = bottom-right + // 2 = top-right + // 3 = top-left + Vector3[] localCorners = + { + (-right * halfWidth) + (-up * halfHeight), + (right * halfWidth) + (-up * halfHeight), + (right * halfWidth) + (up * halfHeight), + (-right * halfWidth) + (up * halfHeight), + }; + + var groundPlane = new Plane(Vector3.up, new Vector3(0f, groundY, 0f)); + + for (var i = 0; i < 4; i++) + { + var rayOrigin = cameraPosition + localCorners[i]; + var ray = new Ray(rayOrigin, forward); + + if (groundPlane.Raycast(ray, out float distance)) + { + results[i] = ray.GetPoint(distance); + } + else + { + results[i] = rayOrigin; + } + } + } + + private Vector3 CalculateCorrection(Vector3[] corners, Bounds bounds) + { + var minX = corners[0].x; + var maxX = corners[0].x; + var minZ = corners[0].z; + var maxZ = corners[0].z; + + for (var i = 1; i < corners.Length; i++) + { + minX = Mathf.Min(minX, corners[i].x); + maxX = Mathf.Max(maxX, corners[i].x); + + minZ = Mathf.Min(minZ, corners[i].z); + maxZ = Mathf.Max(maxZ, corners[i].z); + } + + var correction = Vector3.zero; + + var footprintWidth = maxX - minX; + var footprintDepth = maxZ - minZ; + + var boundsWidth = bounds.max.x - bounds.min.x; + var boundsDepth = bounds.max.z - bounds.min.z; + + // if camera is able to see more than the boundary, + // center it to avoid fighting between min/max. + if (footprintWidth > boundsWidth) + { + var footprintCenterX = (minX + maxX) * 0.5f; + correction.x = bounds.center.x - footprintCenterX; + } + else + { + if (minX < bounds.min.x) + { + correction.x += bounds.min.x - minX; + } + + if (maxX > bounds.max.x) + { + correction.x -= maxX - bounds.max.x; + } + } + + if (footprintDepth > boundsDepth) + { + var footprintCenterZ = (minZ + maxZ) * 0.5f; + correction.z = bounds.center.z - footprintCenterZ; + } + else + { + if (minZ < bounds.min.z) + { + correction.z += bounds.min.z - minZ; + } + + if (maxZ > bounds.max.z) + { + correction.z -= maxZ - bounds.max.z; + } + } + + if (!confineX) + { + correction.x = 0f; + } + if (!confineZ) + { + correction.z = 0f; + } + correction.y = 0f; + + return correction; + } + +#if UNITY_EDITOR + private void OnDrawGizmos() + { + if (!drawDebugProjection) return; + + var outputCamera = CameraSystem.Instance?.MainCamera; + if (outputCamera is null) return; + + Gizmos.color = Color.yellow; + var corners = new Vector3[4]; + if (!TryGetCameraGroundProjection(outputCamera, currentProjectionY, corners)) + { + return; + } + + for (var i = 0; i < 4; i++) + { + var a = corners[i]; + var b = corners[(i + 1) % 4]; + + Gizmos.DrawLine(a, b); + Gizmos.DrawSphere(a, 0.15f); + } + } + + private static bool TryGetCameraGroundProjection( + UnityEngine.Camera camera, + float groundY, + Vector3[] results) + { + var groundPlane = new Plane(Vector3.up, new Vector3(0f, groundY, 0f)); + Vector3[] viewportCorners = + { + new(0f, 0f, 0f), // bottom-left + new(1f, 0f, 0f), // bottom-right + new(1f, 1f, 0f), // top-right + new(0f, 1f, 0f), // top-left + }; + + for (var i = 0; i < 4; i++) + { + var ray = camera.ViewportPointToRay(viewportCorners[i]); + if (!groundPlane.Raycast(ray, out var distance)) + { + return false; + } + results[i] = ray.GetPoint(distance); + } + + return true; + } +#endif + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs.meta b/Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs.meta new file mode 100644 index 0000000..1095b9e --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraGroundProjectionConfinerExtension.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c34046a3bb0046209d0489e839c1f8ba +timeCreated: 1779802276 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/CameraSystem.cs b/Assets/Scripts/Intrepid/Core/Camera/CameraSystem.cs index d3c86a3..cf5d549 100644 --- a/Assets/Scripts/Intrepid/Core/Camera/CameraSystem.cs +++ b/Assets/Scripts/Intrepid/Core/Camera/CameraSystem.cs @@ -11,13 +11,15 @@ namespace Intrepid.Core.Camera { [GroupInjections, SerializeField] private UnityEngine.Camera mainCamera; [GroupInjections, SerializeField] private CinemachineBrain cinemachineBrain; - + [GroupInjections, SerializeField] private CameraBoundaryManager cameraBoundaryManager; + private bool IsBlendClearing { get; set; } private CameraStreamStack CurrentCameraStreamStack { get; set; } public bool IsCameraBlending => cinemachineBrain.IsBlending; public float NormalAngle => mainCamera.transform.rotation.eulerAngles.z; public UnityEngine.Camera MainCamera => mainCamera; - public Vector2 CameraTargetPosition => CurrentCameraStreamStack?.TopCameraStream?.VCamera.Follow.position ?? Vector2.zero; + // public Vector2 CameraTargetPosition => CurrentCameraStreamStack?.TopCameraStream?.VCamera.Follow.position ?? Vector2.zero; + public CameraBoundaryManager CameraBoundaryManager => cameraBoundaryManager; protected override void InitializeVariables() { diff --git a/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs new file mode 100644 index 0000000..59d8a78 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs @@ -0,0 +1,9 @@ +using Intrepid.SimpleEvents; + +namespace Intrepid.Core.Camera.Events +{ + public class CameraBoundaryEnterEvent : IEventBase + { + public CameraBoundary Boundary { get; set; } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs.meta b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs.meta new file mode 100644 index 0000000..da607f3 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryEnterEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5116ac60fac54e48849057a7ddc55849 +timeCreated: 1779808201 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs new file mode 100644 index 0000000..47dbfa5 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs @@ -0,0 +1,9 @@ +using Intrepid.SimpleEvents; + +namespace Intrepid.Core.Camera.Events +{ + public class CameraBoundaryExitEvent : IEventBase + { + public CameraBoundary Boundary { get; set; } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs.meta b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs.meta new file mode 100644 index 0000000..ec74ab9 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Camera/Events/CameraBoundaryExitEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b2e7155437804591b2f1d188808645e0 +timeCreated: 1779808216 \ No newline at end of file