diff --git a/Assets/PerfectWorld/Prefab/UI/HUD.prefab b/Assets/PerfectWorld/Prefab/UI/HUD.prefab index ab7caebd41..83aeefd681 100644 --- a/Assets/PerfectWorld/Prefab/UI/HUD.prefab +++ b/Assets/PerfectWorld/Prefab/UI/HUD.prefab @@ -186,7 +186,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -151.8, y: 7.8005} + m_AnchoredPosition: {x: -151.8, y: -19.49} m_SizeDelta: {x: 128, y: 180} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &2085804573552353186 diff --git a/Assets/PerfectWorld/Prefab/UI/HUDNPC.prefab b/Assets/PerfectWorld/Prefab/UI/HUDNPC.prefab index 602118c85d..4d65aed697 100644 --- a/Assets/PerfectWorld/Prefab/UI/HUDNPC.prefab +++ b/Assets/PerfectWorld/Prefab/UI/HUDNPC.prefab @@ -637,8 +637,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 100, y: 100} + m_AnchoredPosition: {x: 0, y: -19} + m_SizeDelta: {x: 128, y: 180} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &8158437551720672858 CanvasRenderer: diff --git a/Assets/PerfectWorld/Scripts/UI/ElsePlayerPortraitCapture.cs b/Assets/PerfectWorld/Scripts/UI/ElsePlayerPortraitCapture.cs index aacdbce114..01d982ef17 100644 --- a/Assets/PerfectWorld/Scripts/UI/ElsePlayerPortraitCapture.cs +++ b/Assets/PerfectWorld/Scripts/UI/ElsePlayerPortraitCapture.cs @@ -26,20 +26,27 @@ namespace BrewMonster [Header("Portrait Stage")] [Tooltip("Layer index for the Portrait layer (Project Settings > Tags and Layers).")] - [SerializeField] private int portraitLayer = 29; + [SerializeField] private int portraitLayer = 9; [Tooltip("Fixed world position of the clone. Keep far from gameplay to avoid overlap.")] [SerializeField] private Vector3 stagePosition = new Vector3(9999f, 0f, 0f); [Header("Framing")] - [Tooltip("Distance from head focus point to camera.")] - [SerializeField] private float cameraDistance = 1.2f; + [Tooltip("Distance the camera sits in front of the face (meters). ~0.5–1.0 for tight portrait.")] + [SerializeField] private float cameraDistance = 0.7f; - [Tooltip("Height offset above stage origin used when head bone cannot be found.")] - [SerializeField] private float fallbackHeadHeight = 1.6f; + [Tooltip("Extra upward offset applied to the look-at focus point (moves portrait up toward eyes).")] + [SerializeField] private float faceUpLift = 0.04f; - [Tooltip("Slight horizontal angle so the portrait isn't perfectly front-on (degrees).")] - [SerializeField][Range(0f, 45f)] private float horizontalAngleDeg = 15f; + [Tooltip("How fast the camera rotation interpolates toward the face each frame (higher = snappier).")] + [SerializeField][Range(1f, 50f)] private float lookSmoothSpeed = 20f; + + [Header("Head Bone Face Direction")] + [Tooltip("Which local axis of Bip01 Head points OUT of the face (toward the camera).")] + [SerializeField] private FaceAxis headFaceAxis = FaceAxis.Forward; + + [Tooltip("Flip the chosen face axis (e.g. if Forward gives back-of-head, enable this).")] + [SerializeField] private bool flipFaceAxis; [SerializeField][Range(10f, 60f)] private float fieldOfView = 35f; @@ -88,6 +95,7 @@ namespace BrewMonster } _portraitClone = Object.Instantiate(modelRoot.gameObject); + _portraitClone.GetComponentInChildren().enabled = false; _portraitClone.name = "[Portrait] ElsePlayer"; _portraitClone.transform.position = stagePosition; _portraitClone.transform.rotation = Quaternion.identity; @@ -100,7 +108,7 @@ namespace BrewMonster _headBone = PortraitCaptureUtils.FindHeadBone(_portraitClone.transform); - PositionCamera(); + AttachCameraToHeadBone(); SetCameraEnabled(true); } @@ -115,27 +123,68 @@ namespace BrewMonster Object.Destroy(_portraitClone); _portraitClone = null; } + DetachCamera(); _headBone = null; - SetCameraEnabled(false); + } + + private void LateUpdate() + { + if (portraitCamera == null || !portraitCamera.enabled) return; + if (_headBone == null) return; + + Vector3 faceCenter = _headBone.position + Vector3.up * faceUpLift; + Vector3 toFace = faceCenter - portraitCamera.transform.position; + if (toFace.sqrMagnitude < 0.0001f) return; + + Quaternion targetRot = Quaternion.LookRotation(toFace); + portraitCamera.transform.rotation = Quaternion.Slerp( + portraitCamera.transform.rotation, + targetRot, + Time.deltaTime * lookSmoothSpeed); } // ────────────────────────────────────────────────────────────────────────────── // Internal // ────────────────────────────────────────────────────────────────────────────── - private void PositionCamera() + private void AttachCameraToHeadBone() { if (portraitCamera == null) return; - Vector3 focusPoint = _headBone != null - ? _headBone.position - : stagePosition + Vector3.up * fallbackHeadHeight; + Transform anchor = _headBone != null ? _headBone : _portraitClone.transform; + Vector3 faceDir = GetFaceDirectionWorld(anchor); + Vector3 headPos = anchor.position; + Vector3 faceCenter = headPos + Vector3.up * faceUpLift; + Vector3 camWorldPos = headPos + faceDir * cameraDistance; - float rad = horizontalAngleDeg * Mathf.Deg2Rad; - Vector3 offset = new Vector3(Mathf.Sin(rad) * cameraDistance, 0f, -cameraDistance); + portraitCamera.transform.SetParent(anchor, worldPositionStays: false); + portraitCamera.transform.position = camWorldPos; + portraitCamera.transform.LookAt(faceCenter); + } - portraitCamera.transform.position = focusPoint + offset; - portraitCamera.transform.LookAt(focusPoint); + private Vector3 GetFaceDirectionWorld(Transform bone) + { + Vector3 dir = headFaceAxis switch + { + FaceAxis.Forward => bone.forward, + FaceAxis.Back => -bone.forward, + FaceAxis.Up => bone.up, + FaceAxis.Down => -bone.up, + FaceAxis.Right => bone.right, + FaceAxis.Left => -bone.right, + _ => bone.forward + }; + + return flipFaceAxis ? -dir : dir; + } + + private void DetachCamera() + { + if (portraitCamera != null) + { + portraitCamera.transform.SetParent(transform, worldPositionStays: false); + SetCameraEnabled(false); + } } private void SetCameraEnabled(bool enabled) @@ -166,5 +215,7 @@ namespace BrewMonster } outputTexture = PortraitCaptureUtils.CreatePortraitRT("ElsePlayerPortraitRT", portraitCamera); } + + public enum FaceAxis { Forward, Back, Up, Down, Right, Left } } }