Refactor interaction prompt system to use world space prompts

- Removed the InteractPromptWidget from HUD and its references in HUDController.
- Introduced IInteractPromptView interface for world space interaction prompts.
- Implemented WorldInteractPrompt class to manage display of interaction prompts in world space.
- Updated InteractableDetector to handle showing/hiding of world space prompts based on player proximity to interactable objects.
- Created a new prefab for UI_WorldInteractPrompt to facilitate the new interaction prompt system.
This commit is contained in:
2026-06-10 14:14:08 +08:00
parent 32566020c7
commit a1f54b68e6
23 changed files with 6085 additions and 554 deletions

View File

@@ -224,6 +224,14 @@ namespace BaseGames.Editor
if (equipmentConfig != null) AssignReference(equipmentManager, "_config", equipmentConfig, report);
if (charmCatalog != null) AssignReference(equipmentManager, "_charmCatalog", charmCatalog, report);
// ── 交互探测器(检测最近可交互物 + 交互键调用 IInteractable.Interact + 驱动其世界空间提示)──
// 扫描 TriggerZone 层上的可交互物(门/存档点/商店/传送点…)。
// 提示表现由各交互物自带的世界空间子节点WorldInteractPrompt负责本组件不持有 UI。
InteractableDetector interactDetector = GetOrAddComponent<InteractableDetector>(root);
AssignLayerMask(interactDetector, "_interactableLayer", new[] { "TriggerZone" }, report);
if (inputReader != null)
AssignReference(interactDetector, "_inputReader", inputReader, report);
if (animConfig == null) report.Add("★ 需创建并绑定PlayerController._animConfigPLY_PlayerAnimationConfig");
if (statsConfig == null) report.Add("★ 需创建并绑定PlayerStats._configPlayerStatsSO");
if (inputReader == null) report.Add("★ 需手动绑定PlayerController._inputReader / FormController._input / SkillManager._inputInputReaderSO");
@@ -1687,6 +1695,9 @@ namespace BaseGames.Editor
AssignAsset(savePoint, "_onSceneLoaded", report, false, "EVT_SceneLoaded");
AssignAsset(savePoint, "_onSavePointActivated", report, false, "EVT_SavePointActivated");
// 世界空间交互提示
AttachInteractPrompt(go, 1.3f, report);
report.Add("已自动生成唯一 _savePointId可按需改为语义化 ID如 SP_Forest_Entrance。");
Selection.activeGameObject = go;
@@ -1747,6 +1758,30 @@ namespace BaseGames.Editor
MarkDirtyAndLog("Room Transition", go, report);
}
/// <summary>
/// 在交互物上挂载世界空间交互提示UI_WorldInteractPrompt 预制体),作为子节点跟随物体显示。
/// 提示显隐由玩家身上的 InteractableDetector 在进入/离开最近可交互物时驱动(按 IInteractPromptView 接口)。
/// 幂等已存在同名子节点则跳过。localY 为气泡相对物体的高度,可后续在场景中拖动该子节点单独微调。
/// </summary>
private static void AttachInteractPrompt(GameObject host, float localY, List<string> report)
{
if (host.transform.Find("InteractPrompt") != null) return;
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(
"Assets/_Game/Prefabs/UI/UI_WorldInteractPrompt.prefab");
if (prefab == null)
{
report.Add("★ 未找到 UI_WorldInteractPrompt 预制体,已跳过交互提示挂载(检查 Assets/_Game/Prefabs/UI/)。");
return;
}
var go = (GameObject)PrefabUtility.InstantiatePrefab(prefab, host.transform);
go.name = "InteractPrompt";
go.transform.localPosition = new Vector3(0f, localY, 0f);
Undo.RegisterCreatedObjectUndo(go, "Attach InteractPrompt");
report.Add("已挂载世界空间交互提示InteractPrompt 子节点)。拖动它可单独微调气泡位置/样式。");
}
[MenuItem("BaseGames/Scene/Place/Door Transition", priority = 141)]
public static void PlaceDoorTransition()
{
@@ -1774,6 +1809,9 @@ namespace BaseGames.Editor
AssignBool(door, "_autoTrigger", false); // 默认需玩家按交互键
AssignReference(door, "_animancer", animancer, report);
// 世界空间交互提示(按交互键模式默认显示)
AttachInteractPrompt(go, 1.6f, report);
AssignAsset(door, "_onSceneLoadRequest", report, false, "EVT_SceneLoadRequest");
report.Add("填写 _targetSceneAddress目标场景 Addressable Key与 _targetTransitionId目标 PlayerSpawnPoint 的 _transitionId。");
@@ -1843,6 +1881,10 @@ namespace BaseGames.Editor
AssignReference(doorA, "_linkedDoor", doorB, report);
AssignReference(doorB, "_linkedDoor", doorA, report);
// 世界空间交互提示(仅在该门切到按交互键模式 _autoTrigger=false 时才会显示)
AttachInteractPrompt(goA, 1.6f, report);
AttachInteractPrompt(goB, 1.6f, report);
report.Add("LinkedDoor_A ↔ LinkedDoor_B 已互相绑定,统一挂在 LinkedDoorPair 父节点下。");
report.Add("将两扇门移到场景中正确位置后,拖动各自的子节点 SpawnPoint 调整玩家传送到达位置。");
report.Add("转场效果:在各门 GameObject 上添加 SceneFeedback 组件并绑定 MMF_Player如淡入淡出再将其拖入 _transitionOut淡出和 _transitionIn淡入字段。");