feat(Gaze): 实现注视相关提示

This commit is contained in:
2025-10-21 13:07:48 +08:00
parent 4737220e42
commit d3eed0945c
7 changed files with 5827 additions and 5103 deletions

File diff suppressed because one or more lines are too long

View File

@@ -367,6 +367,276 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &598246108
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 598246109}
- component: {fileID: 598246111}
- component: {fileID: 598246110}
m_Layer: 5
m_Name: First
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &598246109
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 598246108}
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: 1874673210}
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: -64}
m_SizeDelta: {x: 400, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &598246110
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 598246108}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: "\u6309F\u4EA4\u4E92\n"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 538fd603dd2dddd4daf31aa55f44c7e5, type: 2}
m_sharedMaterial: {fileID: -3917370665499528571, guid: 538fd603dd2dddd4daf31aa55f44c7e5,
type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 25
m_fontSizeBase: 25
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!222 &598246111
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 598246108}
m_CullTransparentMesh: 1
--- !u!1 &873782154
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 873782155}
- component: {fileID: 873782157}
- component: {fileID: 873782156}
m_Layer: 5
m_Name: Second
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &873782155
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 873782154}
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: 1874673210}
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: -116}
m_SizeDelta: {x: 200, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &873782156
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 873782154}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: "\u6309F\u4EA4\u4E92\n"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 538fd603dd2dddd4daf31aa55f44c7e5, type: 2}
m_sharedMaterial: {fileID: -3917370665499528571, guid: 538fd603dd2dddd4daf31aa55f44c7e5,
type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 15
m_fontSizeBase: 15
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 256
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!222 &873782157
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 873782154}
m_CullTransparentMesh: 1
--- !u!1 &1067459524
GameObject:
m_ObjectHideFlags: 0
@@ -477,6 +747,7 @@ RectTransform:
m_Children:
- {fileID: 1082771939}
- {fileID: 165485610}
- {fileID: 1874673210}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -1300,6 +1571,59 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1852915596}
m_CullTransparentMesh: 1
--- !u!1 &1874673209
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1874673210}
- component: {fileID: 1874673211}
m_Layer: 5
m_Name: Reminder
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1874673210
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1874673209}
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: 598246109}
- {fileID: 873782155}
m_Father: {fileID: 1067459529}
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_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1874673211
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1874673209}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c8ca1a3e5bb84ffdb0f2e03f50e4a077, type: 3}
m_Name:
m_EditorClassIdentifier:
IsOpenOnFirstLoad: 1
reminderFirstText: {fileID: 598246110}
reminderSecondText: {fileID: 873782156}
--- !u!1001 &1937221272884345375
PrefabInstance:
m_ObjectHideFlags: 0

View File

@@ -9,11 +9,44 @@ namespace Script.Gameplay.Player
{
public class PlayerConnectController : MonoBehaviour
{
public bool IsEnableConnecting = true; // 是否启用连接功能
private bool isEnableConnecting = false;
public bool IsEnableConnecting
{
get => isEnableConnecting;
set
{
isEnableConnecting = value;
if (isEnableConnecting == false)
{
// 重置当前目标
CurrentTarget = null;
previousGazedTarget = null;
outTarget = null;
inputTarget = null;
}
}
} // 是否启用连接功能
[SerializeField] private FirstPersonRaycaster raycaster; // 第一人称射线检测器
[SerializeField] private float maxConnectDistance = 10f; // 最大连接距离
private IConnectable currentTarget; // 当前注视的可连接对象
private IConnectable currentTarget;
public IConnectable CurrentTarget
{
get => currentTarget;
set
{
if (value != null)
{
currentTarget = value;
OnGazeEnter?.Invoke(currentTarget.GetGameObject());
}
else
{
OnGazeExit?.Invoke(null);
}
}
} // 当前注视的可连接对象
private IConnectable previousGazedTarget; // 上一次注视的 IConnectable用于触发进入/离开)
private InputManager inputManager;
@@ -21,14 +54,16 @@ namespace Script.Gameplay.Player
private IConnectable inputTarget;
public event Action<IConnectable> OnSetPointA;
public event Action<IConnectable> OnSetPointB;
public event Action<GameObject> OnGazeEnter;
public event Action<GameObject> OnGazeExit;
void Start()
{
inputManager = InputManager.Instance;
inputManager.Input.Player.SetOutput.performed += ctx => SetPointA(currentTarget);
inputManager.Input.Player.SetInput.performed += ctx => SetPointB(currentTarget);
inputManager.Input.Player.SetOutput.performed += ctx => SetPointA(CurrentTarget);
inputManager.Input.Player.SetInput.performed += ctx => SetPointB(CurrentTarget);
inputManager.Input.Player.Connect.performed += ctx => CreateConnection();
inputManager.Input.Player.CutLine.performed += ctx => CutConnectLine(currentTarget);
inputManager.Input.Player.CutLine.performed += ctx => CutConnectLine(CurrentTarget);
if (raycaster == null)
raycaster = GetComponent<FirstPersonRaycaster>() ?? GetComponentInChildren<FirstPersonRaycaster>();
@@ -48,6 +83,7 @@ namespace Script.Gameplay.Player
void DetectConnectable()
{
if (raycaster == null) return;
if(!IsEnableConnecting) return;
GameObject lookAtObj = raycaster.CurrentLookAtObject;
IConnectable hitConnectable = lookAtObj != null ? lookAtObj.GetComponent<IConnectable>() : null;
@@ -68,13 +104,13 @@ namespace Script.Gameplay.Player
previousGazedTarget = hitConnectable;
}
currentTarget = hitConnectable;
CurrentTarget = hitConnectable;
}
public IConnectable GetCurrentTarget()
{
return currentTarget;
return CurrentTarget;
}
public void SetPointA(IConnectable target)

View File

@@ -1,6 +1,7 @@
using UnityEngine;
using Script.Gameplay.Interface;
using System;
using Core;
using Script.Gameplay.Input;
namespace Script.Gameplay.Player
@@ -10,8 +11,29 @@ namespace Script.Gameplay.Player
[SerializeField] private FirstPersonRaycaster raycaster; // 新增:第一人称射线检测器
[SerializeField] private bool isEnablePlayerInteraction = true; // 是否启用玩家交互功能
private IInteractable currentTarget; // 被射线命中的当前可交互对象(用于按键交互)
private IInteractable currentTarget;
private IInteractable CurrentTarget
{
get => currentTarget;
set
{
currentTarget = value;
if (currentTarget != null)
{
// 可以在这里添加一些逻辑比如显示交互提示UI
OnGazeEnter?.Invoke(this.gameObject);
}
else
{
// 可以在这里隐藏交互提示UI
OnGazeExit?.Invoke(this.gameObject);
}
}
} // 被射线命中的当前可交互对象(用于按键交互)
private IInteractable previousGazedTarget; // 上一次注视的对象(用于注视进入/离开事件)
public event Action<GameObject> OnGazeEnter;
public event Action<GameObject> OnGazeExit;
void Start()
{
@@ -25,11 +47,13 @@ namespace Script.Gameplay.Player
var input = InputManager.Instance.Input;
input.Player.Interact.performed += ctx =>
{
if (currentTarget != null)
if (CurrentTarget != null)
{
currentTarget.Interact(this.gameObject);
CurrentTarget.Interact(this.gameObject);
}
};
ControllerLocator.Instance.Register(this);
}
void Update()
@@ -63,7 +87,7 @@ namespace Script.Gameplay.Player
previousGazedTarget = hitInteractable;
}
currentTarget = hitInteractable;
CurrentTarget = hitInteractable;
}
public void SetPlayerInteractionEnabled(bool isEnabled)
@@ -73,7 +97,7 @@ namespace Script.Gameplay.Player
{
previousGazedTarget.OnGazeExit(this.gameObject);
previousGazedTarget = null;
currentTarget = null;
CurrentTarget = null;
}
}

View File

@@ -56,6 +56,7 @@ namespace Script.Gameplay.Player
;
ControllerLocator.Instance.Register(this);
OnEnterWatchMode?.Invoke(currentMode);
}
// 实现按Tap键实现在WatchMode 3个模式切换并通过SwitchWatchMode设置正确的相机模式

View File

@@ -0,0 +1,58 @@
using Core;
using UnityEngine;
using Script.Gameplay.Player;
using TMPro;
namespace UI
{
public class PlayerGazeReminder : UIBase
{
[SerializeField] private TMP_Text reminderFirstText;
[SerializeField] private TMP_Text reminderSecondText;
private PlayerInteractorController playerInteractorController;
private PlayerConnectController playerConnectController;
protected override void Awake()
{
base.Awake();
reminderFirstText.text = "";
reminderSecondText.text = "";
ControllerLocator.Instance.TryGetWait<PlayerInteractorController>(OnGetInteractorController);
ControllerLocator.Instance.TryGetWait<PlayerConnectController>(OnGetConnectController);
}
private void OnGetInteractorController(PlayerInteractorController controller)
{
playerInteractorController = controller;
playerInteractorController.OnGazeEnter += HandleInteractGazeEnter;
playerInteractorController.OnGazeExit += HandleInteractGazeExit;
}
private void HandleInteractGazeEnter(GameObject obj)
{
reminderFirstText.text = "Press [F] to interact";
}
private void HandleInteractGazeExit(GameObject obj)
{
reminderFirstText.text = "";
}
private void OnGetConnectController(PlayerConnectController controller)
{
playerConnectController = controller;
playerConnectController.OnGazeEnter += HandleConnectGazeEnter;
playerConnectController.OnGazeExit += HandleConnectGazeExit;
}
private void HandleConnectGazeEnter(GameObject obj)
{
reminderFirstText.text = "Connectable";
}
private void HandleConnectGazeExit(GameObject obj)
{
reminderFirstText.text = "";
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c8ca1a3e5bb84ffdb0f2e03f50e4a077
timeCreated: 1761008641