Add per-pose camera target Y offset and route ceiling offset to camera
Add cameraTargetYOffset to PlayerPose so each pose can offset the Cinemachine ThirdPersonCamera follow/look target independently of the body mesh. PlayerCharacterController gains a cameraTarget reference (auto-resolved via the "CameraTarget" child, base position captured in Awake) and a single-writer SetCameraTargetLocalYOffset. PlayerPoseController eases the per-pose camera offset and composes it with the shared ceiling offset, so the camera target follows the ceiling lift in lockstep with the mesh. Wire cameraTarget explicitly in SampleScene. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba7f8804c5
commit
0edc59555a
4 files changed
+51
-1
No files matched your search
@@ -4634,6 +4634,7 @@ MonoBehaviour:
|
||||
wallTopOutBoost: 0.3
|
||||
cameraOrbit: {fileID: 0}
|
||||
meshOffsetTarget: {fileID: 0}
|
||||
cameraTarget: {fileID: 1774267802}
|
||||
--- !u!114 &1141039643
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -138,6 +138,9 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
[Tooltip("Visual model root moved by the wall-climb lean offset (into/out of the wall) while the capsule stays put. Must parent both the skinned mesh and its bones. Auto-resolved from the SkinnedMeshRenderer if empty.")]
|
||||
[SerializeField] private Transform meshOffsetTarget;
|
||||
|
||||
[Tooltip("Camera follow/look target (what the ThirdPersonCamera tracks). Moved vertically by the active pose's camera y-offset plus the ceiling offset, without moving the body. Auto-resolved from a child named \"CameraTarget\" if empty.")]
|
||||
[SerializeField] private Transform cameraTarget;
|
||||
|
||||
/// <summary>
|
||||
/// When false, movement/jump input is ignored but gravity and ground contact
|
||||
/// keep running. Toggled by the GameModeManager.
|
||||
@@ -214,6 +217,8 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
private bool _offsetDragging; // true while the offset/rotation is being set (pauses camera orbit)
|
||||
private bool _climbLeftGround; // true once the capsule has left the ground while climbing (enables ground-detach)
|
||||
private Vector3 _meshBaseLocalPos; // meshOffsetTarget's authored local position; the lean offset is applied relative to this
|
||||
private Vector3 _cameraTargetBaseLocalPos; // cameraTarget's authored local position; the pose/ceiling camera offset is applied relative to this
|
||||
private float _cameraYOffset; // last camera-target local-up offset written (cached so an unchanged value skips the transform write)
|
||||
private float _meshOffsetResidual; // leftover mesh lean still easing back to 0 after leaving the wall (no longer climbing)
|
||||
private Vector3 _meshOffsetExitNormal; // wall normal latched at exit; the residual eases along it (live _wallNormal may shift on stray contact)
|
||||
private readonly RaycastHit[] _wallProbe = new RaycastHit[8];
|
||||
@@ -248,6 +253,13 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
if (meshOffsetTarget) _meshBaseLocalPos = meshOffsetTarget.localPosition;
|
||||
|
||||
if (!cameraTarget)
|
||||
{
|
||||
Transform t = transform.Find("CameraTarget");
|
||||
if (t) cameraTarget = t;
|
||||
}
|
||||
if (cameraTarget) _cameraTargetBaseLocalPos = cameraTarget.localPosition;
|
||||
}
|
||||
|
||||
void Update()
|
||||
@@ -778,6 +790,19 @@ public partial class PlayerCharacterController : MonoBehaviour
|
||||
SetMeshLean(_meshOffsetExitNormal, _meshOffsetResidual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places the camera target (the ThirdPersonCamera's follow/look target) at the given local-up
|
||||
/// offset relative to its authored local position, without moving the body. Driven by
|
||||
/// <see cref="PlayerPoseController"/> with the active pose's camera y-offset plus the ceiling offset;
|
||||
/// 0 restores the authored target height. Cached so an unchanged value skips the write.
|
||||
/// </summary>
|
||||
public void SetCameraTargetLocalYOffset(float y)
|
||||
{
|
||||
if (!cameraTarget || _cameraYOffset == y) return;
|
||||
_cameraYOffset = y;
|
||||
cameraTarget.localPosition = _cameraTargetBaseLocalPos + Vector3.up * y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raycasts from the character center toward the surface (along -_wallNormal) to find the
|
||||
/// current normal and the gap (surface clearance) to it. The capsule's support distance
|
||||
|
||||
@@ -41,6 +41,12 @@ public class PlayerPose : ScriptableObject
|
||||
"hangs at the right height. Ignored on the ground and on walls. Meters.")]
|
||||
[SerializeField] private float ceilingYOffset;
|
||||
|
||||
[Tooltip("Local-up offset applied to the camera target (the ThirdPersonCamera's follow/look target) " +
|
||||
"while this pose is active, in ANY state. Frames the pose by raising/lowering what the camera " +
|
||||
"tracks, without moving the body. The ceiling offset is added on top of this while on the " +
|
||||
"ceiling. Meters.")]
|
||||
[SerializeField] private float cameraTargetYOffset;
|
||||
|
||||
/// <summary>Idle clip held while the player stands still in this pose (may be null).</summary>
|
||||
public AnimationClip Clip => clip;
|
||||
|
||||
@@ -61,4 +67,7 @@ public class PlayerPose : ScriptableObject
|
||||
|
||||
/// <summary>Local-up offset applied to the model only while on the ceiling.</summary>
|
||||
public float CeilingYOffset => ceilingYOffset;
|
||||
|
||||
/// <summary>Local-up offset applied to the camera target while this pose is active (any state).</summary>
|
||||
public float CameraTargetYOffset => cameraTargetYOffset;
|
||||
}
|
||||
@@ -24,6 +24,8 @@ using UnityEngine.InputSystem;
|
||||
/// crossfades the outgoing and incoming clips over <see cref="poseBlendDuration"/> instead of snapping.
|
||||
/// The per-pose ceiling y-offset is eased toward its target and routed through the controller (the
|
||||
/// single writer of the model-offset transform) so it composes with the wall-lean instead of fighting it.
|
||||
/// A separate per-pose camera y-offset (plus that same ceiling offset) is likewise eased and routed to the
|
||||
/// controller's camera target, framing each pose for the ThirdPersonCamera without moving the body.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(PlayerCharacterController))]
|
||||
[DisallowMultipleComponent]
|
||||
@@ -83,7 +85,8 @@ public partial class PlayerPoseController : MonoBehaviour
|
||||
private float _blend; // 0..1 weight of the "to" input; 1 = fully on the target pose
|
||||
private bool _blending;
|
||||
|
||||
private float _ceilingOffset; // eased local-up offset currently fed to the controller
|
||||
private float _ceilingOffset; // eased local-up offset currently fed to the controller (mesh + camera)
|
||||
private float _cameraPoseOffset; // eased per-pose camera-target offset; the ceiling offset is added on top when routed to the controller
|
||||
|
||||
// Rest pose snapshot of the whole Animator subtree, taken in Awake before the Animator first
|
||||
// evaluates. An AnimationClipPlayable only writes the bones its curves cover, so a clip that
|
||||
@@ -149,6 +152,16 @@ public partial class PlayerPoseController : MonoBehaviour
|
||||
? Mathf.MoveTowards(_ceilingOffset, ceilingTarget, ceilingOffsetSpeed * Time.deltaTime)
|
||||
: ceilingTarget;
|
||||
_player.SetPoseLocalYOffset(_ceilingOffset);
|
||||
|
||||
// Camera target: a per-pose framing offset (applied in any state) plus the same ceiling offset,
|
||||
// routed to the ThirdPersonCamera's follow target only (never the body). The pose part eases like
|
||||
// the ceiling offset so cycling poses glides the camera; the ceiling part reuses the value above so
|
||||
// camera and model stay in lockstep on the ceiling.
|
||||
float cameraPoseTarget = pose ? pose.CameraTargetYOffset : 0f;
|
||||
_cameraPoseOffset = ceilingOffsetSpeed > 0f
|
||||
? Mathf.MoveTowards(_cameraPoseOffset, cameraPoseTarget, ceilingOffsetSpeed * Time.deltaTime)
|
||||
: cameraPoseTarget;
|
||||
_player.SetCameraTargetLocalYOffset(_cameraPoseOffset + _ceilingOffset);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
@@ -157,9 +170,11 @@ public partial class PlayerPoseController : MonoBehaviour
|
||||
// next idle frame. Also clear any ceiling offset we were holding.
|
||||
ReleasePose();
|
||||
_ceilingOffset = 0f;
|
||||
_cameraPoseOffset = 0f;
|
||||
if (_player)
|
||||
{
|
||||
_player.SetPoseLocalYOffset(0f);
|
||||
_player.SetCameraTargetLocalYOffset(0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user