From 462a3b5850ca53d09c77639848df553c7e1e8feb Mon Sep 17 00:00:00 2001 From: GanX <2423855310@qq.com> Date: Sat, 18 Oct 2025 17:14:09 +0800 Subject: [PATCH] =?UTF-8?q?refactor():=20=E4=BD=BF=E7=94=A8=E7=AC=AC?= =?UTF-8?q?=E4=B8=80=E4=BA=BA=E7=A7=B0=E5=B0=84=E7=BA=BF=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E6=9D=A5=E6=9B=BF=E6=8D=A2=E6=8E=89=20=E5=8F=AF=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=92=8C=E5=8F=AF=E7=BC=96=E8=BE=91=E7=A7=8D=E9=87=8D?= =?UTF-8?q?=E5=90=88=E7=9A=84=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Prefab/Gameplay/Player.prefab | 30 +++++--- .../Facility/DoorInteractController.cs | 20 +++++- .../Gameplay/Player/FirstPersonRaycaster.cs | 72 +++++++++++++++++++ .../Player/FirstPersonRaycaster.cs.meta | 3 + .../Gameplay/Player/PlayerEditController.cs | 63 ++++++---------- .../Player/PlayerInteractorController.cs | 66 +++++++---------- 6 files changed, 161 insertions(+), 93 deletions(-) create mode 100644 Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs create mode 100644 Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs.meta diff --git a/Assets/Prefab/Gameplay/Player.prefab b/Assets/Prefab/Gameplay/Player.prefab index f22c312..2f1ac06 100644 --- a/Assets/Prefab/Gameplay/Player.prefab +++ b/Assets/Prefab/Gameplay/Player.prefab @@ -153,6 +153,7 @@ GameObject: - component: {fileID: 5633281206562703781} - component: {fileID: 1774433545530707224} - component: {fileID: 7145954873497571105} + - component: {fileID: 8217262775185484522} m_Layer: 0 m_Name: Player m_TagString: Player @@ -255,8 +256,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d7071907d14f433789ea37a8367b584a, type: 3} m_Name: m_EditorClassIdentifier: - cardBookData: {fileID: 11400000, guid: 6884501cc489c254aaf6836e84649588, type: 2} - playerCardsController: {fileID: 0} + MaxHealth: 100 --- !u!114 &5633281206562703781 MonoBehaviour: m_ObjectHideFlags: 0 @@ -287,12 +287,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: dc03712b5c254b8380471356f3bac151, type: 3} m_Name: m_EditorClassIdentifier: - interactRange: 15 - interactableLayer: - serializedVersion: 2 - m_Bits: 4294967295 - playerCamera: {fileID: 4390370389971332335} - isDrawGizmos: 0 + raycaster: {fileID: 0} + isEnablePlayerInteraction: 1 --- !u!114 &7145954873497571105 MonoBehaviour: m_ObjectHideFlags: 0 @@ -306,6 +302,24 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: cam: {fileID: 4390370389971332335} +--- !u!114 &8217262775185484522 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3550102669167288263} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 50205a0afd8544b883204613f428e066, type: 3} + m_Name: + m_EditorClassIdentifier: + rayLength: 15 + layerMask: + serializedVersion: 2 + m_Bits: 4294967295 + playerCamera: {fileID: 0} + drawGizmos: 1 --- !u!1 &3610935294554348573 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Script/Gameplay/Facility/DoorInteractController.cs b/Assets/Script/Gameplay/Facility/DoorInteractController.cs index 17b191d..e5f37cd 100644 --- a/Assets/Script/Gameplay/Facility/DoorInteractController.cs +++ b/Assets/Script/Gameplay/Facility/DoorInteractController.cs @@ -5,9 +5,10 @@ using Interface; namespace Script.Gameplay.Facility { - public class DoorInteractController : InteractableBaseController, IEditableComponent + public class DoorInteractController : IInteractable, IEditableComponent { [SerializeField] private bool isActive = true; + public bool Interactable = true; public bool IsActive { get => isActive; @@ -21,8 +22,13 @@ namespace Script.Gameplay.Facility public string ComponentName { get; set; } = "DoorSwitch"; public LockLevel LockLevel => LockLevel.Red; private bool isOpened = false; + + public string GetInteractPrompt() + { + return "按F进行交互"; + } - public override void Interact(GameObject interactor) + public void Interact(GameObject interactor) { if (!Interactable) return; if (isOpened) @@ -37,6 +43,16 @@ namespace Script.Gameplay.Facility } } + public void OnGazeEnter(GameObject editor) + { + throw new NotImplementedException(); + } + + public void OnGazeExit(GameObject editor) + { + throw new NotImplementedException(); + } + private void OpenDoor() { Debug.Log("Open door"); diff --git a/Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs b/Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs new file mode 100644 index 0000000..d7e0aca --- /dev/null +++ b/Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs @@ -0,0 +1,72 @@ +using UnityEngine; + +namespace Gameplay.Player +{ + /// + /// 通用第一人称射线检测,获取玩家注视的 GameObject。 + /// + public class FirstPersonRaycaster : MonoBehaviour + { + [Header("Raycast Settings")] + [SerializeField] private float rayLength = 15f; + [SerializeField] private LayerMask layerMask = ~0; // 默认检测所有层 + [SerializeField] private Camera playerCamera; + [SerializeField] private bool drawGizmos = true; + + /// + /// 当前玩家注视的 GameObject(只读)。 + /// + public GameObject CurrentLookAtObject { get; private set; } + + void Awake() + { + if (playerCamera == null) + { + var camObj = GameObject.FindWithTag("MainCamera"); + if (camObj != null) + playerCamera = camObj.GetComponent(); + } + } + + void Update() + { + UpdateLookAtObject(); + } + + private void UpdateLookAtObject() + { + if (playerCamera == null) + { + CurrentLookAtObject = null; + return; + } + Ray ray = new Ray(playerCamera.transform.position, playerCamera.transform.forward); + if (Physics.Raycast(ray, out RaycastHit hit, rayLength, layerMask)) + { + CurrentLookAtObject = hit.collider.gameObject; + } + else + { + CurrentLookAtObject = null; + } + } + + void OnDrawGizmos() + { + if (!drawGizmos) return; + Camera cam = playerCamera; + if (cam == null && Application.isPlaying == false) + { + var camObj = GameObject.FindWithTag("MainCamera"); + if (camObj != null) + cam = camObj.GetComponent(); + } + if (cam == null) return; + Gizmos.color = Color.cyan; + Vector3 origin = cam.transform.position; + Vector3 dir = cam.transform.forward * rayLength; + Gizmos.DrawLine(origin, origin + dir); + } + } +} + diff --git a/Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs.meta b/Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs.meta new file mode 100644 index 0000000..a456f39 --- /dev/null +++ b/Assets/Script/Gameplay/Player/FirstPersonRaycaster.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50205a0afd8544b883204613f428e066 +timeCreated: 1760775915 \ No newline at end of file diff --git a/Assets/Script/Gameplay/Player/PlayerEditController.cs b/Assets/Script/Gameplay/Player/PlayerEditController.cs index 7cc28a7..bdbb69d 100644 --- a/Assets/Script/Gameplay/Player/PlayerEditController.cs +++ b/Assets/Script/Gameplay/Player/PlayerEditController.cs @@ -7,10 +7,7 @@ namespace Gameplay.Player { public class PlayerEditController:MonoBehaviour { - [SerializeField] private float interactRange = 15f; - [SerializeField] private LayerMask interactableLayer; - [SerializeField] private Camera playerCamera; - [SerializeField] private bool isDrawGizmos; + [SerializeField] private FirstPersonRaycaster raycaster; // 新增:第一人称射线检测器 private IEditable currentTarget; // 射线命中的当前可编辑对象(用于按键交互) private IEditable previousGazedTarget; // 上一次注视的对象(用于注视进入/离开事件) @@ -20,10 +17,14 @@ namespace Gameplay.Player void Start() { inputManager = InputManager.Instance; - if (playerCamera == null) - playerCamera = GameObject.FindWithTag("MainCamera").GetComponent(); - + if (raycaster == null) + raycaster = GetComponent() ?? GetComponentInChildren(); + if (raycaster == null) + raycaster = FindObjectOfType(); + if (raycaster == null) + Debug.LogWarning("FirstPersonRaycaster not found! Please assign or add it to the player."); ControllerLocator.Instance.Register(this); + } void Update() @@ -33,42 +34,27 @@ namespace Gameplay.Player void DetectInteractable() { - if (playerCamera == null) return; + if (raycaster == null) return; + GameObject lookAtObj = raycaster.CurrentLookAtObject; + IEditable hitEditable = lookAtObj != null ? lookAtObj.GetComponent() : null; - Ray ray = new Ray(playerCamera.transform.position, playerCamera.transform.forward); - if (Physics.Raycast(ray, out RaycastHit hit, interactRange, interactableLayer)) + // 如果命中对象与之前注视的不一样,触发进入/离开事件 + if (hitEditable != previousGazedTarget) { - IEditable hitEditable = hit.collider.GetComponent(); - - // 如果命中对象与之前注视的不一样,触发进入/离开事件 - if (hitEditable != previousGazedTarget) - { - if (previousGazedTarget != null) - { - previousGazedTarget.OnGazeExit(this); - } - - if (hitEditable != null) - { - hitEditable.OnGazeEnter(this); - } - - previousGazedTarget = hitEditable; - } - - currentTarget = hitEditable; - } - else - { - // 没有命中时,如果之前有注视对象,触发离开 if (previousGazedTarget != null) { previousGazedTarget.OnGazeExit(this); - previousGazedTarget = null; } - currentTarget = null; + if (hitEditable != null) + { + hitEditable.OnGazeEnter(this); + } + + previousGazedTarget = hitEditable; } + + currentTarget = hitEditable; } public IEditable GetCurrentTarget() @@ -78,12 +64,7 @@ namespace Gameplay.Player void OnDrawGizmos() { - if(!isDrawGizmos) return; - if (playerCamera == null) return; - Gizmos.color = Color.red; - Vector3 origin = playerCamera.transform.position; - Vector3 direction = playerCamera.transform.forward * interactRange; - Gizmos.DrawLine(origin, origin + direction); + // 交由 FirstPersonRaycaster 绘制射线 } } } \ No newline at end of file diff --git a/Assets/Script/Gameplay/Player/PlayerInteractorController.cs b/Assets/Script/Gameplay/Player/PlayerInteractorController.cs index d5cfe8a..38c0487 100644 --- a/Assets/Script/Gameplay/Player/PlayerInteractorController.cs +++ b/Assets/Script/Gameplay/Player/PlayerInteractorController.cs @@ -7,10 +7,7 @@ namespace Gameplay.Player { public class PlayerInteractorController : MonoBehaviour { - [SerializeField] private float interactRange = 15f; - [SerializeField] private LayerMask interactableLayer; - [SerializeField] private Camera playerCamera; - [SerializeField] private bool isDrawGizmos; + [SerializeField] private FirstPersonRaycaster raycaster; // 新增:第一人称射线检测器 [SerializeField] private bool isEnablePlayerInteraction = true; // 是否启用玩家交互功能 private IInteractable currentTarget; // 被射线命中的当前可交互对象(用于按键交互) @@ -18,8 +15,12 @@ namespace Gameplay.Player void Start() { - if (playerCamera == null) - playerCamera = GameObject.FindWithTag("MainCamera").GetComponent(); + if (raycaster == null) + raycaster = GetComponent() ?? GetComponentInChildren(); + if (raycaster == null) + raycaster = FindObjectOfType(); + if (raycaster == null) + Debug.LogWarning("FirstPersonRaycaster not found! Please assign or add it to the player."); var input = InputManager.Instance.Input; input.Player.Interact.performed += ctx => @@ -38,45 +39,31 @@ namespace Gameplay.Player void DetectInteractable() { - if (playerCamera == null) return; + if (raycaster == null) return; if (!isEnablePlayerInteraction) return; - Ray ray = new Ray(playerCamera.transform.position, playerCamera.transform.forward); - if (Physics.Raycast(ray, out RaycastHit hit, interactRange, interactableLayer)) + GameObject lookAtObj = raycaster.CurrentLookAtObject; + IInteractable hitInteractable = lookAtObj != null ? lookAtObj.GetComponent() : null; + + // 如果命中对象与之前注视的不一样,触发进入/离开事件 + if (hitInteractable != previousGazedTarget) { - IInteractable hitInteractable = hit.collider.GetComponent(); - - // 如果命中对象与之前注视的不一样,触发进入/离开事件 - if (hitInteractable != previousGazedTarget) - { - if (previousGazedTarget != null) - { - previousGazedTarget.OnGazeExit(this.gameObject); - } - - if (hitInteractable != null) - { - hitInteractable.OnGazeEnter(this.gameObject); - // 这里可以显示交互提示UI,例如 “E - 开门” - Debug.Log(hitInteractable.GetInteractPrompt()); - } - - previousGazedTarget = hitInteractable; - } - - currentTarget = hitInteractable; - } - else - { - // 没有命中时,如果之前有注视对象,触发离开 if (previousGazedTarget != null) { previousGazedTarget.OnGazeExit(this.gameObject); - previousGazedTarget = null; } - currentTarget = null; + if (hitInteractable != null) + { + hitInteractable.OnGazeEnter(this.gameObject); + // 这里可以显示交互提示UI,例如 “E - 开门” + Debug.Log(hitInteractable.GetInteractPrompt()); + } + + previousGazedTarget = hitInteractable; } + + currentTarget = hitInteractable; } public void SetPlayerInteractionEnabled(bool isEnabled) @@ -92,12 +79,7 @@ namespace Gameplay.Player void OnDrawGizmos() { - if (isDrawGizmos) return; - if (playerCamera == null) return; - Gizmos.color = Color.green; - Vector3 origin = playerCamera.transform.position; - Vector3 direction = playerCamera.transform.forward * interactRange; - Gizmos.DrawLine(origin, origin + direction); + // 交由 FirstPersonRaycaster 绘制射线 } } } \ No newline at end of file