2026.06.24
This commit is contained in:
@@ -1,38 +1,115 @@
|
||||
using UnityEngine;
|
||||
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을 넣으세요.")]
|
||||
[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")]
|
||||
[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("각 체크포인트에 도착했을 때 보여줄 발자국 방향입니다.")]
|
||||
[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 isCompleted;
|
||||
private bool isFailed;
|
||||
private bool hasMemoryPiece;
|
||||
private bool compassUsed;
|
||||
private int currentCheckpointIndex;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
ResolveReferences();
|
||||
InitializeCheckpoints();
|
||||
ResolveMovingWalls();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@@ -40,9 +117,37 @@ 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 (isCompleted || isFailed)
|
||||
if (phase == MazePhase.Completed || phase == MazePhase.Failed)
|
||||
return;
|
||||
|
||||
if (playerTarget == null)
|
||||
@@ -52,10 +157,37 @@ private void Update()
|
||||
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()
|
||||
@@ -63,37 +195,475 @@ 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();
|
||||
|
||||
MazeDirection direction = GetCheckpointDirection(i);
|
||||
if (showDebugLogs)
|
||||
Debug.Log($"MazeGameManager: Checkpoint {i + 1} reached.", this);
|
||||
|
||||
if (footprintHint != null)
|
||||
footprintHint.ShowFootprints(direction);
|
||||
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)
|
||||
Complete();
|
||||
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)
|
||||
@@ -107,61 +677,38 @@ private float GetDistance(Vector3 a, Vector3 b)
|
||||
return Vector3.Distance(a, b);
|
||||
}
|
||||
|
||||
private MazeDirection GetCheckpointDirection(int index)
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
if (checkpointDirections != null &&
|
||||
index >= 0 &&
|
||||
index < checkpointDirections.Length)
|
||||
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)
|
||||
{
|
||||
return checkpointDirections[index];
|
||||
Debug.LogWarning(
|
||||
"MazeGameManager: Checkpoints 배열과 Checkpoint Directions 배열의 길이가 다릅니다. 각 체크포인트와 방향은 같은 인덱스로 1:1 매칭하는 것을 추천합니다.",
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
return MazeDirection.None;
|
||||
}
|
||||
|
||||
private void Complete()
|
||||
{
|
||||
if (isCompleted || isFailed)
|
||||
return;
|
||||
|
||||
isCompleted = true;
|
||||
|
||||
if (footprintHint != null)
|
||||
footprintHint.ClearFootprints();
|
||||
|
||||
if (mazeUI != null)
|
||||
mazeUI.ShowSuccess();
|
||||
}
|
||||
|
||||
public void Fail()
|
||||
{
|
||||
if (isCompleted || isFailed)
|
||||
return;
|
||||
|
||||
isFailed = true;
|
||||
|
||||
if (footprintHint != null)
|
||||
footprintHint.ClearFootprints();
|
||||
|
||||
if (mazeUI != null)
|
||||
mazeUI.ShowFail();
|
||||
}
|
||||
|
||||
public void ResetMaze()
|
||||
{
|
||||
isCompleted = false;
|
||||
isFailed = false;
|
||||
|
||||
if (reached == null || reached.Length != (checkpoints != null ? checkpoints.Length : 0))
|
||||
InitializeCheckpoints();
|
||||
|
||||
for (int i = 0; i < reached.Length; i++)
|
||||
reached[i] = false;
|
||||
|
||||
if (footprintHint != null)
|
||||
footprintHint.ClearFootprints();
|
||||
|
||||
if (mazeUI != null)
|
||||
mazeUI.HideReward();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user