게이트 이동 시스템 추가 및 기존 이동 매니저 정리

This commit is contained in:
dldydtn9755-crypto
2026-06-22 11:28:53 +09:00
parent f443c4a4de
commit 6fc20431e8
57 changed files with 767 additions and 435 deletions

View File

@@ -1,18 +0,0 @@
using UnityEngine;
public class RoomMoveButton : MonoBehaviour
{
[Header("이 버튼을 눌렀을 때 이동할 방 번호 입력")]
[SerializeField] private int targetRoomNumber;
public void OnClickMoveRoom()
{
if (RoomRouteManager.Instance == null)
{
Debug.LogError("RoomRouteManager가 없습니다.");
return;
}
RoomRouteManager.Instance.MoveToRoom(targetRoomNumber);
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 8eb4311a25437fd468487f681f4662e3

View File

@@ -1,92 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class RoomRouteDebugTester : MonoBehaviour
{
private List<RoomRouteManager.RoomData> currentChoices =
new List<RoomRouteManager.RoomData>();
private void Update()
{
if (RoomRouteManager.Instance == null)
{
return;
}
if (Keyboard.current == null)
{
return;
}
// C키: 방문 안 한 방 중 랜덤 후보 뽑기
if (Keyboard.current.cKey.wasPressedThisFrame)
{
currentChoices = RoomRouteManager.Instance.GetRandomNextRooms();
Debug.Log($"현재 선택 가능한 후보 개수: {currentChoices.Count}");
for (int i = 0; i < currentChoices.Count; i++)
{
Debug.Log($"{i + 1}번 선택지 → 방 번호: {currentChoices[i].roomNumber}, 씬 이름: {currentChoices[i].sceneName}");
}
}
// 1키: 첫 번째 후보 선택
if (Keyboard.current.digit1Key.wasPressedThisFrame)
{
MoveToChoice(0);
}
// 2키: 두 번째 후보 선택
if (Keyboard.current.digit2Key.wasPressedThisFrame)
{
MoveToChoice(1);
}
// T키: 방문 상태 확인
if (Keyboard.current.tKey.wasPressedThisFrame)
{
Debug.Log($"방문한 방 개수: {RoomRouteManager.Instance.VisitedRoomCount} / {RoomRouteManager.Instance.TotalRoomCount}");
Debug.Log($"현재 방 번호: {RoomRouteManager.Instance.CurrentRoomNumber}");
}
// X키: 테스트용 방문 기록 초기화
if (Keyboard.current.xKey.wasPressedThisFrame)
{
Debug.Log("방문 기록 초기화");
currentChoices.Clear();
RoomRouteManager.Instance.ResetVisitedRooms();
}
// F키: 모든 방 방문 후 마지막 씬 이동 테스트
if (Keyboard.current.fKey.wasPressedThisFrame)
{
Debug.Log("마지막 씬 이동 테스트");
RoomRouteManager.Instance.MoveToFinalScene();
}
}
private void MoveToChoice(int index)
{
if (currentChoices == null || currentChoices.Count == 0)
{
Debug.LogWarning("먼저 C키를 눌러 랜덤 후보를 뽑아야 합니다.");
return;
}
if (index < 0 || index >= currentChoices.Count)
{
Debug.LogWarning("해당 번호의 선택지가 없습니다.");
return;
}
int targetRoomNumber = currentChoices[index].roomNumber;
Debug.Log($"{index + 1}번 선택지 선택 → 방 {targetRoomNumber} 이동");
currentChoices.Clear();
RoomRouteManager.Instance.MoveToRoom(targetRoomNumber);
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 9e414802b02a03e469c541b086f805bb

View File

@@ -1,219 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
public class RoomRouteManager : MonoBehaviour
{
public static RoomRouteManager Instance;
[System.Serializable]
public class RoomData
{
[Header("방 번호 입력")]
public int roomNumber;
[Header("이 방에 해당하는 Scene 이름 입력")]
public string sceneName;
}
[Header("전체 방 정보 입력")]
[SerializeField] private List<RoomData> rooms = new List<RoomData>();
[Header("시작 방 번호 입력")]
[SerializeField] private int startRoomNumber;
[Header("랜덤 선택지 개수")]
[SerializeField] private int randomChoiceCount = 2;
[Header("모든 방 방문 후 이동할 마지막 Scene 이름")]
[SerializeField] private string finalSceneName;
private int _currentRoomNumber;
private readonly HashSet<int> _visitedRooms = new HashSet<int>();
public int CurrentRoomNumber => _currentRoomNumber;
public int VisitedRoomCount => _visitedRooms.Count;
public int TotalRoomCount => rooms.Count;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
_currentRoomNumber = startRoomNumber;
if (startRoomNumber != 0)
{
_visitedRooms.Add(startRoomNumber);
}
}
else
{
Destroy(gameObject);
}
}
// 방문하지 않은 방 전체 반환
public List<RoomData> GetUnvisitedRooms()
{
List<RoomData> result = new List<RoomData>();
foreach (RoomData room in rooms)
{
if (!_visitedRooms.Contains(room.roomNumber))
{
result.Add(room);
}
}
return result;
}
// 대화 선택지에 보여줄 랜덤 방 목록 반환
public List<RoomData> GetRandomNextRooms()
{
List<RoomData> unvisitedRooms = GetUnvisitedRooms();
List<RoomData> randomRooms = new List<RoomData>();
int count = Mathf.Min(randomChoiceCount, unvisitedRooms.Count);
for (int i = 0; i < count; i++)
{
int randomIndex = Random.Range(0, unvisitedRooms.Count);
randomRooms.Add(unvisitedRooms[randomIndex]);
unvisitedRooms.RemoveAt(randomIndex);
}
return randomRooms;
}
// 버튼이나 대화 선택지에서 호출
public void MoveToRoom(int roomNumber)
{
if (SceneLoadManager.Instance == null)
{
Debug.LogError("SceneLoadManager가 씬에 없습니다.");
return;
}
if (SceneLoadManager.Instance.IsChangingScene)
{
Debug.Log("이미 씬 이동 중입니다.");
return;
}
RoomData targetRoom = GetRoomData(roomNumber);
if (targetRoom == null)
{
Debug.LogWarning($"방 정보를 찾을 수 없습니다. 방 번호: {roomNumber}");
return;
}
if (_visitedRooms.Contains(roomNumber))
{
Debug.Log($"이미 방문한 방입니다. 방 번호: {roomNumber}");
return;
}
if (string.IsNullOrEmpty(targetRoom.sceneName))
{
Debug.LogWarning($"방 {roomNumber}의 Scene 이름이 비어있습니다.");
return;
}
_currentRoomNumber = roomNumber;
_visitedRooms.Add(roomNumber);
SceneLoadManager.Instance.RequestSceneChange(targetRoom.sceneName);
}
// 랜덤 방 하나로 바로 이동하고 싶을 때 사용
public void MoveToRandomRoom()
{
List<RoomData> unvisitedRooms = GetUnvisitedRooms();
if (unvisitedRooms.Count <= 0)
{
Debug.Log("방문하지 않은 방이 없습니다.");
if (IsAllRoomsVisited())
{
MoveToFinalScene();
}
return;
}
int randomIndex = Random.Range(0, unvisitedRooms.Count);
RoomData randomRoom = unvisitedRooms[randomIndex];
MoveToRoom(randomRoom.roomNumber);
}
public bool IsAllRoomsVisited()
{
return rooms.Count > 0 && _visitedRooms.Count >= rooms.Count;
}
public void MoveToFinalScene()
{
if (!IsAllRoomsVisited())
{
Debug.Log("아직 모든 방을 방문하지 않았습니다.");
return;
}
if (SceneLoadManager.Instance == null)
{
Debug.LogError("SceneLoadManager가 씬에 없습니다.");
return;
}
if (SceneLoadManager.Instance.IsChangingScene)
{
Debug.Log("이미 씬 이동 중입니다.");
return;
}
if (string.IsNullOrEmpty(finalSceneName))
{
Debug.LogWarning("마지막 Scene 이름이 비어있습니다.");
return;
}
SceneLoadManager.Instance.RequestSceneChange(finalSceneName);
}
public bool IsVisitedRoom(int roomNumber)
{
return _visitedRooms.Contains(roomNumber);
}
public void ResetVisitedRooms()
{
_visitedRooms.Clear();
_currentRoomNumber = startRoomNumber;
if (startRoomNumber != 0)
{
_visitedRooms.Add(startRoomNumber);
}
}
private RoomData GetRoomData(int roomNumber)
{
foreach (RoomData room in rooms)
{
if (room.roomNumber == roomNumber)
{
return room;
}
}
return null;
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 0a9c2a7a081e65e43806d1ecb3cca28c

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1583d3297de606648b62dde46dc74678
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
using UnityEngine;
public class GateOpenZone : MonoBehaviour
{
[Header("게이트 오픈 관리자")]
[SerializeField] private RoomClearGateController roomClearGateController;
[Header("Player Check")]
[SerializeField] private string playerTag = "Player";
private bool used = false;
private void OnTriggerEnter(Collider other)
{
if (used)
{
return;
}
if (!IsPlayer(other))
{
return;
}
if (roomClearGateController == null)
{
Debug.LogWarning("RoomClearGateController가 연결되지 않았습니다.");
return;
}
if (!roomClearGateController.IsRoomCleared)
{
Debug.Log("아직 방 클리어 전입니다. 게이트를 열지 않습니다.");
return;
}
used = true;
roomClearGateController.OpenClearGate();
}
private bool IsPlayer(Collider other)
{
if (other.CompareTag(playerTag))
{
return true;
}
if (other.transform.root.CompareTag(playerTag))
{
return true;
}
return false;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6986d649e7cfba74881058e544e2f727

View File

@@ -0,0 +1,134 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class RandomSceneRouteManager : MonoBehaviour
{
public static RandomSceneRouteManager Instance;
[Header("랜덤으로 이동할 방 Scene 이름들")]
[SerializeField] private string[] roomSceneNames;
[Header("모든 방 방문 후 이동할 마지막 Scene 이름")]
[SerializeField] private string finalSceneName;
private readonly HashSet<string> visitedScenes = new HashSet<string>();
private bool finalSceneUsed = false;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
public string GetNextSceneName()
{
string currentSceneName = SceneManager.GetActiveScene().name;
Debug.Log("현재 씬 이름: " + currentSceneName);
if (IsRoomScene(currentSceneName))
{
visitedScenes.Add(currentSceneName);
}
List<string> candidates = new List<string>();
foreach (string sceneName in roomSceneNames)
{
if (string.IsNullOrWhiteSpace(sceneName))
{
continue;
}
string cleanSceneName = sceneName.Trim();
if (cleanSceneName == currentSceneName)
{
continue;
}
if (visitedScenes.Contains(cleanSceneName))
{
continue;
}
candidates.Add(cleanSceneName);
}
if (candidates.Count > 0)
{
int randomIndex = Random.Range(0, candidates.Count);
string selectedSceneName = candidates[randomIndex];
visitedScenes.Add(selectedSceneName);
Debug.Log("랜덤으로 선택된 다음 씬: " + selectedSceneName);
return selectedSceneName;
}
if (!finalSceneUsed && !string.IsNullOrWhiteSpace(finalSceneName))
{
finalSceneUsed = true;
string cleanFinalSceneName = finalSceneName.Trim();
Debug.Log("모든 방 방문 완료. 마지막 씬으로 이동: " + cleanFinalSceneName);
return cleanFinalSceneName;
}
Debug.LogWarning("이동 가능한 다음 씬이 없습니다.");
return string.Empty;
}
private bool IsRoomScene(string sceneName)
{
foreach (string roomSceneName in roomSceneNames)
{
if (string.IsNullOrWhiteSpace(roomSceneName))
{
continue;
}
if (roomSceneName.Trim() == sceneName)
{
return true;
}
}
return false;
}
public void RequestRandomSceneChange()
{
if (SceneLoadManager.Instance == null)
{
Debug.LogError("SceneLoadManager가 없습니다.");
return;
}
string nextSceneName = GetNextSceneName();
if (string.IsNullOrEmpty(nextSceneName))
{
Debug.LogWarning("이동할 다음 씬 이름이 비어있습니다.");
return;
}
Debug.Log("랜덤 씬 이동 요청: " + nextSceneName);
SceneLoadManager.Instance.RequestSceneChange(nextSceneName);
}
public void ResetRoute()
{
visitedScenes.Clear();
finalSceneUsed = false;
Debug.Log("랜덤 방 방문 기록 초기화");
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a3fdc82688b5a7144956b337157facc9

View File

@@ -0,0 +1,53 @@
using UnityEngine;
public class RoomClearGateController : MonoBehaviour
{
[Header("방 클리어 후 열릴 게이트")]
[SerializeField] private RoomExitGate exitGate;
private bool isRoomCleared = false;
private bool gateOpened = false;
public bool IsRoomCleared => isRoomCleared;
// 블랙잭 최종 승리 후 호출
// 이 함수는 게이트를 바로 열지 않고, "방 클리어 완료" 상태만 저장함
public void MarkRoomCleared()
{
isRoomCleared = true;
Debug.Log("방 클리어 완료. 이제 오픈존에 들어가면 게이트가 열립니다.");
}
// 오픈존에 들어갔을 때 호출
public void OpenClearGate()
{
if (!isRoomCleared)
{
Debug.Log("아직 방 클리어 전이라 게이트를 열 수 없습니다.");
return;
}
if (gateOpened)
{
return;
}
gateOpened = true;
if (exitGate != null)
{
exitGate.OpenGate();
Debug.Log("방 클리어 게이트 오픈");
}
else
{
Debug.LogWarning("Exit Gate가 연결되지 않았습니다.");
}
}
public void ResetClearState()
{
isRoomCleared = false;
gateOpened = false;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 125cade625c9b644b842b1a4f20527e4

View File

@@ -0,0 +1,326 @@
using System.Collections;
using UnityEngine;
public class RoomExitGate : MonoBehaviour
{
[Header("Gate Root")]
[SerializeField] private GameObject gateVisualRoot;
[Header("Door")]
[SerializeField] private Transform leftDoor;
[SerializeField] private Transform rightDoor;
[Header("Door Open Angle")]
[SerializeField] private float leftOpenAngleY = 110f;
[SerializeField] private float rightOpenAngleY = -110f;
[Header("Portal")]
[SerializeField] private GameObject portalEffectRoot;
[SerializeField] private ParticleSystem[] portalParticles;
[Header("Open Effect")]
[SerializeField] private ParticleSystem openParticle;
[SerializeField] private AudioSource openSound;
[Header("Trigger")]
[SerializeField] private Collider gateTrigger;
[Header("Timing")]
[SerializeField] private float openDelay = 1.2f;
[SerializeField] private float openDuration = 1.2f;
[SerializeField] private float portalShowDelay = 0.3f;
[Header("Player Check")]
[SerializeField] private string playerTag = "Player";
[Header("Scene Move")]
[SerializeField] private bool useRandomScene = true;
[SerializeField] private string fallbackSceneName;
[SerializeField] private float sceneMoveDelay = 0.2f;
[Header("Start Setting")]
[SerializeField] private bool hideGateOnStart = true;
[SerializeField] private bool hidePortalOnStart = true;
private Quaternion leftClosedRotation;
private Quaternion rightClosedRotation;
private Quaternion leftOpenRotation;
private Quaternion rightOpenRotation;
private bool isOpened = false;
private bool isOpening = false;
private bool isEntering = false;
private void Awake()
{
CacheDoorRotations();
AutoFindPortalParticles();
PrepareTrigger();
PrepareStartState();
}
private void CacheDoorRotations()
{
if (leftDoor != null)
{
leftClosedRotation = leftDoor.localRotation;
leftOpenRotation = leftClosedRotation * Quaternion.Euler(0f, leftOpenAngleY, 0f);
}
if (rightDoor != null)
{
rightClosedRotation = rightDoor.localRotation;
rightOpenRotation = rightClosedRotation * Quaternion.Euler(0f, rightOpenAngleY, 0f);
}
}
private void AutoFindPortalParticles()
{
if ((portalParticles == null || portalParticles.Length == 0) && portalEffectRoot != null)
{
portalParticles = portalEffectRoot.GetComponentsInChildren<ParticleSystem>(true);
}
}
private void PrepareTrigger()
{
if (gateTrigger != null)
{
gateTrigger.enabled = false;
gateTrigger.isTrigger = true;
}
Rigidbody rb = GetComponent<Rigidbody>();
if (rb == null)
{
rb = gameObject.AddComponent<Rigidbody>();
}
rb.isKinematic = true;
rb.useGravity = false;
}
private void PrepareStartState()
{
if (hideGateOnStart && gateVisualRoot != null)
{
gateVisualRoot.SetActive(false);
}
if (hidePortalOnStart && portalEffectRoot != null)
{
portalEffectRoot.SetActive(false);
}
}
public void OpenGate()
{
if (isOpened || isOpening)
{
return;
}
StartCoroutine(OpenGateRoutine());
}
private IEnumerator OpenGateRoutine()
{
isOpening = true;
if (openParticle != null)
{
openParticle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
openParticle.Play();
}
if (openSound != null)
{
openSound.Play();
}
yield return new WaitForSeconds(openDelay);
if (gateVisualRoot != null)
{
gateVisualRoot.SetActive(true);
}
float timer = 0f;
bool portalShown = false;
while (timer < openDuration)
{
timer += Time.deltaTime;
float t = Mathf.Clamp01(timer / openDuration);
t = Mathf.SmoothStep(0f, 1f, t);
if (leftDoor != null)
{
leftDoor.localRotation = Quaternion.Slerp(leftClosedRotation, leftOpenRotation, t);
}
if (rightDoor != null)
{
rightDoor.localRotation = Quaternion.Slerp(rightClosedRotation, rightOpenRotation, t);
}
if (!portalShown && timer >= portalShowDelay)
{
ShowPortal();
portalShown = true;
}
yield return null;
}
if (leftDoor != null)
{
leftDoor.localRotation = leftOpenRotation;
}
if (rightDoor != null)
{
rightDoor.localRotation = rightOpenRotation;
}
ShowPortal();
if (gateTrigger != null)
{
gateTrigger.enabled = true;
}
isOpened = true;
isOpening = false;
Debug.Log("게이트 열림 완료");
}
private void ShowPortal()
{
if (portalEffectRoot != null && !portalEffectRoot.activeSelf)
{
portalEffectRoot.SetActive(true);
}
if (portalParticles == null)
{
return;
}
foreach (ParticleSystem particle in portalParticles)
{
if (particle != null && !particle.isPlaying)
{
particle.Play();
}
}
}
private void OnTriggerEnter(Collider other)
{
if (!isOpened || isEntering)
{
return;
}
if (!IsPlayer(other))
{
return;
}
StartCoroutine(EnterGateRoutine());
}
private bool IsPlayer(Collider other)
{
if (other.CompareTag(playerTag))
{
return true;
}
if (other.transform.root.CompareTag(playerTag))
{
return true;
}
return false;
}
private IEnumerator EnterGateRoutine()
{
isEntering = true;
yield return new WaitForSeconds(sceneMoveDelay);
string nextSceneName = GetNextSceneName();
if (string.IsNullOrEmpty(nextSceneName))
{
Debug.LogWarning("다음 씬 이름이 비어있습니다.");
isEntering = false;
yield break;
}
if (SceneLoadManager.Instance == null)
{
Debug.LogError("SceneLoadManager가 없습니다.");
isEntering = false;
yield break;
}
Debug.Log("게이트 진입, 이동할 씬: " + nextSceneName);
SceneLoadManager.Instance.RequestSceneChange(nextSceneName);
}
private string GetNextSceneName()
{
if (useRandomScene && RandomSceneRouteManager.Instance != null)
{
return RandomSceneRouteManager.Instance.GetNextSceneName();
}
return fallbackSceneName;
}
public void CloseGateImmediately()
{
StopAllCoroutines();
isOpened = false;
isOpening = false;
isEntering = false;
if (leftDoor != null)
{
leftDoor.localRotation = leftClosedRotation;
}
if (rightDoor != null)
{
rightDoor.localRotation = rightClosedRotation;
}
if (portalEffectRoot != null)
{
portalEffectRoot.SetActive(false);
}
if (gateTrigger != null)
{
gateTrigger.enabled = false;
}
if (openParticle != null)
{
openParticle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
}
if (hideGateOnStart && gateVisualRoot != null)
{
gateVisualRoot.SetActive(false);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 317d18e41b8485446ac641a9e1cd4ce2