2026-06-19 UI, UI로직
This commit is contained in:
138
Assets/My project/shell Scripts/UI/PlayerMovement.cs
Normal file
138
Assets/My project/shell Scripts/UI/PlayerMovement.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public class PlayerMovement : MonoBehaviour
|
||||
{
|
||||
[Header("Movement")]
|
||||
[SerializeField] private float speed = 3f;
|
||||
|
||||
[Header("VR Camera Reference")]
|
||||
[Tooltip("XR Origin > Camera Offset > Main Camera를 넣으세요.")]
|
||||
[SerializeField] private Transform cameraTransform;
|
||||
|
||||
[Header("Move Input Actions")]
|
||||
[Tooltip("Dynamic Move Provider가 쓰는 Left Move Input과 같은 액션을 넣으세요.")]
|
||||
[SerializeField] private InputActionReference leftMoveAction;
|
||||
|
||||
[Tooltip("Dynamic Move Provider가 쓰는 Right Move Input과 같은 액션을 넣으세요.")]
|
||||
[SerializeField] private InputActionReference rightMoveAction;
|
||||
|
||||
[Header("Reverse Control")]
|
||||
[SerializeField] private bool isReversed = false;
|
||||
|
||||
[Header("External Move Provider")]
|
||||
[Tooltip("XR Origin > Locomotion > Move에 있는 Dynamic Move Provider 컴포넌트를 넣으세요.")]
|
||||
[SerializeField] private Behaviour dynamicMoveProvider;
|
||||
|
||||
public bool IsReversed => isReversed;
|
||||
public Vector2 CurrentInput { get; private set; }
|
||||
public Vector2 CurrentOutput { get; private set; }
|
||||
|
||||
private CharacterController characterController;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
characterController = GetComponent<CharacterController>();
|
||||
ApplyMoveMode();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
EnableAction(leftMoveAction);
|
||||
EnableAction(rightMoveAction);
|
||||
ApplyMoveMode();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
CurrentInput = ReadMoveInput();
|
||||
CurrentOutput = isReversed ? -CurrentInput : CurrentInput;
|
||||
|
||||
// 일반 상태에서는 Dynamic Move Provider가 이동하므로 이 스크립트는 이동하지 않음
|
||||
if (!isReversed)
|
||||
return;
|
||||
|
||||
Move(CurrentOutput);
|
||||
}
|
||||
|
||||
private void Move(Vector2 input)
|
||||
{
|
||||
if (cameraTransform == null)
|
||||
return;
|
||||
|
||||
if (input.sqrMagnitude < 0.0001f)
|
||||
return;
|
||||
|
||||
Vector3 forward = cameraTransform.forward;
|
||||
Vector3 right = cameraTransform.right;
|
||||
|
||||
forward.y = 0f;
|
||||
right.y = 0f;
|
||||
|
||||
forward.Normalize();
|
||||
right.Normalize();
|
||||
|
||||
Vector3 moveDirection =
|
||||
right * input.x +
|
||||
forward * input.y;
|
||||
|
||||
Vector3 movement = moveDirection * speed * Time.deltaTime;
|
||||
|
||||
if (characterController != null && characterController.enabled)
|
||||
{
|
||||
characterController.Move(movement);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position += movement;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 ReadMoveInput()
|
||||
{
|
||||
Vector2 leftInput = ReadAction(leftMoveAction);
|
||||
Vector2 rightInput = ReadAction(rightMoveAction);
|
||||
|
||||
if (leftInput.sqrMagnitude >= rightInput.sqrMagnitude)
|
||||
return leftInput;
|
||||
|
||||
return rightInput;
|
||||
}
|
||||
|
||||
private Vector2 ReadAction(InputActionReference actionReference)
|
||||
{
|
||||
if (actionReference == null || actionReference.action == null)
|
||||
return Vector2.zero;
|
||||
|
||||
return actionReference.action.ReadValue<Vector2>();
|
||||
}
|
||||
|
||||
private void EnableAction(InputActionReference actionReference)
|
||||
{
|
||||
if (actionReference == null || actionReference.action == null)
|
||||
return;
|
||||
|
||||
if (!actionReference.action.enabled)
|
||||
actionReference.action.Enable();
|
||||
}
|
||||
|
||||
public void SetReversed(bool value)
|
||||
{
|
||||
isReversed = value;
|
||||
ApplyMoveMode();
|
||||
}
|
||||
|
||||
public void ToggleReversed()
|
||||
{
|
||||
isReversed = !isReversed;
|
||||
ApplyMoveMode();
|
||||
}
|
||||
|
||||
private void ApplyMoveMode()
|
||||
{
|
||||
if (dynamicMoveProvider != null)
|
||||
{
|
||||
dynamicMoveProvider.enabled = !isReversed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c27449d394caa54ba30bd19636066a8
|
||||
87
Assets/My project/shell Scripts/UI/ShellController.cs
Normal file
87
Assets/My project/shell Scripts/UI/ShellController.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ShellController : MonoBehaviour
|
||||
{
|
||||
[Header("Shell Parts")]
|
||||
[Tooltip("¿¸®°í ´ÝÈ÷´Â ÀÁ¶°³ Transform")]
|
||||
[SerializeField] private Transform upperShell;
|
||||
|
||||
[Header("Timing")]
|
||||
[SerializeField] private float openDuration = 1.5f;
|
||||
[SerializeField] private float closedDuration = 1.5f;
|
||||
|
||||
[Header("Animation")]
|
||||
[SerializeField] private float closedAngle = 0f;
|
||||
[SerializeField] private float openAngle = -65f;
|
||||
[SerializeField] private float rotateSpeed = 8f;
|
||||
|
||||
[Header("Start State")]
|
||||
[SerializeField] private bool startOpen = false;
|
||||
|
||||
public bool IsOpen { get; private set; }
|
||||
public float StateTimer01 { get; private set; }
|
||||
|
||||
private float timer;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
IsOpen = startOpen;
|
||||
timer = 0f;
|
||||
UpdateShellVisual(true);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateShellState();
|
||||
UpdateShellVisual(false);
|
||||
}
|
||||
|
||||
private void UpdateShellState()
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
|
||||
float currentDuration = IsOpen ? openDuration : closedDuration;
|
||||
|
||||
if (currentDuration <= 0f)
|
||||
currentDuration = 0.1f;
|
||||
|
||||
StateTimer01 = Mathf.Clamp01(timer / currentDuration);
|
||||
|
||||
if (timer >= currentDuration)
|
||||
{
|
||||
IsOpen = !IsOpen;
|
||||
timer = 0f;
|
||||
StateTimer01 = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateShellVisual(bool instant)
|
||||
{
|
||||
if (upperShell == null)
|
||||
return;
|
||||
|
||||
float targetAngle = IsOpen ? openAngle : closedAngle;
|
||||
Quaternion targetRotation = Quaternion.Euler(targetAngle, 0f, 0f);
|
||||
|
||||
if (instant)
|
||||
{
|
||||
upperShell.localRotation = targetRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
upperShell.localRotation = Quaternion.Lerp(
|
||||
upperShell.localRotation,
|
||||
targetRotation,
|
||||
Time.deltaTime * rotateSpeed
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetShell()
|
||||
{
|
||||
IsOpen = startOpen;
|
||||
timer = 0f;
|
||||
StateTimer01 = 0f;
|
||||
UpdateShellVisual(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8219803680f358f4c9e1b955ec0fd097
|
||||
354
Assets/My project/shell Scripts/UI/ShellGameManager.cs
Normal file
354
Assets/My project/shell Scripts/UI/ShellGameManager.cs
Normal file
@@ -0,0 +1,354 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public class ShellGameManager : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private PlayerMovement player;
|
||||
[SerializeField] private UIManager ui;
|
||||
[SerializeField] private ShellController shellController;
|
||||
|
||||
[Header("Shell Target")]
|
||||
[Tooltip("조개 전체 Transform 또는 보상 위치 Transform을 넣으세요.")]
|
||||
[SerializeField] private Transform shell;
|
||||
|
||||
[Header("Reward Settings")]
|
||||
[SerializeField] private float collectDistance = 1.5f;
|
||||
[SerializeField] private bool failWhenClosed = true;
|
||||
|
||||
[Header("VR Distance Option")]
|
||||
[Tooltip("VR에서는 플레이어 머리 높이 때문에 Y축 거리를 무시하는 것이 안정적입니다.")]
|
||||
[SerializeField] private bool ignoreHeight = true;
|
||||
|
||||
[Header("Input Actions")]
|
||||
[Tooltip("오른손 Trigger 또는 XRI RightHand Interaction / Activate 액션을 연결하세요.")]
|
||||
[SerializeField] private InputActionReference collectAction;
|
||||
|
||||
[Tooltip("선택 사항입니다. 리셋 버튼 액션을 연결하고 싶을 때 사용합니다.")]
|
||||
[SerializeField] private InputActionReference resetAction;
|
||||
|
||||
[Header("Events")]
|
||||
[SerializeField] private UnityEvent onSuccess;
|
||||
[SerializeField] private UnityEvent onFail;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool showDebugLog = true;
|
||||
|
||||
private bool gameFinished;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] OnEnable 실행됨");
|
||||
|
||||
RegisterInputActions();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] OnDisable 실행됨");
|
||||
|
||||
UnregisterInputActions();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] Start 실행됨 / ResetGame 호출");
|
||||
|
||||
ResetGame();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
private void RegisterInputActions()
|
||||
{
|
||||
if (collectAction == null)
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] Collect Action이 연결되지 않았습니다.");
|
||||
}
|
||||
else if (collectAction.action == null)
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] Collect Action 안의 action이 null입니다.");
|
||||
}
|
||||
else
|
||||
{
|
||||
collectAction.action.performed += OnCollectPerformed;
|
||||
collectAction.action.Enable();
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log(
|
||||
$"[ShellGameManager] Collect Action 등록 완료: {collectAction.action.name} / Enabled: {collectAction.action.enabled}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (resetAction != null && resetAction.action != null)
|
||||
{
|
||||
resetAction.action.performed += OnResetPerformed;
|
||||
resetAction.action.Enable();
|
||||
|
||||
if (showDebugLog)
|
||||
{
|
||||
Debug.Log(
|
||||
$"[ShellGameManager] Reset Action 등록 완료: {resetAction.action.name} / Enabled: {resetAction.action.enabled}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterInputActions()
|
||||
{
|
||||
if (collectAction != null && collectAction.action != null)
|
||||
{
|
||||
collectAction.action.performed -= OnCollectPerformed;
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] Collect Action 등록 해제");
|
||||
}
|
||||
|
||||
if (resetAction != null && resetAction.action != null)
|
||||
{
|
||||
resetAction.action.performed -= OnResetPerformed;
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] Reset Action 등록 해제");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
if (ui == null)
|
||||
return;
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
ui.SetControl(player.IsReversed);
|
||||
ui.SetMovementDebug(player.CurrentInput, player.CurrentOutput);
|
||||
}
|
||||
|
||||
if (shellController != null)
|
||||
{
|
||||
ui.SetShellState(shellController.IsOpen);
|
||||
ui.SetShellTimer(shellController.StateTimer01, shellController.IsOpen);
|
||||
}
|
||||
|
||||
if (!gameFinished)
|
||||
UpdatePromptUI();
|
||||
}
|
||||
|
||||
private void UpdatePromptUI()
|
||||
{
|
||||
if (ui == null || player == null || shell == null || shellController == null)
|
||||
return;
|
||||
|
||||
float distance = GetDistance(player.transform.position, shell.position);
|
||||
|
||||
if (distance > collectDistance)
|
||||
{
|
||||
ui.SetPromptFarFromShell();
|
||||
}
|
||||
else
|
||||
{
|
||||
ui.SetPromptNearShell(shellController.IsOpen);
|
||||
}
|
||||
}
|
||||
|
||||
public void TryCollectReward()
|
||||
{
|
||||
Debug.Log("[ShellGameManager] TryCollectReward 실행됨");
|
||||
|
||||
if (gameFinished)
|
||||
{
|
||||
Debug.Log("[ShellGameManager] 이미 게임이 끝난 상태라 입력을 무시합니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (player == null)
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] Player 참조가 비어 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell == null)
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] Shell Transform 참조가 비어 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shellController == null)
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] ShellController 참조가 비어 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
float distance = GetDistance(player.transform.position, shell.position);
|
||||
|
||||
Debug.Log(
|
||||
$"[ShellGameManager] 판정 정보 / 거리: {distance:F2} / 성공 가능 거리: {collectDistance:F2} / 조개 열림: {shellController.IsOpen} / FailWhenClosed: {failWhenClosed}"
|
||||
);
|
||||
|
||||
if (distance > collectDistance)
|
||||
{
|
||||
if (ui != null)
|
||||
ui.SetPromptFarFromShell();
|
||||
|
||||
Debug.Log("[ShellGameManager] 조개와 너무 멀어서 보상을 획득할 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shellController.IsOpen)
|
||||
{
|
||||
Debug.Log("[ShellGameManager] 성공 조건 만족: 조개 근처 + 조개 열림");
|
||||
Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (failWhenClosed)
|
||||
{
|
||||
Debug.Log("[ShellGameManager] 실패 조건 만족: 조개 근처 + 조개 닫힘");
|
||||
Fail();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ui != null)
|
||||
ui.SetPromptNearShell(false);
|
||||
|
||||
Debug.Log("[ShellGameManager] 조개가 닫혀 있어서 아직 획득할 수 없습니다. FailWhenClosed가 꺼져 있습니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float GetDistance(Vector3 a, Vector3 b)
|
||||
{
|
||||
if (ignoreHeight)
|
||||
{
|
||||
a.y = 0f;
|
||||
b.y = 0f;
|
||||
}
|
||||
|
||||
return Vector3.Distance(a, b);
|
||||
}
|
||||
|
||||
private void Success()
|
||||
{
|
||||
gameFinished = true;
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.ShowReward(true);
|
||||
Debug.Log("[ShellGameManager] UIManager.ShowReward(true) 호출됨");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] UI 참조가 비어 있어서 성공 UI를 표시할 수 없습니다.");
|
||||
}
|
||||
|
||||
onSuccess?.Invoke();
|
||||
|
||||
Debug.Log("[ShellGameManager] SUCCESS! 조개가 열린 타이밍에 보상을 획득했습니다.");
|
||||
}
|
||||
|
||||
private void Fail()
|
||||
{
|
||||
gameFinished = true;
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.ShowReward(false);
|
||||
Debug.Log("[ShellGameManager] UIManager.ShowReward(false) 호출됨");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] UI 참조가 비어 있어서 실패 UI를 표시할 수 없습니다.");
|
||||
}
|
||||
|
||||
onFail?.Invoke();
|
||||
|
||||
Debug.Log("[ShellGameManager] FAIL! 조개가 닫힌 타이밍에 시도했습니다.");
|
||||
}
|
||||
|
||||
public void ResetGame()
|
||||
{
|
||||
gameFinished = false;
|
||||
|
||||
if (ui != null)
|
||||
{
|
||||
ui.HideReward();
|
||||
ui.SetPromptDefault();
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] UI 초기화 완료");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] UI 참조가 비어 있습니다.");
|
||||
}
|
||||
|
||||
if (shellController != null)
|
||||
{
|
||||
shellController.ResetShell();
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] ShellController.ResetShell 호출됨");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[ShellGameManager] ShellController 참조가 비어 있습니다.");
|
||||
}
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[ShellGameManager] ResetGame 완료");
|
||||
}
|
||||
|
||||
private void OnCollectPerformed(InputAction.CallbackContext context)
|
||||
{
|
||||
Debug.Log(
|
||||
$"[ShellGameManager] Collect Action 입력됨 / Action: {context.action.name} / Control: {context.control?.path} / Phase: {context.phase}"
|
||||
);
|
||||
|
||||
TryCollectReward();
|
||||
}
|
||||
|
||||
private void OnResetPerformed(InputAction.CallbackContext context)
|
||||
{
|
||||
Debug.Log(
|
||||
$"[ShellGameManager] Reset Action 입력됨 / Action: {context.action.name} / Control: {context.control?.path} / Phase: {context.phase}"
|
||||
);
|
||||
|
||||
ResetGame();
|
||||
}
|
||||
|
||||
// PlayerInput Send Messages 방식을 쓸 때도 호환되게 남겨둔 함수입니다.
|
||||
public void OnCollect(InputValue value)
|
||||
{
|
||||
Debug.Log("[ShellGameManager] OnCollect(InputValue) 호출됨");
|
||||
|
||||
if (value.isPressed)
|
||||
{
|
||||
Debug.Log("[ShellGameManager] OnCollect 입력 Pressed 상태");
|
||||
TryCollectReward();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[ShellGameManager] OnCollect 입력이 Pressed 상태가 아님");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset(InputValue value)
|
||||
{
|
||||
Debug.Log("[ShellGameManager] OnReset(InputValue) 호출됨");
|
||||
|
||||
if (value.isPressed)
|
||||
{
|
||||
Debug.Log("[ShellGameManager] OnReset 입력 Pressed 상태");
|
||||
ResetGame();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf7f727838ba918459e2db994fe3ce23
|
||||
338
Assets/My project/shell Scripts/UI/UIManager.cs
Normal file
338
Assets/My project/shell Scripts/UI/UIManager.cs
Normal file
@@ -0,0 +1,338 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class UIManager : MonoBehaviour
|
||||
{
|
||||
[Header("Follow Camera UI")]
|
||||
[SerializeField] private bool followCamera = true;
|
||||
|
||||
[Tooltip("XR Origin > Camera Offset > Main Camera를 넣으세요.")]
|
||||
[SerializeField] private Transform cameraTarget;
|
||||
|
||||
[Tooltip("따라다닐 UI 전체 Root입니다. 비워두면 이 UIManager가 붙은 오브젝트를 움직입니다.")]
|
||||
[SerializeField] private Transform followRoot;
|
||||
|
||||
[Tooltip("카메라 앞 몇 m 위치에 UI를 둘지 정합니다.")]
|
||||
[SerializeField] private float followDistance = 1.3f;
|
||||
|
||||
[Tooltip("카메라 높이 기준 위/아래 위치 보정값입니다. 양수면 위로, 음수면 아래로 갑니다.")]
|
||||
[SerializeField] private float heightOffset = 0.1f;
|
||||
|
||||
[Tooltip("카메라 기준 좌우 위치 보정값입니다.")]
|
||||
[SerializeField] private float sideOffset = 0f;
|
||||
|
||||
[Tooltip("켜면 UI가 부드럽게 따라옵니다.")]
|
||||
[SerializeField] private bool smoothFollow = true;
|
||||
|
||||
[SerializeField] private float followSpeed = 8f;
|
||||
|
||||
[Tooltip("켜면 회전도 부드럽게 따라옵니다.")]
|
||||
[SerializeField] private bool smoothRotation = true;
|
||||
|
||||
[SerializeField] private float rotationSpeed = 8f;
|
||||
|
||||
[Tooltip("켜면 카메라의 위아래 기울기는 무시하고 좌우 방향만 따라갑니다.")]
|
||||
[SerializeField] private bool useFlatForward = true;
|
||||
|
||||
[Tooltip("글자가 뒤집혀 보이면 켜세요.")]
|
||||
[SerializeField] private bool flipRotation = false;
|
||||
|
||||
[Header("Timing UI")]
|
||||
[SerializeField] private Image timingBarFill;
|
||||
|
||||
[Header("Control UI")]
|
||||
[SerializeField] private TextMeshProUGUI controlText;
|
||||
|
||||
[Header("Prompt UI")]
|
||||
[Tooltip("CompletePromptText 또는 CollectPromptText를 연결하세요.")]
|
||||
[SerializeField] private TextMeshProUGUI promptText;
|
||||
|
||||
[Header("Shell UI")]
|
||||
[SerializeField] private TextMeshProUGUI shellStateText;
|
||||
|
||||
[Header("Movement Arrows")]
|
||||
[SerializeField] private Image upArrow;
|
||||
[SerializeField] private Image downArrow;
|
||||
[SerializeField] private Image leftArrow;
|
||||
[SerializeField] private Image rightArrow;
|
||||
|
||||
[SerializeField] private Color activeColor = Color.white;
|
||||
[SerializeField] private Color inactiveColor = new Color(1f, 1f, 1f, 0.2f);
|
||||
|
||||
[Header("Reward Panel")]
|
||||
[SerializeField] private GameObject rewardPanel;
|
||||
[SerializeField] private TextMeshProUGUI rewardText;
|
||||
[SerializeField] private TextMeshProUGUI resultSubText;
|
||||
|
||||
[Header("Text Colors")]
|
||||
[SerializeField] private Color normalColor = Color.white;
|
||||
[SerializeField] private Color reversedColor = new Color(1f, 0.25f, 0.25f);
|
||||
[SerializeField] private Color openColor = new Color(0.3f, 1f, 0.65f);
|
||||
[SerializeField] private Color closedColor = new Color(1f, 0.25f, 0.25f);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (followRoot == null)
|
||||
followRoot = transform;
|
||||
|
||||
if (cameraTarget == null && Camera.main != null)
|
||||
cameraTarget = Camera.main.transform;
|
||||
|
||||
HideReward();
|
||||
ResetArrows();
|
||||
|
||||
SetControl(true);
|
||||
SetShellState(false);
|
||||
SetShellTimer(0f, false);
|
||||
SetPromptDefault();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
FollowCamera();
|
||||
}
|
||||
|
||||
public void SetControl(bool reversed)
|
||||
{
|
||||
if (controlText == null)
|
||||
return;
|
||||
|
||||
if (reversed)
|
||||
{
|
||||
controlText.text = "조작 반전 중";
|
||||
controlText.color = reversedColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
controlText.text = "일반 조작";
|
||||
controlText.color = normalColor;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPromptDefault()
|
||||
{
|
||||
if (promptText == null)
|
||||
return;
|
||||
|
||||
promptText.text = "조개가 열렸을 때 트리거를 누르세요";
|
||||
promptText.color = normalColor;
|
||||
}
|
||||
|
||||
public void SetPromptFarFromShell()
|
||||
{
|
||||
if (promptText == null)
|
||||
return;
|
||||
|
||||
promptText.text = "조개 가까이 이동하세요";
|
||||
promptText.color = normalColor;
|
||||
}
|
||||
|
||||
public void SetPromptNearShell(bool isOpen)
|
||||
{
|
||||
if (promptText == null)
|
||||
return;
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
promptText.text = "지금 트리거를 누르세요!";
|
||||
promptText.color = openColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
promptText.text = "조개가 열릴 때까지 기다리세요";
|
||||
promptText.color = closedColor;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShellTimer(float value01, bool isOpen)
|
||||
{
|
||||
if (timingBarFill == null)
|
||||
return;
|
||||
|
||||
timingBarFill.fillAmount = Mathf.Clamp01(value01);
|
||||
timingBarFill.color = isOpen ? openColor : closedColor;
|
||||
}
|
||||
|
||||
public void SetShellState(bool isOpen)
|
||||
{
|
||||
if (shellStateText == null)
|
||||
return;
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
shellStateText.text = "조개 상태: 열림";
|
||||
shellStateText.color = openColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
shellStateText.text = "조개 상태: 닫힘";
|
||||
shellStateText.color = closedColor;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMovementDebug(Vector2 input, Vector2 output)
|
||||
{
|
||||
UpdateArrows(output);
|
||||
}
|
||||
|
||||
private void UpdateArrows(Vector2 output)
|
||||
{
|
||||
ResetArrows();
|
||||
|
||||
if (output.y > 0.1f && upArrow != null)
|
||||
upArrow.color = activeColor;
|
||||
|
||||
if (output.y < -0.1f && downArrow != null)
|
||||
downArrow.color = activeColor;
|
||||
|
||||
if (output.x < -0.1f && leftArrow != null)
|
||||
leftArrow.color = activeColor;
|
||||
|
||||
if (output.x > 0.1f && rightArrow != null)
|
||||
rightArrow.color = activeColor;
|
||||
}
|
||||
|
||||
private void ResetArrows()
|
||||
{
|
||||
if (upArrow != null)
|
||||
upArrow.color = inactiveColor;
|
||||
|
||||
if (downArrow != null)
|
||||
downArrow.color = inactiveColor;
|
||||
|
||||
if (leftArrow != null)
|
||||
leftArrow.color = inactiveColor;
|
||||
|
||||
if (rightArrow != null)
|
||||
rightArrow.color = inactiveColor;
|
||||
}
|
||||
|
||||
public void ShowReward(bool success)
|
||||
{
|
||||
if (rewardPanel != null)
|
||||
{
|
||||
rewardPanel.SetActive(true);
|
||||
rewardPanel.transform.SetAsLastSibling();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[UIManager] RewardPanel이 연결되지 않았습니다.");
|
||||
}
|
||||
|
||||
if (rewardText != null)
|
||||
{
|
||||
rewardText.gameObject.SetActive(true);
|
||||
rewardText.text = success ? "성공!" : "실패!";
|
||||
rewardText.color = success ? openColor : closedColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[UIManager] RewardText가 연결되지 않았습니다.");
|
||||
}
|
||||
|
||||
if (resultSubText != null)
|
||||
{
|
||||
resultSubText.gameObject.SetActive(true);
|
||||
resultSubText.text = success ? "기억 조각을 획득했습니다" : "조개가 닫혀 있었습니다";
|
||||
resultSubText.color = normalColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[UIManager] ResultSubText가 연결되지 않았습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
public void HideReward()
|
||||
{
|
||||
if (rewardPanel != null)
|
||||
rewardPanel.SetActive(false);
|
||||
}
|
||||
|
||||
private void FollowCamera()
|
||||
{
|
||||
if (!followCamera)
|
||||
return;
|
||||
|
||||
if (followRoot == null)
|
||||
return;
|
||||
|
||||
if (cameraTarget == null)
|
||||
{
|
||||
if (Camera.main != null)
|
||||
cameraTarget = Camera.main.transform;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 forward = cameraTarget.forward;
|
||||
Vector3 right = cameraTarget.right;
|
||||
|
||||
if (useFlatForward)
|
||||
{
|
||||
forward.y = 0f;
|
||||
right.y = 0f;
|
||||
|
||||
if (forward.sqrMagnitude < 0.001f)
|
||||
{
|
||||
forward = cameraTarget.parent != null
|
||||
? cameraTarget.parent.forward
|
||||
: Vector3.forward;
|
||||
|
||||
forward.y = 0f;
|
||||
}
|
||||
|
||||
if (right.sqrMagnitude < 0.001f)
|
||||
{
|
||||
right = cameraTarget.parent != null
|
||||
? cameraTarget.parent.right
|
||||
: Vector3.right;
|
||||
|
||||
right.y = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
forward.Normalize();
|
||||
right.Normalize();
|
||||
|
||||
Vector3 targetPosition =
|
||||
cameraTarget.position +
|
||||
forward * followDistance +
|
||||
right * sideOffset;
|
||||
|
||||
targetPosition.y = cameraTarget.position.y + heightOffset;
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(forward, Vector3.up);
|
||||
|
||||
if (flipRotation)
|
||||
{
|
||||
targetRotation *= Quaternion.Euler(0f, 180f, 0f);
|
||||
}
|
||||
|
||||
if (smoothFollow)
|
||||
{
|
||||
followRoot.position = Vector3.Lerp(
|
||||
followRoot.position,
|
||||
targetPosition,
|
||||
Time.deltaTime * followSpeed
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
followRoot.position = targetPosition;
|
||||
}
|
||||
|
||||
if (smoothRotation)
|
||||
{
|
||||
followRoot.rotation = Quaternion.Slerp(
|
||||
followRoot.rotation,
|
||||
targetRotation,
|
||||
Time.deltaTime * rotationSpeed
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
followRoot.rotation = targetRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/My project/shell Scripts/UI/UIManager.cs.meta
Normal file
2
Assets/My project/shell Scripts/UI/UIManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 781be1a523f7f1e46839a06911bff32c
|
||||
Reference in New Issue
Block a user