2026-06-19 UI, UI로직
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user