2026-06-19 UI, UI로직
This commit is contained in:
266
Assets/My project/RoomSelect/Scripts/RoomEnterHandler.cs
Normal file
266
Assets/My project/RoomSelect/Scripts/RoomEnterHandler.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class RoomEnterHandler : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public class RoomTarget
|
||||
{
|
||||
public string roomId;
|
||||
public Transform spawnPoint;
|
||||
public GameObject roomRoot;
|
||||
public UnityEvent onEnter;
|
||||
}
|
||||
|
||||
[Header("Enter Mode")]
|
||||
[Tooltip("켜면 RoomData의 Scene Name으로 씬을 이동합니다. 끄면 같은 씬 안에서 위치 이동/방 오브젝트 활성화를 합니다.")]
|
||||
[SerializeField] private bool useSceneLoading = false;
|
||||
|
||||
[Header("Same Scene Movement")]
|
||||
[SerializeField] private Transform playerRoot;
|
||||
[SerializeField] private bool deactivateOtherRoomRoots = false;
|
||||
[SerializeField] private List<RoomTarget> roomTargets = new List<RoomTarget>();
|
||||
|
||||
[Header("UI")]
|
||||
[SerializeField] private GameObject roomSelectRoot;
|
||||
[SerializeField] private bool hideRoomSelectOnEnter = true;
|
||||
|
||||
[Header("Room Enter Message UI")]
|
||||
[Tooltip("방 입장 메시지 패널입니다.")]
|
||||
[SerializeField] private GameObject roomEnterMessagePanel;
|
||||
|
||||
[Tooltip("방 입장 메시지를 표시할 TextMeshProUGUI입니다.")]
|
||||
[SerializeField] private TextMeshProUGUI roomEnterMessageText;
|
||||
|
||||
[Tooltip("방 입장 메시지 전체 Root입니다. 보통 RoomEnterMessageCanvas를 넣습니다.")]
|
||||
[SerializeField] private Transform roomEnterMessageRoot;
|
||||
|
||||
[Tooltip("메시지가 화면에 표시되는 시간입니다.")]
|
||||
[SerializeField] private float roomEnterMessageDuration = 2f;
|
||||
|
||||
[Tooltip("{0} 자리에 RoomData의 RoomName이 들어갑니다.")]
|
||||
[SerializeField] private string roomEnterMessageFormat = "{0}에 입장했습니다";
|
||||
|
||||
[Header("Room Enter Message Position")]
|
||||
[Tooltip("메시지를 기준으로 배치할 카메라입니다. XR Origin > Camera Offset > Main Camera를 넣으세요.")]
|
||||
[SerializeField] private Transform messageCameraTarget;
|
||||
|
||||
[Tooltip("켜면 방 입장 메시지가 카메라 앞쪽으로 자동 이동합니다.")]
|
||||
[SerializeField] private bool placeMessageInFrontOfCamera = true;
|
||||
|
||||
[Tooltip("카메라 앞 몇 m 위치에 메시지를 띄울지 정합니다.")]
|
||||
[SerializeField] private float messageDistance = 1.6f;
|
||||
|
||||
[Tooltip("월드 기준 위/아래 위치 보정값입니다. 양수면 위로 올라갑니다.")]
|
||||
[SerializeField] private float messageHeightOffset = 0.25f;
|
||||
|
||||
[Tooltip("글자가 뒤집혀 보이면 켜세요.")]
|
||||
[SerializeField] private bool flipMessageRotation = false;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent onAnyRoomEntered;
|
||||
|
||||
private Coroutine roomEnterMessageCoroutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (messageCameraTarget == null && Camera.main != null)
|
||||
{
|
||||
messageCameraTarget = Camera.main.transform;
|
||||
}
|
||||
|
||||
if (roomEnterMessagePanel != null)
|
||||
{
|
||||
roomEnterMessagePanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void EnterRoom(RoomData roomData)
|
||||
{
|
||||
if (roomData == null)
|
||||
{
|
||||
Debug.LogWarning($"[{nameof(RoomEnterHandler)}] RoomData가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hideRoomSelectOnEnter && roomSelectRoot != null)
|
||||
{
|
||||
roomSelectRoot.SetActive(false);
|
||||
}
|
||||
|
||||
if (useSceneLoading)
|
||||
{
|
||||
EnterRoomByScene(roomData);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterRoomInSameScene(roomData);
|
||||
}
|
||||
|
||||
onAnyRoomEntered?.Invoke();
|
||||
}
|
||||
|
||||
private void EnterRoomByScene(RoomData roomData)
|
||||
{
|
||||
if (!roomData.HasSceneName())
|
||||
{
|
||||
Debug.LogWarning($"[{nameof(RoomEnterHandler)}] Scene Name이 비어 있습니다: {roomData.RoomName}");
|
||||
return;
|
||||
}
|
||||
|
||||
SceneManager.LoadScene(roomData.SceneName);
|
||||
}
|
||||
|
||||
private void EnterRoomInSameScene(RoomData roomData)
|
||||
{
|
||||
RoomTarget target = FindTarget(roomData.RoomId);
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
Debug.Log($"[{nameof(RoomEnterHandler)}] 방 입장 테스트: {roomData.RoomName} / roomId: {roomData.RoomId}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (deactivateOtherRoomRoots)
|
||||
{
|
||||
foreach (RoomTarget roomTarget in roomTargets)
|
||||
{
|
||||
if (roomTarget != null && roomTarget.roomRoot != null)
|
||||
{
|
||||
roomTarget.roomRoot.SetActive(roomTarget == target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (target.roomRoot != null)
|
||||
{
|
||||
target.roomRoot.SetActive(true);
|
||||
}
|
||||
|
||||
if (playerRoot != null && target.spawnPoint != null)
|
||||
{
|
||||
CharacterController controller = playerRoot.GetComponent<CharacterController>();
|
||||
bool hadController = controller != null;
|
||||
|
||||
if (hadController)
|
||||
{
|
||||
controller.enabled = false;
|
||||
}
|
||||
|
||||
playerRoot.SetPositionAndRotation(target.spawnPoint.position, target.spawnPoint.rotation);
|
||||
|
||||
if (hadController)
|
||||
{
|
||||
controller.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
target.onEnter?.Invoke();
|
||||
|
||||
ShowRoomEnterMessage(roomData);
|
||||
|
||||
Debug.Log($"[{nameof(RoomEnterHandler)}] 방 입장: {roomData.RoomName}");
|
||||
}
|
||||
|
||||
private RoomTarget FindTarget(string roomId)
|
||||
{
|
||||
return roomTargets.Find(target => target != null && target.roomId == roomId);
|
||||
}
|
||||
|
||||
private void ShowRoomEnterMessage(RoomData roomData)
|
||||
{
|
||||
if (roomData == null)
|
||||
return;
|
||||
|
||||
if (roomEnterMessagePanel == null || roomEnterMessageText == null)
|
||||
return;
|
||||
|
||||
if (roomEnterMessageRoot != null)
|
||||
{
|
||||
roomEnterMessageRoot.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
PlaceRoomEnterMessageInFrontOfCamera();
|
||||
|
||||
string roomName = roomData.RoomName;
|
||||
roomEnterMessageText.text = string.Format(roomEnterMessageFormat, roomName);
|
||||
|
||||
roomEnterMessagePanel.SetActive(true);
|
||||
|
||||
if (roomEnterMessageCoroutine != null)
|
||||
{
|
||||
StopCoroutine(roomEnterMessageCoroutine);
|
||||
}
|
||||
|
||||
roomEnterMessageCoroutine = StartCoroutine(HideRoomEnterMessageAfterDelay());
|
||||
}
|
||||
|
||||
private void PlaceRoomEnterMessageInFrontOfCamera()
|
||||
{
|
||||
if (!placeMessageInFrontOfCamera)
|
||||
return;
|
||||
|
||||
if (roomEnterMessageRoot == null)
|
||||
return;
|
||||
|
||||
if (messageCameraTarget == null)
|
||||
{
|
||||
if (Camera.main != null)
|
||||
{
|
||||
messageCameraTarget = Camera.main.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 카메라가 아래를 보고 있어도 메시지가 바닥으로 들어가지 않게
|
||||
// 카메라 forward에서 Y값을 제거해서 수평 방향만 사용
|
||||
Vector3 flatForward = messageCameraTarget.forward;
|
||||
flatForward.y = 0f;
|
||||
|
||||
if (flatForward.sqrMagnitude < 0.001f)
|
||||
{
|
||||
flatForward = messageCameraTarget.parent != null
|
||||
? messageCameraTarget.parent.forward
|
||||
: Vector3.forward;
|
||||
|
||||
flatForward.y = 0f;
|
||||
}
|
||||
|
||||
flatForward.Normalize();
|
||||
|
||||
Vector3 targetPosition =
|
||||
messageCameraTarget.position +
|
||||
flatForward * messageDistance +
|
||||
Vector3.up * messageHeightOffset;
|
||||
|
||||
roomEnterMessageRoot.position = targetPosition;
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(flatForward, Vector3.up);
|
||||
|
||||
if (flipMessageRotation)
|
||||
{
|
||||
targetRotation *= Quaternion.Euler(0f, 180f, 0f);
|
||||
}
|
||||
|
||||
roomEnterMessageRoot.rotation = targetRotation;
|
||||
}
|
||||
|
||||
private IEnumerator HideRoomEnterMessageAfterDelay()
|
||||
{
|
||||
yield return new WaitForSeconds(roomEnterMessageDuration);
|
||||
|
||||
if (roomEnterMessagePanel != null)
|
||||
{
|
||||
roomEnterMessagePanel.SetActive(false);
|
||||
}
|
||||
|
||||
roomEnterMessageCoroutine = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user