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:
File diff suppressed because one or more lines are too long
@@ -247,6 +247,7 @@ GameObject:
|
||||
- component: {fileID: 3038514324348343369}
|
||||
- component: {fileID: 8116248334753210478}
|
||||
- component: {fileID: 2674139131311821727}
|
||||
- component: {fileID: 1348220184733719450}
|
||||
m_Layer: 9
|
||||
m_Name: Player
|
||||
m_TagString: Player
|
||||
@@ -759,6 +760,25 @@ MonoBehaviour:
|
||||
- {fileID: 0}
|
||||
_namedPresets: []
|
||||
_sfxPresets: []
|
||||
--- !u!114 &1348220184733719450
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6834103521996502824}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 50b55c5d6664b9f4b8fd1104f272bb8f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_detectRadius: 1.5
|
||||
_interactableLayer:
|
||||
serializedVersion: 2
|
||||
m_Bits: 16777216
|
||||
_inputReader: {fileID: 11400000, guid: 88fadef8bc554e04483edd7418d20aa2, type: 2}
|
||||
_onShowInteractPrompt: {fileID: 11400000, guid: 18bb3dbe7d352dc4a9b62b13e068eb9d, type: 2}
|
||||
_onHideInteractPrompt: {fileID: 11400000, guid: b8cfbee27c2493e4ea75b4158a74a7c4, type: 2}
|
||||
--- !u!1001 &5247753868797769228
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
346
Assets/_Game/Prefabs/UI/UI_WorldInteractPrompt.prefab
Normal file
346
Assets/_Game/Prefabs/UI/UI_WorldInteractPrompt.prefab
Normal file
@@ -0,0 +1,346 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &5902215120526753177
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 306690772086867781}
|
||||
- component: {fileID: 5212307479158172143}
|
||||
- component: {fileID: 4877910466526129253}
|
||||
m_Layer: 0
|
||||
m_Name: Label
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &306690772086867781
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5902215120526753177}
|
||||
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: 1147906227671256622}
|
||||
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: 34, y: 0}
|
||||
m_SizeDelta: {x: 150, y: 60}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5212307479158172143
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5902215120526753177}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4877910466526129253
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5902215120526753177}
|
||||
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: "\u4E92\u52A8"
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: c9ebf78286d2b6345b16575012502acc, type: 2}
|
||||
m_sharedMaterial: {fileID: 1445647621079294416, guid: c9ebf78286d2b6345b16575012502acc, 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: 0
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 30
|
||||
m_fontSizeBase: 30
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 4096
|
||||
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!1 &7659749007432977271
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 729044554010322444}
|
||||
- component: {fileID: 5683374179690853931}
|
||||
- component: {fileID: 1786876915617350021}
|
||||
m_Layer: 0
|
||||
m_Name: KeyIcon
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &729044554010322444
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7659749007432977271}
|
||||
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: 1147906227671256622}
|
||||
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: -62, y: 0}
|
||||
m_SizeDelta: {x: 48, y: 48}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5683374179690853931
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7659749007432977271}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1786876915617350021
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7659749007432977271}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, 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_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &9114100267004531834
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1147906227671256622}
|
||||
- component: {fileID: 8599924487310631316}
|
||||
- component: {fileID: 4553354974615421263}
|
||||
- component: {fileID: 4133494467148546095}
|
||||
- component: {fileID: 7694003554034453721}
|
||||
- component: {fileID: 1416140487490777574}
|
||||
m_Layer: 0
|
||||
m_Name: UI_WorldInteractPrompt
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1147906227671256622
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9114100267004531834}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0.012, y: 0.012, z: 0.012}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 729044554010322444}
|
||||
- {fileID: 306690772086867781}
|
||||
m_Father: {fileID: 0}
|
||||
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: 220, y: 70}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!223 &8599924487310631316
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9114100267004531834}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 2
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: -1053558941
|
||||
m_SortingOrder: 100
|
||||
m_TargetDisplay: 0
|
||||
--- !u!114 &4553354974615421263
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9114100267004531834}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 4
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!114 &4133494467148546095
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9114100267004531834}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!225 &7694003554034453721
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9114100267004531834}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 0
|
||||
m_Interactable: 0
|
||||
m_BlocksRaycasts: 0
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!114 &1416140487490777574
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9114100267004531834}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 845b4e4cb1d22f54e8b0910d937650a8, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_canvasGroup: {fileID: 7694003554034453721}
|
||||
_keyIcon: {fileID: 1786876915617350021}
|
||||
_labelText: {fileID: 4877910466526129253}
|
||||
_actionName: Interact
|
||||
_fadeInDuration: 0.12
|
||||
_fadeOutDuration: 0.08
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d72c5939060bc4440a14d0c638985e50
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -933,60 +933,6 @@ Canvas:
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 10
|
||||
m_TargetDisplay: 0
|
||||
--- !u!1 &139460216
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 139460217}
|
||||
- component: {fileID: 139460218}
|
||||
m_Layer: 0
|
||||
m_Name: InteractPrompt
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &139460217
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 139460216}
|
||||
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: 801594610}
|
||||
m_Father: {fileID: 906750105}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0}
|
||||
m_AnchorMax: {x: 0.5, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 140}
|
||||
m_SizeDelta: {x: 320, y: 64}
|
||||
m_Pivot: {x: 0.5, y: 0}
|
||||
--- !u!114 &139460218
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 139460216}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 85bdb69d66e546f49b6c89941beda368, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_keyIcon: {fileID: 653850731}
|
||||
_labelText: {fileID: 2040427193}
|
||||
_root: {fileID: 801594609}
|
||||
_onShowPrompt: {fileID: 0}
|
||||
_onHidePrompt: {fileID: 0}
|
||||
--- !u!1 &141610520
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -4015,81 +3961,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 647963841}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &653850729
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 653850730}
|
||||
- component: {fileID: 653850732}
|
||||
- component: {fileID: 653850731}
|
||||
m_Layer: 0
|
||||
m_Name: KeyIcon
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &653850730
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 653850729}
|
||||
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: 801594610}
|
||||
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 &653850731
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 653850729}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, 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_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!222 &653850732
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 653850729}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &660965037
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -5421,43 +5292,6 @@ MonoBehaviour:
|
||||
_lensConfig: {fileID: 11400000, guid: 12fec951ce5cc3d499b00e38b5dfa14a, type: 2}
|
||||
_onPlayerSpawned: {fileID: 11400000, guid: 7e2c7e614f6627b449a244ab44443adf, type: 2}
|
||||
_showDebugOverlay: 0
|
||||
--- !u!1 &801594609
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 801594610}
|
||||
m_Layer: 0
|
||||
m_Name: Root
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
--- !u!224 &801594610
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 801594609}
|
||||
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: 653850730}
|
||||
- {fileID: 2040427192}
|
||||
m_Father: {fileID: 139460217}
|
||||
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!1 &814710351
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -6091,7 +5925,6 @@ RectTransform:
|
||||
- {fileID: 1223335663}
|
||||
- {fileID: 878269100}
|
||||
- {fileID: 1915171111}
|
||||
- {fileID: 139460217}
|
||||
- {fileID: 1638453482}
|
||||
- {fileID: 834642924}
|
||||
- {fileID: 1162213833}
|
||||
@@ -6129,7 +5962,7 @@ MonoBehaviour:
|
||||
- {fileID: 1981898190}
|
||||
- {fileID: 1670690207}
|
||||
- {fileID: 38682127}
|
||||
_interactPromptWidget: {fileID: 139460218}
|
||||
_interactPromptWidget: {fileID: 0}
|
||||
_onHPChanged: {fileID: 11400000, guid: b5a2264ba6cf6544ebc53a67b5074d89, type: 2}
|
||||
_onMaxHPChanged: {fileID: 11400000, guid: 34f14924fad74884aac7b2bd6fbad649, type: 2}
|
||||
_onSoulPowerChanged: {fileID: 11400000, guid: 3694bb359f4ba5f40a6cba067478578c, type: 2}
|
||||
@@ -15739,140 +15572,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2037445550}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &2040427191
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2040427192}
|
||||
- component: {fileID: 2040427194}
|
||||
- component: {fileID: 2040427193}
|
||||
m_Layer: 0
|
||||
m_Name: LabelText
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2040427192
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2040427191}
|
||||
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: 801594610}
|
||||
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: 200, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &2040427193
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2040427191}
|
||||
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: "\u4E92\u52A8"
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: c9ebf78286d2b6345b16575012502acc, type: 2}
|
||||
m_sharedMaterial: {fileID: 1445647621079294416, guid: c9ebf78286d2b6345b16575012502acc, 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: 0
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 36
|
||||
m_fontSizeBase: 36
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
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 &2040427194
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2040427191}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &2073048742
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 交互提示事件负载。
|
||||
/// 由 InteractableDetector 广播,包含触发动作名称和显示文本,
|
||||
/// UI 层(InteractPromptWidget)据此查询图标并显示提示。
|
||||
/// </summary>
|
||||
public readonly struct InteractPromptEvent
|
||||
{
|
||||
/// <summary>InputSystem Action 名称,如 "Interact"。用于查询按键图标。</summary>
|
||||
public readonly string ActionName;
|
||||
|
||||
/// <summary>交互物提供的说明文本,如 "对话"、"存档"、"传送"。</summary>
|
||||
public readonly string LabelText;
|
||||
|
||||
public InteractPromptEvent(string actionName, string labelText)
|
||||
{
|
||||
ActionName = actionName;
|
||||
LabelText = labelText;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "BaseGames/Events/InteractPrompt")]
|
||||
public class InteractPromptEventChannelSO : BaseEventChannelSO<InteractPromptEvent> { }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e6db212f7619344588f054af0c6330a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/_Game/Scripts/Core/Interaction.meta
Normal file
8
Assets/_Game/Scripts/Core/Interaction.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af30250f65fc2f745a3b4089172739be
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Assets/_Game/Scripts/Core/Interaction/IInteractPromptView.cs
Normal file
21
Assets/_Game/Scripts/Core/Interaction/IInteractPromptView.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace BaseGames.Core.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// 世界空间交互提示视图接口。
|
||||
/// <para>
|
||||
/// 由可交互物上的子节点组件实现(如 <c>WorldInteractPrompt</c>),挂在世界空间里跟随物体显示。
|
||||
/// 玩家身上的交互探测器在「最近可交互物」变化时调用 <see cref="Show"/> / <see cref="Hide"/> 驱动其显隐。
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 接口放在 Core 程序集,使「世界检测」(World)与「UI 表现」(UI)解耦,避免程序集互相引用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IInteractPromptView
|
||||
{
|
||||
/// <summary>显示提示。<paramref name="label"/> 为交互动作文字(来自 IInteractable.InteractPrompt)。</summary>
|
||||
void Show(string label);
|
||||
|
||||
/// <summary>隐藏提示。</summary>
|
||||
void Hide();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85bdb69d66e546f49b6c89941beda368
|
||||
guid: 8559c1ac8ee8243459db9370032e789a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1,7 +1,8 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UI;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.UI;
|
||||
|
||||
namespace BaseGames.Dialogue
|
||||
{
|
||||
@@ -26,10 +27,9 @@ namespace BaseGames.Dialogue
|
||||
[SerializeField] private TMP_Text _label;
|
||||
|
||||
[Header("按键图标")]
|
||||
[Tooltip("键盘/鼠标设备激活时使用的按键图标 Sprite。")]
|
||||
[SerializeField] private Sprite _keyboardIcon;
|
||||
[Tooltip("手柄设备激活时使用的按键图标 Sprite。")]
|
||||
[SerializeField] private Sprite _gamepadIcon;
|
||||
[Tooltip("用于查询按键图标的输入动作名(与 InputReader 的 Action 对应)。\n" +
|
||||
"图标走 IInputIconService,随设备切换 / 改键自动刷新。")]
|
||||
[SerializeField] private string _actionName = "Interact";
|
||||
|
||||
[Header("位置与动画")]
|
||||
[Tooltip("相对于本组件 transform 的世界空间偏移。调整此值可控制气泡与 NPC 的相对位置。")]
|
||||
@@ -42,10 +42,11 @@ namespace BaseGames.Dialogue
|
||||
[SerializeField] [Min(0f)] private float _fadeOutDuration = 0.08f;
|
||||
|
||||
// ── 运行时状态 ────────────────────────────────────────────────────────
|
||||
private InteractableNPC _npc;
|
||||
private bool _visible;
|
||||
private float _alpha;
|
||||
private Camera _cam;
|
||||
private InteractableNPC _npc;
|
||||
private bool _visible;
|
||||
private float _alpha;
|
||||
private Camera _cam;
|
||||
private IInputIconService _iconService;
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -69,6 +70,11 @@ namespace BaseGames.Dialogue
|
||||
_npc.PlayerEnteredRange -= OnPlayerEntered;
|
||||
_npc.PlayerExitedRange -= OnPlayerExited;
|
||||
}
|
||||
if (_iconService != null)
|
||||
{
|
||||
_iconService.OnIconSetChanged -= UpdateIcon;
|
||||
_iconService = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -151,9 +157,19 @@ namespace BaseGames.Dialogue
|
||||
|
||||
private void UpdateIcon()
|
||||
{
|
||||
if (_icon == null) return;
|
||||
bool isGamepad = Gamepad.current != null && Gamepad.current.enabled;
|
||||
_icon.sprite = isGamepad ? _gamepadIcon : _keyboardIcon;
|
||||
if (_icon == null || string.IsNullOrEmpty(_actionName)) return;
|
||||
|
||||
// 延迟绑定:首次需要时获取服务(确保 ServiceLocator 已初始化),并订阅设备/改键刷新
|
||||
if (_iconService == null)
|
||||
{
|
||||
_iconService = ServiceLocator.GetOrDefault<IInputIconService>();
|
||||
if (_iconService != null)
|
||||
_iconService.OnIconSetChanged += UpdateIcon;
|
||||
}
|
||||
|
||||
var sprite = _iconService?.GetActionIcon(_actionName);
|
||||
if (sprite != null) { _icon.sprite = sprite; _icon.enabled = true; }
|
||||
else { _icon.enabled = false; }
|
||||
}
|
||||
|
||||
private void ApplyAlpha(float a)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace BaseGames.Editor.Input
|
||||
/// ① 当前 Action × 当前设备的绑定路径 + Sprite 字段
|
||||
/// ② 48px 大图预览
|
||||
/// ③ 所有设备快览行(4 行)
|
||||
/// ④ 模拟 InteractPromptWidget 外观预览
|
||||
/// ④ 模拟交互提示 外观预览
|
||||
///
|
||||
/// 菜单:BaseGames / Input Icon Studio (priority=55)
|
||||
/// </summary>
|
||||
@@ -621,7 +621,7 @@ namespace BaseGames.Editor.Input
|
||||
var effectivePath = GetEffectivePath(_selectedAction);
|
||||
var sprite = (iconSet != null && effectivePath != null) ? iconSet.GetIcon(effectivePath) : null;
|
||||
|
||||
// 模拟 InteractPromptWidget 的外观
|
||||
// 模拟交互提示 的外观
|
||||
var mockup = new VisualElement();
|
||||
mockup.style.flexDirection = FlexDirection.Row;
|
||||
mockup.style.alignItems = Align.Center;
|
||||
|
||||
@@ -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._animConfig(PLY_PlayerAnimationConfig)");
|
||||
if (statsConfig == null) report.Add("★ 需创建并绑定:PlayerStats._config(PlayerStatsSO)");
|
||||
if (inputReader == null) report.Add("★ 需手动绑定:PlayerController._inputReader / FormController._input / SkillManager._input(InputReaderSO)");
|
||||
@@ -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(淡入)字段。");
|
||||
|
||||
@@ -87,21 +87,8 @@ namespace BaseGames.Editor.UI
|
||||
le.preferredHeight = 40f;
|
||||
}
|
||||
|
||||
// ── 交互提示(InteractPrompt)─────────────────────────────────────
|
||||
GameObject interactPromptGo = GetOrCreateChild(hudRootGo.transform, "InteractPrompt").gameObject;
|
||||
InteractPromptWidget interactWidget = GetOrAddComponent<InteractPromptWidget>(interactPromptGo);
|
||||
|
||||
GameObject promptRootGo = GetOrCreateChild(interactPromptGo.transform, "Root").gameObject;
|
||||
GameObject promptKeyIconGo = GetOrCreateChild(promptRootGo.transform, "KeyIcon").gameObject;
|
||||
Image keyIcon = GetOrAddComponent<Image>(promptKeyIconGo);
|
||||
GameObject promptLabelGo = GetOrCreateChild(promptRootGo.transform, "LabelText").gameObject;
|
||||
TMP_Text promptLabel = GetOrAddComponent<TextMeshProUGUI>(promptLabelGo);
|
||||
promptLabel.text = "互动";
|
||||
promptRootGo.SetActive(false);
|
||||
|
||||
AssignRef(interactWidget, "_keyIcon", keyIcon);
|
||||
AssignRef(interactWidget, "_labelText", promptLabel);
|
||||
AssignRef(interactWidget, "_root", promptRootGo);
|
||||
// 交互提示已改为「每个交互物自带的世界空间提示」(WorldInteractPrompt),
|
||||
// 不再放在屏幕固定 HUD 上。由交互物脚手架在各自预制体/场景物体上创建。
|
||||
|
||||
// ── 法术槽(SpellSlot)───────────────────────────────────────────
|
||||
GameObject spellSlotGo = GetOrCreateChild(hudRootGo.transform, "SpellSlot").gameObject;
|
||||
@@ -209,7 +196,6 @@ namespace BaseGames.Editor.UI
|
||||
AssignRef(hudCtrl, "_spiritGaugeFill", spiritFill);
|
||||
AssignRef(hudCtrl, "_lingZhuText", lingZhuText);
|
||||
AssignRef(hudCtrl, "_springContainer", springContainerGo.transform);
|
||||
AssignRef(hudCtrl, "_interactPromptWidget", interactWidget);
|
||||
AssignArrayRefs(hudCtrl, "_formIcons", formImages, report);
|
||||
|
||||
// ── HP 格子 / 回春图标 Prefab(自动创建并绑定,无需手工补)──────────
|
||||
@@ -229,9 +215,6 @@ namespace BaseGames.Editor.UI
|
||||
AssignAsset(hudCtrl, "_onSpringChargesChanged", report, false, "EVT_SpringChargesChanged", "EVT_SpringCharges");
|
||||
AssignAsset(hudCtrl, "_onFormChanged", report, false, "EVT_FormChanged");
|
||||
|
||||
AssignAsset(interactWidget, "_onShowPrompt", report, false, "EVT_ShowInteractPrompt", "EVT_InteractPromptShow");
|
||||
AssignAsset(interactWidget, "_onHidePrompt", report, false, "EVT_HideInteractPrompt", "EVT_InteractPromptHide");
|
||||
|
||||
AssignAsset(bossHPBar, "_onBossFightToggled", report, false, "EVT_BossFightToggled", "EVT_BossFightStarted");
|
||||
AssignAsset(bossHPBar, "_onBossHPChanged", report, false, "EVT_BossHPChanged");
|
||||
AssignAsset(bossHPBar, "_onBossHPMaxSet", report, false, "EVT_BossHPMaxSet");
|
||||
@@ -266,8 +249,6 @@ namespace BaseGames.Editor.UI
|
||||
// 右下角:法术槽 + 工具栏
|
||||
SetRect(spellSlotGo.transform, BottomRight, BottomRight, new Vector2(-40f, 40f), new Vector2(90f, 90f));
|
||||
SetRect(toolHUDGo.transform, BottomRight, BottomRight, new Vector2(-150f, 40f), new Vector2(180f, 90f));
|
||||
// 底部居中:交互提示
|
||||
SetRect(interactPromptGo.transform, BottomCenter, BottomCenter, new Vector2(0f, 140f), new Vector2(320f, 64f));
|
||||
|
||||
// ── 手工步骤说明 ──────────────────────────────────────────────────
|
||||
// _hpCellPrefab / _springIconPrefab 已自动创建并绑定(占位红/青方块,美术可替换)。
|
||||
|
||||
8
Assets/_Game/Scripts/Input/Icons.meta
Normal file
8
Assets/_Game/Scripts/Input/Icons.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd6a85a37d5b2be4bb80f145d8990ba5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -30,9 +30,7 @@ namespace BaseGames.UI.HUD
|
||||
[Header("Form")]
|
||||
[SerializeField] private Image[] _formIcons;
|
||||
|
||||
[Header("Interact Prompt")]
|
||||
[Tooltip("独立 Widget 组件负责渲染图标+文本,HUDController 仅保留引用供编辑器配置检查")]
|
||||
[SerializeField] private InteractPromptWidget _interactPromptWidget;
|
||||
// 交互提示已改为「每个交互物自带的世界空间提示」(WorldInteractPrompt),不再由 HUD 承载。
|
||||
|
||||
[Header("Event Channels - Subscribe")]
|
||||
[SerializeField] private IntEventChannelSO _onHPChanged;
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.UI.HUD
|
||||
{
|
||||
/// <summary>
|
||||
/// 交互提示 Widget。
|
||||
///
|
||||
/// 职责:
|
||||
/// • 订阅 InteractPromptEventChannelSO 显示/隐藏提示
|
||||
/// • 显示按键图标(Image)+ 动作文本(TMP_Text)
|
||||
/// • 监听 IInputIconService.OnIconSetChanged,在设备切换或改键后自动刷新图标
|
||||
///
|
||||
/// 布置方式:放在 HUD Canvas 下,引用对应的事件频道 SO 资产。
|
||||
/// 不依赖 HUDController,可独立使用。
|
||||
/// </summary>
|
||||
public sealed class InteractPromptWidget : MonoBehaviour
|
||||
{
|
||||
[Header("UI 引用")]
|
||||
[SerializeField] private Image _keyIcon;
|
||||
[SerializeField] private TMP_Text _labelText;
|
||||
[Tooltip("整个提示根节点,控制显示/隐藏")]
|
||||
[SerializeField] private GameObject _root;
|
||||
|
||||
[Header("Event Channels")]
|
||||
[SerializeField] private InteractPromptEventChannelSO _onShowPrompt;
|
||||
[SerializeField] private VoidEventChannelSO _onHidePrompt;
|
||||
|
||||
// ── 运行时状态 ────────────────────────────────────────────────────────
|
||||
private IInputIconService _iconService;
|
||||
private string _currentActionName;
|
||||
private readonly CompositeDisposable _subs = new();
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// ServiceLocator 可能在此组件 OnEnable 时尚未注册(执行顺序问题),
|
||||
// 延迟到 ShowPrompt 首次调用时再获取,确保服务可用
|
||||
_onShowPrompt?.Subscribe(ShowPrompt).AddTo(_subs);
|
||||
_onHidePrompt?.Subscribe(HidePrompt).AddTo(_subs);
|
||||
|
||||
HidePrompt();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_subs.Clear();
|
||||
UnsubscribeFromIconService();
|
||||
}
|
||||
|
||||
// ── Handlers ──────────────────────────────────────────────────────────
|
||||
|
||||
private void ShowPrompt(InteractPromptEvent evt)
|
||||
{
|
||||
_currentActionName = evt.ActionName;
|
||||
|
||||
// 延迟绑定:首次显示时获取服务(确保 ServiceLocator 已初始化)
|
||||
if (_iconService == null)
|
||||
{
|
||||
_iconService = ServiceLocator.GetOrDefault<IInputIconService>();
|
||||
if (_iconService != null)
|
||||
_iconService.OnIconSetChanged += RefreshIcon;
|
||||
}
|
||||
|
||||
if (_labelText != null)
|
||||
_labelText.text = evt.LabelText;
|
||||
|
||||
RefreshIcon();
|
||||
|
||||
if (_root != null)
|
||||
_root.SetActive(true);
|
||||
else
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
private void HidePrompt()
|
||||
{
|
||||
_currentActionName = null;
|
||||
|
||||
if (_root != null)
|
||||
_root.SetActive(false);
|
||||
else
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// ── Icon Refresh ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>设备切换或改键后刷新图标。由 IInputIconService.OnIconSetChanged 调用。</summary>
|
||||
private void RefreshIcon()
|
||||
{
|
||||
if (_keyIcon == null || string.IsNullOrEmpty(_currentActionName)) return;
|
||||
|
||||
var sprite = _iconService?.GetActionIcon(_currentActionName);
|
||||
if (sprite != null)
|
||||
{
|
||||
_keyIcon.sprite = sprite;
|
||||
_keyIcon.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 找不到图标时隐藏图标格,避免显示错误占位图
|
||||
_keyIcon.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromIconService()
|
||||
{
|
||||
if (_iconService != null)
|
||||
{
|
||||
_iconService.OnIconSetChanged -= RefreshIcon;
|
||||
_iconService = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Assets/_Game/Scripts/UI/World.meta
Normal file
8
Assets/_Game/Scripts/UI/World.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e5e071d3ce62d74284bcd89d5bbe6d4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
141
Assets/_Game/Scripts/UI/World/WorldInteractPrompt.cs
Normal file
141
Assets/_Game/Scripts/UI/World/WorldInteractPrompt.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Interaction;
|
||||
|
||||
namespace BaseGames.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 世界空间交互提示(按键图标 + 文字),挂在可交互物的子节点上,跟随物体显示。
|
||||
///
|
||||
/// 职责:
|
||||
/// • 实现 <see cref="IInteractPromptView"/>,由玩家身上的交互探测器在进入/离开最近可交互物时驱动显隐。
|
||||
/// • 显示按键图标(Image)+ 动作文本(TMP_Text);图标走 <see cref="IInputIconService"/>,
|
||||
/// 随设备切换 / 改键自动刷新(与全局一致)。
|
||||
/// • 通过 <see cref="CanvasGroup"/> 做淡入 / 淡出。
|
||||
///
|
||||
/// 布置方式:作为可交互物(门 / 存档点 / 商店 / 传送点…)的子节点,
|
||||
/// 用世界空间 Canvas 承载。气泡相对物体的位置 = 本节点的 Transform,
|
||||
/// 直接在场景里拖动本子节点即可单独微调每个物体的提示锚点。
|
||||
/// </summary>
|
||||
public sealed class WorldInteractPrompt : MonoBehaviour, IInteractPromptView
|
||||
{
|
||||
[Header("UI 引用")]
|
||||
[Tooltip("淡入淡出用 CanvasGroup(一般挂在本节点的 Canvas 上)。")]
|
||||
[SerializeField] private CanvasGroup _canvasGroup;
|
||||
[Tooltip("按键图标 Image。由 IInputIconService 按当前设备填充。")]
|
||||
[SerializeField] private Image _keyIcon;
|
||||
[Tooltip("提示文字 TMP_Text。显示 IInteractable.InteractPrompt 的当前值。")]
|
||||
[SerializeField] private TMP_Text _labelText;
|
||||
|
||||
[Header("按键动作")]
|
||||
[Tooltip("用于查询按键图标的输入动作名(与 InputReader 的 Action 对应)。")]
|
||||
[SerializeField] private string _actionName = "Interact";
|
||||
|
||||
[Header("淡入淡出")]
|
||||
[Tooltip("淡入持续时间(秒)。0 = 立即显示。")]
|
||||
[SerializeField] [Min(0f)] private float _fadeInDuration = 0.12f;
|
||||
[Tooltip("淡出持续时间(秒)。0 = 立即隐藏。")]
|
||||
[SerializeField] [Min(0f)] private float _fadeOutDuration = 0.08f;
|
||||
|
||||
// ── 运行时状态 ────────────────────────────────────────────────────────
|
||||
private IInputIconService _iconService;
|
||||
private bool _visible;
|
||||
private float _alpha;
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_alpha = 0f;
|
||||
_visible = false;
|
||||
ApplyAlpha(0f);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
UnsubscribeFromIconService();
|
||||
_visible = false;
|
||||
_alpha = 0f;
|
||||
ApplyAlpha(0f);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 完全隐藏且无需继续淡出时,跳过
|
||||
if (!_visible && _alpha <= 0f) return;
|
||||
|
||||
float target = _visible ? 1f : 0f;
|
||||
if (!Mathf.Approximately(_alpha, target))
|
||||
{
|
||||
float dur = _visible ? _fadeInDuration : _fadeOutDuration;
|
||||
float speed = dur > 0f ? Time.unscaledDeltaTime / dur : 1f;
|
||||
_alpha = Mathf.MoveTowards(_alpha, target, speed);
|
||||
ApplyAlpha(_alpha);
|
||||
}
|
||||
}
|
||||
|
||||
// ── IInteractPromptView ───────────────────────────────────────────────
|
||||
|
||||
public void Show(string label)
|
||||
{
|
||||
if (_labelText != null)
|
||||
_labelText.text = label;
|
||||
|
||||
// 延迟绑定:首次显示时获取服务(确保 ServiceLocator 已初始化)
|
||||
if (_iconService == null)
|
||||
{
|
||||
_iconService = ServiceLocator.GetOrDefault<IInputIconService>();
|
||||
if (_iconService != null)
|
||||
_iconService.OnIconSetChanged += RefreshIcon;
|
||||
}
|
||||
|
||||
RefreshIcon();
|
||||
_visible = true;
|
||||
}
|
||||
|
||||
public void Hide() => _visible = false;
|
||||
|
||||
// ── Icon ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>设备切换或改键后刷新图标。由 IInputIconService.OnIconSetChanged 调用。</summary>
|
||||
private void RefreshIcon()
|
||||
{
|
||||
if (_keyIcon == null || string.IsNullOrEmpty(_actionName)) return;
|
||||
|
||||
var sprite = _iconService?.GetActionIcon(_actionName);
|
||||
if (sprite != null)
|
||||
{
|
||||
_keyIcon.sprite = sprite;
|
||||
_keyIcon.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyIcon.enabled = false; // 找不到图标时隐藏图标格,避免错误占位图
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromIconService()
|
||||
{
|
||||
if (_iconService != null)
|
||||
{
|
||||
_iconService.OnIconSetChanged -= RefreshIcon;
|
||||
_iconService = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 辅助 ──────────────────────────────────────────────────────────────
|
||||
|
||||
private void ApplyAlpha(float a)
|
||||
{
|
||||
if (_canvasGroup != null)
|
||||
{
|
||||
_canvasGroup.alpha = a;
|
||||
// 提示纯展示,不接收交互;同时彻底关闭射线避免遮挡
|
||||
_canvasGroup.interactable = false;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9eccce8fdbd936b46a467d078957a387
|
||||
guid: 845b4e4cb1d22f54e8b0910d937650a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1,21 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Core.Interaction;
|
||||
using BaseGames.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.World
|
||||
{
|
||||
/// <summary>
|
||||
/// 挂在 Player 上,检测附近可交互物,驱动 UI 提示显示/隐藏。
|
||||
/// 挂在 Player 上,检测附近可交互物,并驱动其世界空间提示(<see cref="IInteractPromptView"/>)显隐。
|
||||
/// 通过 InputReaderSO.InteractEvent 绑定交互输入。
|
||||
/// <para>
|
||||
/// 提示表现是「每个交互物自带」的世界空间子节点(跟随物体、可单独配样式),
|
||||
/// 本组件只负责「检测最近可交互物」并通知它显示/隐藏,不持有任何 UI。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class InteractableDetector : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private float _detectRadius = 1.5f;
|
||||
[SerializeField] private LayerMask _interactableLayer;
|
||||
[SerializeField] private InputReaderSO _inputReader;
|
||||
[SerializeField] private InteractPromptEventChannelSO _onShowInteractPrompt;
|
||||
[SerializeField] private VoidEventChannelSO _onHideInteractPrompt;
|
||||
[SerializeField] private float _detectRadius = 1.5f;
|
||||
[SerializeField] private LayerMask _interactableLayer;
|
||||
[SerializeField] private InputReaderSO _inputReader;
|
||||
|
||||
private IInteractable _nearest;
|
||||
private IInteractable _previousNearest;
|
||||
@@ -26,6 +28,9 @@ namespace BaseGames.World
|
||||
// Collider → IInteractable 缓存,避免 FindNearest 每帧重复 GetComponentInParent
|
||||
private readonly Dictionary<Collider2D, IInteractable> _componentCache = new();
|
||||
|
||||
// IInteractable → 其世界空间提示视图缓存(可能为 null,表示该物体未配置提示)
|
||||
private readonly Dictionary<IInteractable, IInteractPromptView> _promptCache = new();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_inputReader.InteractEvent += TryInteract;
|
||||
@@ -34,7 +39,12 @@ namespace BaseGames.World
|
||||
private void OnDisable()
|
||||
{
|
||||
_inputReader.InteractEvent -= TryInteract;
|
||||
// 离场前隐藏当前提示,防止跨场景残留
|
||||
ResolvePrompt(_previousNearest)?.Hide();
|
||||
_componentCache.Clear(); // 清理缓存,防止跨场景持有旧引用
|
||||
_promptCache.Clear();
|
||||
_previousNearest = null;
|
||||
_nearest = null;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -48,13 +58,13 @@ namespace BaseGames.World
|
||||
if (_previousNearest != null)
|
||||
{
|
||||
_previousNearest.OnPlayerExitRange();
|
||||
_onHideInteractPrompt?.Raise();
|
||||
ResolvePrompt(_previousNearest)?.Hide();
|
||||
}
|
||||
|
||||
if (_nearest != null)
|
||||
{
|
||||
_nearest.OnPlayerEnterRange(transform);
|
||||
_onShowInteractPrompt?.Raise(new InteractPromptEvent("Interact", _nearest.InteractPrompt));
|
||||
ResolvePrompt(_nearest)?.Show(_nearest.InteractPrompt);
|
||||
}
|
||||
|
||||
_previousNearest = _nearest;
|
||||
@@ -98,6 +108,17 @@ namespace BaseGames.World
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>解析交互物的世界空间提示视图(在其子节点上查找一次并缓存;无则记 null)。</summary>
|
||||
private IInteractPromptView ResolvePrompt(IInteractable interactable)
|
||||
{
|
||||
if (interactable == null) return null;
|
||||
if (_promptCache.TryGetValue(interactable, out var view)) return view;
|
||||
|
||||
view = (interactable as Component)?.GetComponentInChildren<IInteractPromptView>(true);
|
||||
_promptCache[interactable] = view;
|
||||
return view;
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
|
||||
Reference in New Issue
Block a user