715 lines
22 KiB
C#
715 lines
22 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
[DisallowMultipleComponent]
|
|
public class MazeGameManager : MonoBehaviour
|
|
{
|
|
private enum MazePhase
|
|
{
|
|
SearchingExit,
|
|
Completed,
|
|
Failed
|
|
}
|
|
|
|
[Header("VR Player Reference")]
|
|
[Tooltip("XR Origin 안의 Main Camera Transform을 넣는 것을 추천합니다.")]
|
|
[SerializeField] private Transform playerTarget;
|
|
[SerializeField] private bool autoFindMainCameraIfMissing = true;
|
|
|
|
[Header("Managers")]
|
|
[SerializeField] private MazeUIManager mazeUI;
|
|
[SerializeField] private FootprintHintManager footprintHint;
|
|
|
|
[Header("Goal / Exit")]
|
|
[Tooltip("미로 출구입니다. 출구에 도착하면 기억의 조각을 획득하고 성공 처리됩니다.")]
|
|
[SerializeField] private Transform goal;
|
|
[SerializeField] private float goalDistance = 1.5f;
|
|
|
|
[Header("Exit Reward")]
|
|
[Tooltip("체크하면 출구에 도착했을 때 기억의 조각을 획득한 것으로 처리합니다.")]
|
|
[SerializeField] private bool giveMemoryPieceAtExit = true;
|
|
[Tooltip("출구 도착 시 켜거나 보여줄 기억의 조각 연출 오브젝트입니다. 없어도 됩니다.")]
|
|
[SerializeField] private GameObject memoryPieceRewardObject;
|
|
[Tooltip("Reset 시 기억의 조각 연출 오브젝트를 꺼둘지 정합니다.")]
|
|
[SerializeField] private bool hideRewardObjectOnReset = true;
|
|
|
|
[Header("Checkpoints")]
|
|
[Tooltip("출구까지 가는 길목입니다. 코너/갈림길마다 순서대로 배치하세요.")]
|
|
[SerializeField] private Transform[] checkpoints;
|
|
[SerializeField] private float checkpointDistance = 1.5f;
|
|
|
|
[Header("Checkpoint Rules")]
|
|
[Tooltip("체크하면 Checkpoint1 -> Checkpoint2 -> ... 순서대로만 인정됩니다.")]
|
|
[SerializeField] private bool enforceCheckpointOrder = true;
|
|
[Tooltip("체크하면 모든 체크포인트를 지난 뒤에만 Goal 성공이 됩니다.")]
|
|
[SerializeField] private bool requireAllCheckpointsBeforeGoal = true;
|
|
[Tooltip("체크포인트에 도착했을 때 다음 길목/출구 방향으로 발자국 힌트를 표시합니다.")]
|
|
[SerializeField] private bool showHintAtCheckpoint = true;
|
|
[Tooltip("체크하면 Direction enum보다 다음 체크포인트/출구 위치를 기준으로 발자국을 표시합니다.")]
|
|
[SerializeField] private bool useTargetBasedHint = true;
|
|
|
|
[Header("Checkpoint Directions")]
|
|
[Tooltip("useTargetBasedHint가 꺼져 있거나 다음 목표가 없을 때 사용하는 발자국 방향입니다.")]
|
|
[SerializeField] private MazeDirection[] checkpointDirections;
|
|
|
|
[Header("Hint Settings")]
|
|
[SerializeField] private int checkpointHintFootprintCount = 6;
|
|
[SerializeField] private float checkpointHintShowTime = 3f;
|
|
|
|
[Header("Compass Hint")]
|
|
[Tooltip("나침반이 있으면 1회 출구 경로 힌트를 보여줍니다.")]
|
|
[SerializeField] private bool hasCompass = true;
|
|
[SerializeField] private int compassHintFootprintCount = 8;
|
|
[SerializeField] private float compassHintShowTime = 4f;
|
|
|
|
[Header("Start Guide")]
|
|
[SerializeField] private bool showStartGuideMessage = true;
|
|
[TextArea]
|
|
[SerializeField] private string startGuideMessage = "푸른빛 정원에 들어왔다.\n손목의 나침반을 사용하면 빛나는 발자국 힌트를 볼 수 있다.";
|
|
|
|
[Header("VR Distance Option")]
|
|
[Tooltip("VR에서는 머리 높이가 달라지므로 보통 Y축 높이는 무시하는 것이 좋습니다.")]
|
|
[SerializeField] private bool ignoreHeight = true;
|
|
|
|
[Header("Result Buttons / Retry & Exit")]
|
|
[Tooltip("체크하면 MazeUIManager의 Compass / Retry / Exit 버튼 이벤트를 자동으로 연결합니다. 이 경우 Inspector의 OnClick에 같은 함수를 중복 연결하지 마세요.")]
|
|
[SerializeField] private bool wireUIButtonsAutomatically = true;
|
|
|
|
[Tooltip("다시하기 버튼을 눌렀을 때 플레이어를 시작 위치로 되돌릴지 정합니다.")]
|
|
[SerializeField] private bool movePlayerToRetrySpawn = false;
|
|
[Tooltip("이동시킬 플레이어 루트입니다. XR Origin 루트 오브젝트를 넣는 것을 추천합니다. 비워두면 Player Target을 이동합니다.")]
|
|
[SerializeField] private Transform playerRootToMoveOnRetry;
|
|
[Tooltip("다시하기 버튼을 눌렀을 때 이동할 시작 위치입니다.")]
|
|
[SerializeField] private Transform retrySpawnPoint;
|
|
|
|
[Tooltip("나가기/계속하기 버튼을 누르면 Result/HUD/Guide UI를 숨깁니다.")]
|
|
[SerializeField] private bool hideUIWhenExitButtonClicked = true;
|
|
[Tooltip("나가기/계속하기 버튼을 눌렀을 때 꺼둘 오브젝트입니다. 예: MazeRoot, MazeCanvas 등. 없어도 됩니다.")]
|
|
[SerializeField] private GameObject objectToDisableWhenExit;
|
|
[Tooltip("나가기/계속하기 버튼 클릭 후 호출할 추가 이벤트입니다. 다음 씬 이동, 다음 스토리 진행 등을 여기에 연결하세요.")]
|
|
[SerializeField] private UnityEvent onExitButtonClicked = new UnityEvent();
|
|
|
|
[Header("Moving Walls")]
|
|
[Tooltip("다시하기/초기화 때 시작 위치로 되돌릴 움직이는 벽들입니다. 비워두면 씬 안의 MovingMazeWall을 자동으로 찾을 수 있습니다.")]
|
|
[SerializeField] private MovingMazeWall[] movingWallsToReset;
|
|
[SerializeField] private bool autoFindMovingWallsIfEmpty = true;
|
|
[SerializeField] private bool resetMovingWallsOnMazeReset = true;
|
|
|
|
[Header("Debug")]
|
|
[SerializeField] private bool showDebugLogs = true;
|
|
[SerializeField] private bool drawDebugDistances = true;
|
|
|
|
private MazePhase phase;
|
|
private bool[] reached;
|
|
private bool hasMemoryPiece;
|
|
private bool compassUsed;
|
|
private int currentCheckpointIndex;
|
|
|
|
private void Awake()
|
|
{
|
|
ResolveReferences();
|
|
InitializeCheckpoints();
|
|
ResolveMovingWalls();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
ResetMaze();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
SetUIEventWiring(true);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
SetUIEventWiring(false);
|
|
}
|
|
|
|
private void SetUIEventWiring(bool addListeners)
|
|
{
|
|
if (!wireUIButtonsAutomatically || mazeUI == null)
|
|
return;
|
|
|
|
// 중복 연결 방지용으로 먼저 제거합니다.
|
|
mazeUI.OnCompassButtonClicked.RemoveListener(UseCompass);
|
|
mazeUI.OnRetryButtonClicked.RemoveListener(RetryMaze);
|
|
mazeUI.OnExitButtonClicked.RemoveListener(ExitMaze);
|
|
|
|
if (!addListeners)
|
|
return;
|
|
|
|
mazeUI.OnCompassButtonClicked.AddListener(UseCompass);
|
|
mazeUI.OnRetryButtonClicked.AddListener(RetryMaze);
|
|
mazeUI.OnExitButtonClicked.AddListener(ExitMaze);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (phase == MazePhase.Completed || phase == MazePhase.Failed)
|
|
return;
|
|
|
|
if (playerTarget == null)
|
|
return;
|
|
|
|
CheckCheckpoints();
|
|
CheckGoal();
|
|
}
|
|
|
|
private void ResolveReferences()
|
|
{
|
|
if (autoFindMainCameraIfMissing && playerTarget == null && Camera.main != null)
|
|
playerTarget = Camera.main.transform;
|
|
|
|
if (footprintHint != null && playerTarget != null)
|
|
footprintHint.SetPlayerTarget(playerTarget);
|
|
}
|
|
|
|
private void ResolveMovingWalls()
|
|
{
|
|
if (!autoFindMovingWallsIfEmpty)
|
|
return;
|
|
|
|
if (movingWallsToReset != null && movingWallsToReset.Length > 0)
|
|
return;
|
|
|
|
#if UNITY_2023_1_OR_NEWER
|
|
movingWallsToReset = FindObjectsByType<MovingMazeWall>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
|
#else
|
|
movingWallsToReset = FindObjectsOfType<MovingMazeWall>(true);
|
|
#endif
|
|
}
|
|
|
|
private void InitializeCheckpoints()
|
|
{
|
|
int count = checkpoints != null ? checkpoints.Length : 0;
|
|
reached = new bool[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
reached[i] = checkpoints[i] == null;
|
|
}
|
|
|
|
private void CheckCheckpoints()
|
|
{
|
|
if (checkpoints == null || checkpoints.Length == 0)
|
|
return;
|
|
|
|
if (enforceCheckpointOrder)
|
|
CheckCurrentCheckpointOnly();
|
|
else
|
|
CheckAllCheckpoints();
|
|
}
|
|
|
|
private void CheckCurrentCheckpointOnly()
|
|
{
|
|
SkipNullCheckpointsInOrder();
|
|
|
|
if (currentCheckpointIndex >= checkpoints.Length)
|
|
return;
|
|
|
|
Transform checkpoint = checkpoints[currentCheckpointIndex];
|
|
|
|
float distance = GetDistance(playerTarget.position, checkpoint.position);
|
|
|
|
if (drawDebugDistances)
|
|
Debug.DrawLine(playerTarget.position, checkpoint.position, Color.yellow);
|
|
|
|
if (distance <= checkpointDistance)
|
|
{
|
|
int reachedIndex = currentCheckpointIndex;
|
|
|
|
reached[reachedIndex] = true;
|
|
currentCheckpointIndex++;
|
|
UpdateCheckpointUI();
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log($"MazeGameManager: Checkpoint {reachedIndex + 1} reached. NextIndex={currentCheckpointIndex}", this);
|
|
|
|
if (showHintAtCheckpoint)
|
|
ShowCheckpointHint(reachedIndex);
|
|
}
|
|
}
|
|
|
|
private void SkipNullCheckpointsInOrder()
|
|
{
|
|
while (checkpoints != null && currentCheckpointIndex < checkpoints.Length && checkpoints[currentCheckpointIndex] == null)
|
|
{
|
|
if (showDebugLogs)
|
|
Debug.Log($"MazeGameManager: Checkpoint {currentCheckpointIndex + 1} is null. It is skipped.", this);
|
|
|
|
reached[currentCheckpointIndex] = true;
|
|
currentCheckpointIndex++;
|
|
UpdateCheckpointUI();
|
|
}
|
|
}
|
|
|
|
private void CheckAllCheckpoints()
|
|
{
|
|
for (int i = 0; i < checkpoints.Length; i++)
|
|
{
|
|
if (reached[i])
|
|
continue;
|
|
|
|
if (checkpoints[i] == null)
|
|
{
|
|
reached[i] = true;
|
|
UpdateCheckpointUI();
|
|
continue;
|
|
}
|
|
|
|
float distance = GetDistance(playerTarget.position, checkpoints[i].position);
|
|
|
|
if (drawDebugDistances)
|
|
Debug.DrawLine(playerTarget.position, checkpoints[i].position, Color.yellow);
|
|
|
|
if (distance <= checkpointDistance)
|
|
{
|
|
reached[i] = true;
|
|
UpdateCheckpointUI();
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log($"MazeGameManager: Checkpoint {i + 1} reached.", this);
|
|
|
|
if (showHintAtCheckpoint)
|
|
ShowCheckpointHint(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ShowCheckpointHint(int checkpointIndex)
|
|
{
|
|
if (footprintHint == null)
|
|
return;
|
|
|
|
if (useTargetBasedHint)
|
|
{
|
|
Transform target = GetNextHintTarget();
|
|
|
|
if (target != null)
|
|
{
|
|
if (showDebugLogs)
|
|
Debug.Log($"MazeGameManager: Show checkpoint hint to {target.name}.", this);
|
|
|
|
footprintHint.ShowFootprintsToTarget(target, checkpointHintFootprintCount, checkpointHintShowTime);
|
|
return;
|
|
}
|
|
}
|
|
|
|
MazeDirection direction = GetCheckpointDirection(checkpointIndex);
|
|
footprintHint.ShowFootprints(direction, checkpointHintFootprintCount, checkpointHintShowTime);
|
|
}
|
|
|
|
private void CheckGoal()
|
|
{
|
|
if (goal == null)
|
|
return;
|
|
|
|
if (drawDebugDistances)
|
|
Debug.DrawLine(playerTarget.position, goal.position, Color.green);
|
|
|
|
if (requireAllCheckpointsBeforeGoal && !AllCheckpointsReached())
|
|
return;
|
|
|
|
float distance = GetDistance(playerTarget.position, goal.position);
|
|
|
|
if (distance <= goalDistance)
|
|
CompleteAtExit();
|
|
}
|
|
|
|
public void UseCompass()
|
|
{
|
|
if (phase == MazePhase.Completed || phase == MazePhase.Failed)
|
|
return;
|
|
|
|
if (!hasCompass)
|
|
{
|
|
if (mazeUI != null)
|
|
mazeUI.OnCompassMissing();
|
|
|
|
return;
|
|
}
|
|
|
|
if (compassUsed)
|
|
{
|
|
if (mazeUI != null)
|
|
mazeUI.OnCompassAlreadyUsed();
|
|
|
|
return;
|
|
}
|
|
|
|
compassUsed = true;
|
|
|
|
bool hintShown = ShowHintToNextTarget(compassHintFootprintCount, compassHintShowTime);
|
|
|
|
if (!hintShown && footprintHint != null)
|
|
{
|
|
MazeDirection fallbackDirection = GetCurrentFallbackDirection();
|
|
footprintHint.ShowFootprints(fallbackDirection, compassHintFootprintCount, compassHintShowTime);
|
|
}
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log("MazeGameManager: Compass used.", this);
|
|
|
|
if (mazeUI != null)
|
|
{
|
|
mazeUI.SetCompassUsed();
|
|
mazeUI.ShowGuideMessage("나침반이 다음 길목을 가리킨다.\n빛나는 발자국을 따라가 보자.");
|
|
}
|
|
}
|
|
|
|
private bool ShowHintToNextTarget(int footprintCount, float showTime)
|
|
{
|
|
Transform target = GetNextHintTarget();
|
|
|
|
if (target == null || footprintHint == null)
|
|
return false;
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log($"MazeGameManager: Show hint to target: {target.name}", this);
|
|
|
|
footprintHint.ShowFootprintsToTarget(target, footprintCount, showTime);
|
|
return true;
|
|
}
|
|
|
|
private Transform GetNextHintTarget()
|
|
{
|
|
if (enforceCheckpointOrder && checkpoints != null)
|
|
{
|
|
for (int i = currentCheckpointIndex; i < checkpoints.Length; i++)
|
|
{
|
|
if (checkpoints[i] != null && (reached == null || i >= reached.Length || !reached[i]))
|
|
return checkpoints[i];
|
|
}
|
|
}
|
|
|
|
return goal;
|
|
}
|
|
|
|
private MazeDirection GetCurrentFallbackDirection()
|
|
{
|
|
int index = enforceCheckpointOrder ? currentCheckpointIndex : 0;
|
|
return GetCheckpointDirection(index);
|
|
}
|
|
|
|
private bool AllCheckpointsReached()
|
|
{
|
|
if (checkpoints == null || checkpoints.Length == 0)
|
|
return true;
|
|
|
|
if (reached == null || reached.Length != checkpoints.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < reached.Length; i++)
|
|
{
|
|
if (checkpoints[i] == null)
|
|
continue;
|
|
|
|
if (!reached[i])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private int GetCheckpointCount()
|
|
{
|
|
if (checkpoints == null)
|
|
return 0;
|
|
|
|
int count = 0;
|
|
|
|
for (int i = 0; i < checkpoints.Length; i++)
|
|
{
|
|
if (checkpoints[i] != null)
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private int GetReachedCheckpointCount()
|
|
{
|
|
if (reached == null || checkpoints == null)
|
|
return 0;
|
|
|
|
int count = 0;
|
|
|
|
int length = Mathf.Min(reached.Length, checkpoints.Length);
|
|
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
if (checkpoints[i] != null && reached[i])
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private void UpdateCheckpointUI()
|
|
{
|
|
if (mazeUI != null)
|
|
mazeUI.SetCheckpointProgress(GetReachedCheckpointCount(), GetCheckpointCount());
|
|
}
|
|
|
|
private MazeDirection GetCheckpointDirection(int index)
|
|
{
|
|
if (checkpointDirections != null && index >= 0 && index < checkpointDirections.Length)
|
|
return checkpointDirections[index];
|
|
|
|
return MazeDirection.Forward;
|
|
}
|
|
|
|
private void CompleteAtExit()
|
|
{
|
|
if (phase == MazePhase.Completed || phase == MazePhase.Failed)
|
|
return;
|
|
|
|
phase = MazePhase.Completed;
|
|
|
|
if (giveMemoryPieceAtExit)
|
|
hasMemoryPiece = true;
|
|
|
|
if (memoryPieceRewardObject != null)
|
|
memoryPieceRewardObject.SetActive(true);
|
|
|
|
if (footprintHint != null)
|
|
footprintHint.ClearFootprints();
|
|
|
|
UpdateCheckpointUI();
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log("MazeGameManager: Maze completed at exit.", this);
|
|
|
|
if (mazeUI != null)
|
|
{
|
|
mazeUI.SetMemoryPieceState(hasMemoryPiece);
|
|
|
|
int reachedCheckpointCount = GetReachedCheckpointCount();
|
|
int totalCheckpointCount = GetCheckpointCount();
|
|
|
|
if (giveMemoryPieceAtExit)
|
|
{
|
|
mazeUI.ShowSuccessResult(
|
|
"기억의 조각을 되찾았다!",
|
|
"별빛 발자국을 따라 미로를 통과했습니다.",
|
|
hasMemoryPiece,
|
|
reachedCheckpointCount,
|
|
totalCheckpointCount
|
|
);
|
|
}
|
|
else
|
|
{
|
|
mazeUI.ShowSuccessResult(
|
|
"미로 탈출 성공!",
|
|
"발자국 힌트를 따라 미로 출구에 도착했습니다.",
|
|
false,
|
|
reachedCheckpointCount,
|
|
totalCheckpointCount
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Fail()
|
|
{
|
|
Fail("길을 잃었다...");
|
|
}
|
|
|
|
public void Fail(string message)
|
|
{
|
|
if (phase == MazePhase.Completed || phase == MazePhase.Failed)
|
|
return;
|
|
|
|
phase = MazePhase.Failed;
|
|
|
|
if (footprintHint != null)
|
|
footprintHint.ClearFootprints();
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log($"MazeGameManager: Maze failed. Message={message}", this);
|
|
|
|
if (mazeUI != null)
|
|
mazeUI.ShowFail(message);
|
|
}
|
|
|
|
public void RetryMaze()
|
|
{
|
|
ResolveReferences();
|
|
MovePlayerToRetrySpawnIfNeeded();
|
|
ResetMazeStateOnly();
|
|
}
|
|
|
|
public void ResetMaze()
|
|
{
|
|
ResolveReferences();
|
|
ResetMazeStateOnly();
|
|
}
|
|
|
|
private void ResetMazeStateOnly()
|
|
{
|
|
phase = MazePhase.SearchingExit;
|
|
hasMemoryPiece = false;
|
|
compassUsed = false;
|
|
currentCheckpointIndex = 0;
|
|
|
|
if (reached == null || reached.Length != (checkpoints != null ? checkpoints.Length : 0))
|
|
InitializeCheckpoints();
|
|
else
|
|
{
|
|
for (int i = 0; i < reached.Length; i++)
|
|
reached[i] = checkpoints != null && i < checkpoints.Length && checkpoints[i] == null;
|
|
}
|
|
|
|
if (memoryPieceRewardObject != null && hideRewardObjectOnReset)
|
|
memoryPieceRewardObject.SetActive(false);
|
|
|
|
if (footprintHint != null)
|
|
footprintHint.ClearFootprints();
|
|
|
|
ResetMovingWallsIfNeeded();
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log("MazeGameManager: Maze reset.", this);
|
|
|
|
if (mazeUI != null)
|
|
{
|
|
mazeUI.ResetUI(hasCompass);
|
|
mazeUI.SetStartObjective();
|
|
mazeUI.SetCheckpointProgress(0, GetCheckpointCount());
|
|
mazeUI.SetMemoryPieceState(false);
|
|
|
|
if (showStartGuideMessage)
|
|
mazeUI.ShowGuideMessage(startGuideMessage);
|
|
}
|
|
}
|
|
|
|
private void ResetMovingWallsIfNeeded()
|
|
{
|
|
if (!resetMovingWallsOnMazeReset)
|
|
return;
|
|
|
|
ResolveMovingWalls();
|
|
|
|
if (movingWallsToReset == null)
|
|
return;
|
|
|
|
for (int i = 0; i < movingWallsToReset.Length; i++)
|
|
{
|
|
if (movingWallsToReset[i] != null)
|
|
movingWallsToReset[i].ResetToInitialState();
|
|
}
|
|
}
|
|
|
|
private void MovePlayerToRetrySpawnIfNeeded()
|
|
{
|
|
if (!movePlayerToRetrySpawn || retrySpawnPoint == null)
|
|
return;
|
|
|
|
Transform targetRoot = playerRootToMoveOnRetry;
|
|
|
|
if (targetRoot == null)
|
|
targetRoot = playerTarget;
|
|
|
|
if (targetRoot == null)
|
|
return;
|
|
|
|
targetRoot.SetPositionAndRotation(retrySpawnPoint.position, retrySpawnPoint.rotation);
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log("MazeGameManager: Player moved to retry spawn point.", this);
|
|
}
|
|
|
|
public void ExitMaze()
|
|
{
|
|
if (footprintHint != null)
|
|
footprintHint.ClearFootprints();
|
|
|
|
if (mazeUI != null && hideUIWhenExitButtonClicked)
|
|
{
|
|
mazeUI.HideResult();
|
|
mazeUI.HideHUD();
|
|
mazeUI.HideGuideImmediately();
|
|
}
|
|
|
|
if (objectToDisableWhenExit != null)
|
|
objectToDisableWhenExit.SetActive(false);
|
|
|
|
onExitButtonClicked?.Invoke();
|
|
|
|
if (showDebugLogs)
|
|
Debug.Log("MazeGameManager: Exit / Continue button clicked.", this);
|
|
}
|
|
|
|
public void SetHasCompass(bool value)
|
|
{
|
|
hasCompass = value;
|
|
|
|
if (mazeUI != null)
|
|
{
|
|
if (compassUsed)
|
|
mazeUI.SetCompassUsed();
|
|
else if (hasCompass)
|
|
mazeUI.SetCompassAvailable();
|
|
else
|
|
mazeUI.SetCompassNone();
|
|
}
|
|
}
|
|
|
|
public bool HasMemoryPiece()
|
|
{
|
|
return hasMemoryPiece;
|
|
}
|
|
|
|
public bool IsCompassUsed()
|
|
{
|
|
return compassUsed;
|
|
}
|
|
|
|
private float GetDistance(Vector3 a, Vector3 b)
|
|
{
|
|
if (ignoreHeight)
|
|
{
|
|
a.y = 0f;
|
|
b.y = 0f;
|
|
}
|
|
|
|
return Vector3.Distance(a, b);
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnValidate()
|
|
{
|
|
goalDistance = Mathf.Max(0.01f, goalDistance);
|
|
checkpointDistance = Mathf.Max(0.01f, checkpointDistance);
|
|
checkpointHintFootprintCount = Mathf.Max(1, checkpointHintFootprintCount);
|
|
checkpointHintShowTime = Mathf.Max(0f, checkpointHintShowTime);
|
|
compassHintFootprintCount = Mathf.Max(1, compassHintFootprintCount);
|
|
compassHintShowTime = Mathf.Max(0f, compassHintShowTime);
|
|
|
|
if (playerTarget == null && !autoFindMainCameraIfMissing)
|
|
Debug.LogWarning("MazeGameManager: Player Target이 비어 있습니다. XR Origin 안의 Main Camera를 넣는 것을 추천합니다.", this);
|
|
|
|
if (goal == null)
|
|
Debug.LogWarning("MazeGameManager: Goal이 비어 있습니다. 출구 오브젝트를 연결하세요.", this);
|
|
|
|
if (mazeUI == null)
|
|
Debug.LogWarning("MazeGameManager: Maze UI가 비어 있습니다.", this);
|
|
|
|
if (footprintHint == null)
|
|
Debug.LogWarning("MazeGameManager: Footprint Hint가 비어 있습니다.", this);
|
|
|
|
if (requireAllCheckpointsBeforeGoal && (checkpoints == null || checkpoints.Length == 0))
|
|
Debug.LogWarning("MazeGameManager: Require All Checkpoints Before Goal이 켜져 있지만 Checkpoints가 비어 있습니다.", this);
|
|
|
|
if (checkpoints != null && checkpointDirections != null && checkpointDirections.Length > 0 && checkpoints.Length != checkpointDirections.Length)
|
|
{
|
|
Debug.LogWarning(
|
|
"MazeGameManager: Checkpoints 배열과 Checkpoint Directions 배열의 길이가 다릅니다. 각 체크포인트와 방향은 같은 인덱스로 1:1 매칭하는 것을 추천합니다.",
|
|
this
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
}
|