인터랙션 하이라이트 기능 추가, 인터랙션 인터페이스 일부 변경
This commit is contained in:
parent
0444b84249
commit
2d4edba36c
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
@ -15,21 +16,27 @@ public enum InteractionType : uint
|
|||||||
public interface IInteractable
|
public interface IInteractable
|
||||||
{
|
{
|
||||||
bool CanInteract();
|
bool CanInteract();
|
||||||
bool OnInteracted(IInteractor interactor, ScriptableObject interactionPayloadSo = null);
|
bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null);
|
||||||
InteractionType GetInteractionType();
|
InteractionType GetInteractionType();
|
||||||
GameObject GetInteractableGameObject();
|
GameObject GetInteractableGameObject();
|
||||||
void InitializeInteraction(InteractionType interactionType);
|
void InitializeInteraction(InteractionType interactionType);
|
||||||
float GetRequiredHoldTime();
|
float GetRequiredHoldTime();
|
||||||
string GetInteractionMessageKey();
|
string GetInteractionMessageKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IInteractor
|
public interface IInteractor
|
||||||
{
|
{
|
||||||
GameObject GetInteractorGameObject();
|
GameObject GetInteractorGameObject();
|
||||||
|
IInteractable GetFocusedInteractable();
|
||||||
|
bool CanSolveInteractionType(InteractionType interactionType);
|
||||||
|
bool CanInteractTo(IInteractable interactable,
|
||||||
|
ScriptableObject payloadSo = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IInteractionSolver
|
public interface IInteractionSolver
|
||||||
{
|
{
|
||||||
bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject interactionPayloadSo = null);
|
bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null);
|
||||||
bool CanExecuteInteraction();
|
bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
|
ScriptableObject payloadSo = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
Assets/_DDD/_Scripts/GameFramework/PlayerManager.cs
Normal file
20
Assets/_DDD/_Scripts/GameFramework/PlayerManager.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
public class PlayerManager : Singleton<PlayerManager>
|
||||||
|
{
|
||||||
|
private GameObject _player;
|
||||||
|
|
||||||
|
public void RegisterPlayer(GameObject player)
|
||||||
|
{
|
||||||
|
_player = player;
|
||||||
|
Debug.Log($"Player registered: {player.name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject GetPlayer()
|
||||||
|
{
|
||||||
|
return _player ? _player : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,10 @@ namespace DDD
|
|||||||
{
|
{
|
||||||
public class RestaurantPlayerCharacter : RestaurantCharacter
|
public class RestaurantPlayerCharacter : RestaurantCharacter
|
||||||
{
|
{
|
||||||
|
protected override void Awake()
|
||||||
|
{
|
||||||
|
base.Awake();
|
||||||
|
PlayerManager.Instance.RegisterPlayer(gameObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -45,7 +45,7 @@ protected override void OnDestroy()
|
|||||||
|
|
||||||
private void OnInteractPerformed(InputAction.CallbackContext context)
|
private void OnInteractPerformed(InputAction.CallbackContext context)
|
||||||
{
|
{
|
||||||
if (_nearestInteractable == null || CanInteract(_nearestInteractable) == false) return;
|
if (_nearestInteractable == null || CanInteractTo(_nearestInteractable) == false) return;
|
||||||
|
|
||||||
float requiredHoldTime = _nearestInteractable.GetRequiredHoldTime();
|
float requiredHoldTime = _nearestInteractable.GetRequiredHoldTime();
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ protected override void OnNearestInteractableChanged(IInteractable newTarget)
|
|||||||
{
|
{
|
||||||
if (newTarget != null)
|
if (newTarget != null)
|
||||||
{
|
{
|
||||||
BroadcastShowUi(newTarget, CanInteract(newTarget), 0f);
|
BroadcastShowUi(newTarget, CanInteractTo(newTarget), 0f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -83,7 +83,7 @@ protected override void OnInteractionHoldProgress(float ratio)
|
|||||||
{
|
{
|
||||||
if (_interactingTarget != null)
|
if (_interactingTarget != null)
|
||||||
{
|
{
|
||||||
BroadcastShowUi(_interactingTarget, CanInteract(_interactingTarget), ratio);
|
BroadcastShowUi(_interactingTarget, CanInteractTo(_interactingTarget), ratio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,11 @@ public class RestaurantCharacter : MonoBehaviour, IGameCharacter, IInteractor
|
|||||||
{
|
{
|
||||||
[EnumToggleButtons, SerializeField] protected InteractionType _interactionType;
|
[EnumToggleButtons, SerializeField] protected InteractionType _interactionType;
|
||||||
|
|
||||||
protected virtual void Awake() { }
|
RestaurantCharacterInteraction _interactionComponent;
|
||||||
|
protected virtual void Awake()
|
||||||
|
{
|
||||||
|
_interactionComponent = GetComponent<RestaurantCharacterInteraction>();
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Start()
|
protected virtual void Start()
|
||||||
{
|
{
|
||||||
@ -27,7 +31,22 @@ protected virtual void Start()
|
|||||||
|
|
||||||
public GameObject GetInteractorGameObject()
|
public GameObject GetInteractorGameObject()
|
||||||
{
|
{
|
||||||
return gameObject;
|
return _interactionComponent.GetInteractorGameObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IInteractable GetFocusedInteractable()
|
||||||
|
{
|
||||||
|
return _interactionComponent.GetFocusedInteractable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanSolveInteractionType(InteractionType interactionType)
|
||||||
|
{
|
||||||
|
return _interactionComponent.CanSolveInteractionType(interactionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanInteractTo(IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
return _interactionComponent.CanInteractTo(interactable, payloadSo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -33,7 +33,7 @@ protected virtual void Update()
|
|||||||
|
|
||||||
if (_isInteracting)
|
if (_isInteracting)
|
||||||
{
|
{
|
||||||
if (_nearestInteractable != _interactingTarget || CanInteract(_interactingTarget) == false)
|
if (_nearestInteractable != _interactingTarget || CanInteractTo(_interactingTarget) == false)
|
||||||
{
|
{
|
||||||
ResetInteractionState();
|
ResetInteractionState();
|
||||||
return;
|
return;
|
||||||
@ -83,7 +83,7 @@ protected IInteractable GetNearestInteractable()
|
|||||||
if (col.TryGetComponent<IInteractable>(out var interactable) == false) continue;
|
if (col.TryGetComponent<IInteractable>(out var interactable) == false) continue;
|
||||||
|
|
||||||
var type = interactable.GetInteractionType();
|
var type = interactable.GetInteractionType();
|
||||||
if (ContainsSolverForType(type) == false) continue;
|
if (CanSolveInteractionType(type) == false) continue;
|
||||||
|
|
||||||
float distance = Vector3.Distance(transform.position, col.transform.position);
|
float distance = Vector3.Distance(transform.position, col.transform.position);
|
||||||
if (distance < closestDistance)
|
if (distance < closestDistance)
|
||||||
@ -100,6 +100,17 @@ public virtual void Invoke(RestaurantInteractionEvent evt) { }
|
|||||||
|
|
||||||
public GameObject GetInteractorGameObject() => gameObject;
|
public GameObject GetInteractorGameObject() => gameObject;
|
||||||
|
|
||||||
|
public IInteractionSolver GetInteractionSolver(InteractionType interactionType)
|
||||||
|
{
|
||||||
|
TryGetSolverForType(interactionType, out var solver);
|
||||||
|
return solver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IInteractable GetFocusedInteractable()
|
||||||
|
{
|
||||||
|
return _nearestInteractable;
|
||||||
|
}
|
||||||
|
|
||||||
private bool TryGetSolverFor(IInteractable interactable, out IInteractionSolver solver)
|
private bool TryGetSolverFor(IInteractable interactable, out IInteractionSolver solver)
|
||||||
{
|
{
|
||||||
solver = null;
|
solver = null;
|
||||||
@ -124,7 +135,7 @@ private bool TryGetSolverForType(InteractionType type, out IInteractionSolver so
|
|||||||
return solver != null;
|
return solver != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ContainsSolverForType(InteractionType type)
|
public bool CanSolveInteractionType(InteractionType type)
|
||||||
{
|
{
|
||||||
if (_cachedSolvers.TryGetValue(type, out var cachedSolver)) return cachedSolver != null;
|
if (_cachedSolvers.TryGetValue(type, out var cachedSolver)) return cachedSolver != null;
|
||||||
|
|
||||||
@ -137,13 +148,12 @@ private bool ContainsSolverForType(InteractionType type)
|
|||||||
return solver != null;
|
return solver != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool CanInteract(IInteractable interactable)
|
public bool CanInteractTo(IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
if (interactable == null) return false;
|
if (interactable == null) return false;
|
||||||
if (interactable.CanInteract() == false) return false;
|
if (interactable.CanInteract() == false) return false;
|
||||||
if (TryGetSolverFor(interactable, out var solver) == false) return false;
|
if (TryGetSolverFor(interactable, out var solver) == false) return false;
|
||||||
|
return solver.CanExecuteInteraction(this, interactable, payloadSo);
|
||||||
return solver.CanExecuteInteraction();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,50 +36,61 @@ public class InteractableHighlight : MonoBehaviour
|
|||||||
{InteractionOutlineType.None, new InteractionOutlineData() {Color = Color.clear, Width = 0.5f, Opacity = 0f}}
|
{InteractionOutlineType.None, new InteractionOutlineData() {Color = Color.clear, Width = 0.5f, Opacity = 0f}}
|
||||||
};
|
};
|
||||||
|
|
||||||
private float OpacityMultiply = 1.0f;
|
private HighlightEffect _highlightComponent;
|
||||||
private HighlightEffect highlight;
|
private RestaurantInteractionComponent _interactionComponent;
|
||||||
private RestaurantInteractionComponent interaction;
|
private IInteractor _interactor;
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
// Cache HighlightEffect
|
// Cache HighlightEffect
|
||||||
highlight = GetComponent<HighlightPlus.HighlightEffect>();
|
_highlightComponent = GetComponent<HighlightPlus.HighlightEffect>();
|
||||||
interaction = GetComponent<RestaurantInteractionComponent>();
|
_interactionComponent = GetComponent<RestaurantInteractionComponent>();
|
||||||
|
|
||||||
// highlightEffect에 alphaCutoff, constantWidth, combineMeshes, outlineQuality, outlineIndependent 등의 필수 옵션이 켜져있는지 확인
|
// highlightEffect에 alphaCutoff, constantWidth, combineMeshes, outlineQuality, outlineIndependent 등의 필수 옵션이 켜져있는지 확인
|
||||||
highlight.alphaCutOff = 0.5f;
|
_highlightComponent.alphaCutOff = 0.5f;
|
||||||
highlight.combineMeshes = true;
|
_highlightComponent.combineMeshes = true;
|
||||||
highlight.constantWidth = true;
|
_highlightComponent.constantWidth = true;
|
||||||
highlight.outlineQuality = QualityLevel.Highest;
|
_highlightComponent.outlineQuality = QualityLevel.Highest;
|
||||||
highlight.outlineIndependent = true;
|
_highlightComponent.outlineIndependent = true;
|
||||||
highlight.outlineBlurPasses = 1;
|
_highlightComponent.outlineBlurPasses = 1;
|
||||||
highlight.outlineSharpness = 8;
|
_highlightComponent.outlineSharpness = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
|
FetchPlayerInteractorComponent();
|
||||||
|
|
||||||
var currentType = GetCurrentOutlineType();
|
var currentType = GetCurrentOutlineType();
|
||||||
ApplyOutlineType(currentType);
|
ApplyOutlineType(currentType);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FetchPlayerInteractorComponent()
|
||||||
|
{
|
||||||
|
if (_interactor == null)
|
||||||
|
{
|
||||||
|
var player = PlayerManager.Instance.GetPlayer();
|
||||||
|
_interactor = player?.GetComponent<IInteractor>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private InteractionOutlineType GetCurrentOutlineType()
|
private InteractionOutlineType GetCurrentOutlineType()
|
||||||
{
|
{
|
||||||
// interaction이 null이거나 컴포넌트가 비활성화된 경우
|
// interaction이 null이거나 컴포넌트가 비활성화된 경우
|
||||||
if (interaction == null || !interaction.enabled)
|
if (!_interactionComponent || !_interactionComponent.enabled)
|
||||||
return InteractionOutlineType.None;
|
return InteractionOutlineType.None;
|
||||||
|
|
||||||
// IInteractable 인터페이스로 캐스팅하여 상태 확인
|
// IInteractable 인터페이스로 캐스팅하여 상태 확인
|
||||||
var interactable = interaction as IInteractable;
|
if (_interactionComponent is not IInteractable interactable)
|
||||||
if (interactable == null)
|
|
||||||
return InteractionOutlineType.None;
|
return InteractionOutlineType.None;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 상호작용 불가능한 경우
|
// 상호작용 불가능한 경우
|
||||||
if (!interactable.CanInteract())
|
if (CanExecuteInteraction() == false)
|
||||||
return InteractionOutlineType.Unavailable;
|
return InteractionOutlineType.Unavailable;
|
||||||
|
|
||||||
// TODO: 여기에 추가 상태 로직을 구현
|
// TODO: 여기에 추가 상태 로직을 구현
|
||||||
// - isHovered, isFocused 등의 상태를 체크
|
|
||||||
// - isObjective 등의 퀘스트 상태를 체크
|
// - isObjective 등의 퀘스트 상태를 체크
|
||||||
|
|
||||||
// 플레이어가 현재 이 오브젝트를 포커스 중인지 확인
|
// 플레이어가 현재 이 오브젝트를 포커스 중인지 확인
|
||||||
@ -93,7 +104,6 @@ private InteractionOutlineType GetCurrentOutlineType()
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// CanInteract() 호출 중 예외 발생 시 안전하게 처리
|
|
||||||
return InteractionOutlineType.Unavailable;
|
return InteractionOutlineType.Unavailable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +118,7 @@ private void ApplyOutlineType(InteractionOutlineType type)
|
|||||||
|
|
||||||
lastAppliedType = type;
|
lastAppliedType = type;
|
||||||
|
|
||||||
if (highlight == null)
|
if (!_highlightComponent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// OutlineData에서 해당 타입의 스타일 가져오기
|
// OutlineData에서 해당 타입의 스타일 가져오기
|
||||||
@ -121,35 +131,35 @@ private void ApplyOutlineType(InteractionOutlineType type)
|
|||||||
// HighlightEffect에 적용
|
// HighlightEffect에 적용
|
||||||
if (type == InteractionOutlineType.None)
|
if (type == InteractionOutlineType.None)
|
||||||
{
|
{
|
||||||
highlight.highlighted = false;
|
_highlightComponent.highlighted = false;
|
||||||
highlight.outline = 0;
|
_highlightComponent.outline = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
highlight.highlighted = true;
|
_highlightComponent.highlighted = true;
|
||||||
highlight.outlineColor = data.Color;
|
_highlightComponent.outlineColor = data.Color;
|
||||||
highlight.outlineWidth = data.Width;
|
_highlightComponent.outlineWidth = data.Width;
|
||||||
highlight.outline = data.Opacity * OpacityMultiply;
|
_highlightComponent.outline = data.Opacity * opacityMultiply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private bool IsPlayerFocusing()
|
private bool IsPlayerFocusing()
|
||||||
{
|
{
|
||||||
// 방법 1: 싱글톤 패턴의 플레이어 매니저 사용
|
return _interactor?.GetFocusedInteractable() == _interactionComponent;
|
||||||
// if (PlayerManager.Instance != null)
|
|
||||||
// {
|
|
||||||
// return PlayerManager.Instance.CurrentFocusTarget == gameObject;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 방법 3: 정적 참조를 통한 현재 포커스 대상 확인
|
|
||||||
// if (InteractionSystem.CurrentFocusedObject == gameObject)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanExecuteInteraction()
|
||||||
|
{
|
||||||
|
if (_interactionComponent.CanInteract() == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_interactor == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _interactor.CanInteractTo(_interactionComponent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,14 +13,14 @@ public bool CanInteract()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnInteracted(IInteractor interactor, ScriptableObject interactionPayloadSo = null)
|
public bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
if (CanInteract() == false)
|
if (CanInteract() == false)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool interactionResult = RestaurantInteractionEvents.RestaurantInteraction.RequestInteraction(interactor.GetInteractorGameObject(),
|
bool interactionResult = RestaurantInteractionEvents.RestaurantInteraction.RequestInteraction(interactor.GetInteractorGameObject(),
|
||||||
GetInteractableGameObject(), GetInteractionType(), interactionPayloadSo, true);
|
GetInteractableGameObject(), GetInteractionType(), payloadSo, true);
|
||||||
return interactionResult;
|
return interactionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ namespace DDD
|
|||||||
{
|
{
|
||||||
public class RestaurantManagementUiEventSolver : MonoBehaviour, IInteractionSolver
|
public class RestaurantManagementUiEventSolver : MonoBehaviour, IInteractionSolver
|
||||||
{
|
{
|
||||||
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject interactionPayloadSo = null)
|
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
if (CanExecuteInteraction() == false) return false;
|
if (CanExecuteInteraction() == false) return false;
|
||||||
|
|
||||||
@ -14,7 +14,8 @@ public bool ExecuteInteraction(IInteractor interactor, IInteractable interactabl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanExecuteInteraction()
|
public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
|
ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
GameFlowState currentGameFlowState = GameFlowManager.Instance.GameFlowDataSo.CurrentGameState;
|
GameFlowState currentGameFlowState = GameFlowManager.Instance.GameFlowDataSo.CurrentGameState;
|
||||||
return currentGameFlowState == GameFlowState.ReadyForRestaurant;
|
return currentGameFlowState == GameFlowState.ReadyForRestaurant;
|
||||||
|
@ -17,7 +17,7 @@ private async Task Initialize()
|
|||||||
_restaurantManagementSo = await AssetManager.LoadAsset<RestaurantManagementSo>(DataConstants.RestaurantManagementSo);
|
_restaurantManagementSo = await AssetManager.LoadAsset<RestaurantManagementSo>(DataConstants.RestaurantManagementSo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject interactionPayloadSo = null)
|
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
if (CanExecuteInteraction() == false) return false;
|
if (CanExecuteInteraction() == false) return false;
|
||||||
|
|
||||||
@ -25,7 +25,8 @@ public bool ExecuteInteraction(IInteractor interactor, IInteractable interactabl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanExecuteInteraction()
|
public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
|
ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
GameFlowState currentGameFlowState = GameFlowManager.Instance.GameFlowDataSo.CurrentGameState;
|
GameFlowState currentGameFlowState = GameFlowManager.Instance.GameFlowDataSo.CurrentGameState;
|
||||||
return currentGameFlowState == GameFlowState.ReadyForRestaurant && _restaurantManagementSo.IsOpenable();
|
return currentGameFlowState == GameFlowState.ReadyForRestaurant && _restaurantManagementSo.IsOpenable();
|
||||||
|
Loading…
Reference in New Issue
Block a user