상호작용이 불가능한 상태에서도 모달 UI가 나타나도록 변경

This commit is contained in:
2026-02-12 14:49:18 +09:00
parent 2bb4797ec5
commit b32cc925e3
5 changed files with 124 additions and 14 deletions

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ Assets/_Recovery
Assets/_Recovery.meta Assets/_Recovery.meta
GameData/Backups GameData/Backups
.claude/settings.local.json .claude/settings.local.json
AGENTS.md

View File

@@ -59,7 +59,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
GlobalObjectIdHash: 4211758632 GlobalObjectIdHash: 1360081626
InScenePlacedSourceGlobalObjectIdHash: 4211758632 InScenePlacedSourceGlobalObjectIdHash: 4211758632
DeferredDespawnTick: 0 DeferredDespawnTick: 0
Ownership: 1 Ownership: 1
@@ -714,6 +714,18 @@ PrefabInstance:
serializedVersion: 3 serializedVersion: 3
m_TransformParent: {fileID: 1710962577221812646} m_TransformParent: {fileID: 1710962577221812646}
m_Modifications: m_Modifications:
- target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_AnchorMax.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3} - target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_AnchorMax.y propertyPath: m_AnchorMax.y
value: 0 value: 0
@@ -724,7 +736,11 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3} - target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_SizeDelta.x propertyPath: m_SizeDelta.x
value: 4 value: 500
objectReference: {fileID: 0}
- target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_SizeDelta.y
value: 50
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3} - target: {fileID: 1325989084779099817, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_AnchoredPosition.x propertyPath: m_AnchoredPosition.x
@@ -834,6 +850,10 @@ PrefabInstance:
propertyPath: m_Name propertyPath: m_Name
value: ModalPanel value: ModalPanel
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 7839034454177399683, guid: 274613c415998a647a86a5e09950ce41, type: 3}
propertyPath: m_fontStyle
value: 1
objectReference: {fileID: 0}
m_RemovedComponents: [] m_RemovedComponents: []
m_RemovedGameObjects: [] m_RemovedGameObjects: []
m_AddedGameObjects: [] m_AddedGameObjects: []
@@ -878,6 +898,8 @@ MonoBehaviour:
modalPanel: {fileID: 4694931302848755623} modalPanel: {fileID: 4694931302848755623}
keyText: {fileID: 4193449683656256336} keyText: {fileID: 4193449683656256336}
interactText: {fileID: 8795452566802714605} interactText: {fileID: 8795452566802714605}
unavailablePanel: {fileID: 4694931302848755623}
unavailableText: {fileID: 8795452566802714605}
defaultKey: E defaultKey: E
--- !u!114 &8795452566802714605 stripped --- !u!114 &8795452566802714605 stripped
MonoBehaviour: MonoBehaviour:

View File

@@ -9,6 +9,8 @@ namespace Northbound
[SerializeField] private GameObject modalPanel; [SerializeField] private GameObject modalPanel;
[SerializeField] private TextMeshProUGUI keyText; [SerializeField] private TextMeshProUGUI keyText;
[SerializeField] private TextMeshProUGUI interactText; [SerializeField] private TextMeshProUGUI interactText;
[SerializeField] private GameObject unavailablePanel;
[SerializeField] private TextMeshProUGUI unavailableText;
[Header("Settings")] [Header("Settings")]
[SerializeField] private string defaultKey = "E"; [SerializeField] private string defaultKey = "E";
@@ -27,6 +29,11 @@ namespace Northbound
{ {
modalPanel.SetActive(false); modalPanel.SetActive(false);
} }
if (unavailablePanel != null)
{
unavailablePanel.SetActive(false);
}
} }
public void ShowModal(IInteractable interactable) public void ShowModal(IInteractable interactable)
@@ -54,6 +61,11 @@ namespace Northbound
{ {
modalPanel.SetActive(false); modalPanel.SetActive(false);
} }
if (unavailablePanel != null)
{
unavailablePanel.SetActive(false);
}
} }
public void UpdateModalContent() public void UpdateModalContent()
@@ -86,5 +98,48 @@ namespace Northbound
return prompt; return prompt;
} }
/// <summary>
/// 상호작용 불가능 메시지를 표시합니다.
/// </summary>
/// <param name="interactable">상호작용 불가능한 대상</param>
public void ShowUnavailableMessage(IInteractable interactable)
{
if (interactable == null)
{
HideModal();
return;
}
if (unavailablePanel != null)
{
// unavailablePanel이 할당된 경우 전용 패널 사용
if (modalPanel != null)
{
modalPanel.SetActive(false);
}
unavailablePanel.SetActive(true);
if (unavailableText != null)
{
string prompt = interactable.GetInteractionPrompt();
unavailableText.text = ExtractInteractText(prompt);
}
}
else if (modalPanel != null && interactText != null)
{
// unavailablePanel이 없는 경우 modalPanel을 사용하여 불가능 메시지 표시
modalPanel.SetActive(true);
if (keyText != null)
{
keyText.text = "";
}
string prompt = interactable.GetInteractionPrompt();
interactText.text = ExtractInteractText(prompt);
}
}
} }
} }

View File

@@ -12,6 +12,7 @@ namespace Northbound
[SerializeField] private PlayerInteraction playerInteraction; [SerializeField] private PlayerInteraction playerInteraction;
private IInteractable _currentInteractable; private IInteractable _currentInteractable;
private IInteractable _previousUnavailableInteractable;
private bool _isEnabled = false; private bool _isEnabled = false;
private void Awake() private void Awake()
@@ -33,14 +34,13 @@ namespace Northbound
FindModalComponent(); FindModalComponent();
if (interactableModal != null) if (interactableModal == null)
{
interactableModal.HideModal();
}
else
{ {
Debug.LogError("[InteractableModalManager] InteractableModal is still null in Start!"); Debug.LogError("[InteractableModalManager] InteractableModal is still null in Start!");
} }
// Start()에서 HideModal()을 호출하지 않음 - 첫 번째 Update에서 Modal이 표시되지 않는 문제 방지
// modalPanel과 unavailablePanel은 InteractableModal.Start()에서 이미 false로 설정됨
} }
private void FindModalComponent() private void FindModalComponent()
@@ -88,16 +88,26 @@ namespace Northbound
_isEnabled = true; _isEnabled = true;
IInteractable detectedInteractable = GetDetectedInteractable(); IInteractable detectedInteractable = GetDetectedInteractable();
IInteractable unavailableInteractable = playerInteraction.CurrentUnavailableInteractable;
if (detectedInteractable != _currentInteractable) bool interactableChanged = detectedInteractable != _currentInteractable;
bool unavailableChanged = unavailableInteractable != _previousUnavailableInteractable;
if (interactableChanged || unavailableChanged)
{ {
_currentInteractable = detectedInteractable; _currentInteractable = detectedInteractable;
_previousUnavailableInteractable = unavailableInteractable;
if (_currentInteractable != null) if (_currentInteractable != null)
{ {
Debug.Log($"[InteractableModalManager] Show modal for interactable"); Debug.Log($"[InteractableModalManager] Show modal for interactable");
ShowModal(_currentInteractable); ShowModal(_currentInteractable);
} }
else if (unavailableInteractable != null)
{
Debug.Log($"[InteractableModalManager] Show unavailable message");
ShowUnavailableMessageModal(unavailableInteractable);
}
else else
{ {
Debug.Log($"[InteractableModalManager] Hide modal"); Debug.Log($"[InteractableModalManager] Hide modal");
@@ -149,6 +159,14 @@ namespace Northbound
} }
} }
private void ShowUnavailableMessageModal(IInteractable interactable)
{
if (interactableModal != null)
{
interactableModal.ShowUnavailableMessage(interactable);
}
}
private void UpdateModalContent() private void UpdateModalContent()
{ {
if (interactableModal != null) if (interactableModal != null)

View File

@@ -34,6 +34,7 @@ namespace Northbound
private PlayerInputActions _inputActions; private PlayerInputActions _inputActions;
private IInteractable _currentInteractable; private IInteractable _currentInteractable;
private IInteractable _unavailableInteractable;
private Camera _mainCamera; private Camera _mainCamera;
private Animator _animator; private Animator _animator;
private EquipmentSocket _equipmentSocket; private EquipmentSocket _equipmentSocket;
@@ -45,6 +46,7 @@ namespace Northbound
public bool IsInteracting => _isInteracting; public bool IsInteracting => _isInteracting;
public float WorkPower => workPower; public float WorkPower => workPower;
public IInteractable CurrentUnavailableInteractable => _unavailableInteractable;
public override void OnNetworkSpawn() public override void OnNetworkSpawn()
{ {
@@ -108,14 +110,26 @@ namespace Northbound
if (interactable == null) if (interactable == null)
interactable = hit.collider.GetComponentInParent<IInteractable>(); interactable = hit.collider.GetComponentInParent<IInteractable>();
if (interactable != null && interactable.CanInteract(OwnerClientId)) if (interactable != null)
{ {
_currentInteractable = interactable; if (interactable.CanInteract(OwnerClientId))
return; {
_currentInteractable = interactable;
_unavailableInteractable = null;
return;
}
else
{
// CanInteract가 false인 경우 추적
_currentInteractable = null;
_unavailableInteractable = interactable;
return;
}
} }
} }
_currentInteractable = null; _currentInteractable = null;
_unavailableInteractable = null;
} }
private void OnInteract(InputAction.CallbackContext context) private void OnInteract(InputAction.CallbackContext context)