diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj index 76af8ee..4c237bb 100644 --- a/Assembly-CSharp.csproj +++ b/Assembly-CSharp.csproj @@ -68,6 +68,7 @@ + diff --git a/Assets/Prefabs/Gate.prefab b/Assets/Prefabs/Gate.prefab index c19a8da..5007636 100644 --- a/Assets/Prefabs/Gate.prefab +++ b/Assets/Prefabs/Gate.prefab @@ -1,5 +1,67 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1 &529351500009441178 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9184257640407244267} + m_Layer: 0 + m_Name: FrontExitPoint + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9184257640407244267 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 529351500009441178} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 7180212943015590733} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2607013536690627895 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1475778514072803484} + m_Layer: 0 + m_Name: BackExitPoint + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1475778514072803484 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2607013536690627895} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 7180212943015590733} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2998551506809628252 GameObject: m_ObjectHideFlags: 0 @@ -11,6 +73,12 @@ GameObject: - component: {fileID: 7180212943015590733} - component: {fileID: 6249878823685746784} - component: {fileID: 321081944195966357} + - component: {fileID: 2557322720114216881} + - component: {fileID: 6718554010216801635} + - component: {fileID: 6336968390548483685} + - component: {fileID: 1893682255695519596} + - component: {fileID: 2622677457934171119} + - component: {fileID: 2624539771375861302} m_Layer: 0 m_Name: Gate m_TagString: Untagged @@ -32,6 +100,8 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1060510535957475976} + - {fileID: 9184257640407244267} + - {fileID: 1475778514072803484} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6249878823685746784 @@ -71,6 +141,150 @@ NavMeshObstacle: m_CarveOnlyStationary: 1 m_Center: {x: 0, y: 0, z: 0} m_TimeToStationary: 0.5 +--- !u!114 &2557322720114216881 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2998551506809628252} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject + GlobalObjectIdHash: 2819059600 + InScenePlacedSourceGlobalObjectIdHash: 1171432577 + DeferredDespawnTick: 0 + Ownership: 1 + AlwaysReplicateAsRoot: 0 + SynchronizeTransform: 1 + ActiveSceneSynchronization: 0 + SceneMigrationSynchronization: 0 + SpawnWithObservers: 1 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 +--- !u!114 &6718554010216801635 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2998551506809628252} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 71da9742096858d4d97441d6b84f13dd, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Northbound.TeamGate + ShowTopMostFoldoutHeaderGroup: 1 + allowedTeam: 1 + allowAllTeams: 0 + frontTrigger: {fileID: 1893682255695519596} + backTrigger: {fileID: 2622677457934171119} + frontExitPoint: {fileID: 9184257640407244267} + backExitPoint: {fileID: 1475778514072803484} + teleportCooldown: 1 + teleportEffectPrefab: {fileID: 0} + teleportSound: {fileID: 0} + blockedSound: {fileID: 0} + teamIndicatorRenderer: {fileID: 0} + allowedTeamMaterial: {fileID: 0} + blockedMaterial: {fileID: 0} +--- !u!114 &6336968390548483685 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2998551506809628252} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0ceedb9b012d848478813136b65738ae, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Northbound.Building + ShowTopMostFoldoutHeaderGroup: 1 + buildingData: {fileID: 0} + gridPosition: {x: 0, y: 0, z: 0} + rotation: 0 + initialTeam: 1 + initialOwnerId: 0 + useInitialOwner: 0 + showHealthBar: 1 + healthBarPrefab: {fileID: 0} + destroyEffectPrefab: {fileID: 0} + damageEffectPrefab: {fileID: 0} + effectSpawnPoint: {fileID: 0} + showGridBounds: 1 + gridBoundsColor: {r: 0, g: 1, b: 1, a: 1} +--- !u!65 &1893682255695519596 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2998551506809628252} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 8, y: 8, z: 1} + m_Center: {x: 0, y: 0, z: -2} +--- !u!65 &2622677457934171119 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2998551506809628252} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 8, y: 8, z: 1} + m_Center: {x: 0, y: 0, z: 2} +--- !u!114 &2624539771375861302 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2998551506809628252} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6eeb5dc026fdf4b488bc7ae0138ab719, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.AI.Navigation::Unity.AI.Navigation.NavMeshLink + m_SerializedVersion: 1 + m_AgentTypeID: 0 + m_StartPoint: {x: 0, y: 0, z: -2.5} + m_EndPoint: {x: 0, y: 0, z: 2.5} + m_StartTransform: {fileID: 0} + m_EndTransform: {fileID: 0} + m_Activated: 1 + m_Width: 0 + m_CostModifier: 1 + m_IsOverridingCost: 0 + m_Bidirectional: 1 + m_AutoUpdatePosition: 0 + m_Area: 0 --- !u!1001 &665699090875585891 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/Player.prefab b/Assets/Prefabs/Player.prefab index b991c6c..d3778f9 100644 --- a/Assets/Prefabs/Player.prefab +++ b/Assets/Prefabs/Player.prefab @@ -129,12 +129,12 @@ CharacterController: m_Enabled: 1 serializedVersion: 3 m_Height: 2 - m_Radius: 0.5 + m_Radius: 0.3 m_SlopeLimit: 45 m_StepOffset: 0.3 m_SkinWidth: 0.08 m_MinMoveDistance: 0.001 - m_Center: {x: 0, y: 0, z: 0} + m_Center: {x: 0, y: 1, z: 0} --- !u!114 &1883169379180791275 MonoBehaviour: m_ObjectHideFlags: 0 @@ -156,6 +156,8 @@ MonoBehaviour: invalidMaterial: {fileID: 0} selectedBuildingIndex: 0 showGridBounds: 1 + enableDragBuilding: 1 + maxDragBuildingCount: 50 --- !u!114 &8729870597719024730 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Scenes/GameMain.unity b/Assets/Scenes/GameMain.unity index b23c747..d24bb00 100644 --- a/Assets/Scenes/GameMain.unity +++ b/Assets/Scenes/GameMain.unity @@ -1872,6 +1872,11 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1354840086 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} + m_PrefabInstance: {fileID: 8960047168891090775} + m_PrefabAsset: {fileID: 0} --- !u!1 &1433142230 GameObject: m_ObjectHideFlags: 0 @@ -3147,13 +3152,70 @@ PrefabInstance: m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] - m_AddedComponents: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} + insertIndex: -1 + addedObject: {fileID: 8960047168891090780} + - targetCorrespondingSourceObject: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} + insertIndex: -1 + addedObject: {fileID: 8960047168891090779} m_SourcePrefab: {fileID: 100100000, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} --- !u!4 &8960047168891090776 stripped Transform: m_CorrespondingSourceObject: {fileID: 7180212943015590733, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} m_PrefabInstance: {fileID: 8960047168891090775} m_PrefabAsset: {fileID: 0} +--- !u!114 &8960047168891090779 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1354840086} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject + GlobalObjectIdHash: 1203277090 + InScenePlacedSourceGlobalObjectIdHash: 0 + DeferredDespawnTick: 0 + Ownership: 1 + AlwaysReplicateAsRoot: 0 + SynchronizeTransform: 1 + ActiveSceneSynchronization: 0 + SceneMigrationSynchronization: 0 + SpawnWithObservers: 1 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 +--- !u!114 &8960047168891090780 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1354840086} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 71da9742096858d4d97441d6b84f13dd, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Northbound.TeamGate + ShowTopMostFoldoutHeaderGroup: 1 + allowedTeam: 1 + allowAllTeams: 0 + frontTrigger: {fileID: 0} + backTrigger: {fileID: 0} + frontExitPoint: {fileID: 0} + backExitPoint: {fileID: 0} + teleportCooldown: 1 + teleportEffectPrefab: {fileID: 0} + teleportSound: {fileID: 0} + blockedSound: {fileID: 0} + teamIndicatorRenderer: {fileID: 0} + allowedTeamMaterial: {fileID: 0} + blockedMaterial: {fileID: 0} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/TeamGate.cs b/Assets/Scripts/TeamGate.cs new file mode 100644 index 0000000..551870e --- /dev/null +++ b/Assets/Scripts/TeamGate.cs @@ -0,0 +1,386 @@ +using Unity.Netcode; +using UnityEngine; +using System.Collections.Generic; + +namespace Northbound +{ + /// + /// 특정 팀만 순간이동으로 통과할 수 있는 성문 + /// 허용되지 않은 팀은 물리적으로 막힘 + /// + public class TeamGate : NetworkBehaviour + { + [Header("Gate Settings")] + [Tooltip("통과를 허용할 팀")] + public TeamType allowedTeam = TeamType.Player; + + [Tooltip("모든 팀 통과 허용 (공용 문)")] + public bool allowAllTeams = false; + + [Header("Teleport Settings")] + [Tooltip("트리거 콜라이더 (문 앞면)")] + public Collider frontTrigger; + + [Tooltip("트리거 콜라이더 (문 뒷면)")] + public Collider backTrigger; + + [Tooltip("앞면 텔레포트 목적지 (문 뒤)")] + public Transform frontExitPoint; + + [Tooltip("뒷면 텔레포트 목적지 (문 앞)")] + public Transform backExitPoint; + + [Tooltip("텔레포트 쿨다운 (초) - 연속 텔레포트 방지")] + public float teleportCooldown = 1f; + + [Header("Visual Feedback")] + public GameObject teleportEffectPrefab; + public AudioClip teleportSound; + public AudioClip blockedSound; + + [Header("Team Indicator")] + [Tooltip("팀 표시용 렌더러 (깃발, 조명 등)")] + public Renderer teamIndicatorRenderer; + + [Tooltip("허용 팀 머티리얼")] + public Material allowedTeamMaterial; + + [Tooltip("차단 표시 머티리얼")] + public Material blockedMaterial; + + // 쿨다운 추적 + private Dictionary _lastTeleportTime = new Dictionary(); + + // 트리거별 진입 방향 추적 + private Dictionary _triggerStates = new Dictionary(); + + private AudioSource _audioSource; + + private void Awake() + { + _audioSource = GetComponent(); + if (_audioSource == null && (teleportSound != null || blockedSound != null)) + { + _audioSource = gameObject.AddComponent(); + _audioSource.spatialBlend = 1f; + _audioSource.maxDistance = 20f; + } + + // 트리거 설정 확인 + if (frontTrigger != null) + { + frontTrigger.isTrigger = true; + } + else + { + Debug.LogError($"[TeamGate] {gameObject.name}: Front Trigger가 설정되지 않았습니다!"); + } + + if (backTrigger != null) + { + backTrigger.isTrigger = true; + } + else + { + Debug.LogError($"[TeamGate] {gameObject.name}: Back Trigger가 설정되지 않았습니다!"); + } + + // 출구 지점 확인 + if (frontExitPoint == null || backExitPoint == null) + { + Debug.LogError($"[TeamGate] {gameObject.name}: Exit Points가 설정되지 않았습니다!"); + } + + UpdateVisual(); + } + + private void OnTriggerEnter(Collider other) + { + // 서버에서만 텔레포트 처리 + if (!IsServer) return; + + // 어느 트리거에 진입했는지 확인 + Collider triggeredCollider = null; + Transform exitPoint = null; + + if (other.bounds.Intersects(frontTrigger.bounds)) + { + triggeredCollider = frontTrigger; + exitPoint = frontExitPoint; + } + else if (other.bounds.Intersects(backTrigger.bounds)) + { + triggeredCollider = backTrigger; + exitPoint = backExitPoint; + } + + if (triggeredCollider == null || exitPoint == null) + return; + + // 이미 트리거 안에 있으면 무시 (연속 텔레포트 방지) + if (_triggerStates.ContainsKey(other) && _triggerStates[other]) + return; + + _triggerStates[other] = true; + + // 팀 정보 가져오기 + ITeamMember teamMember = other.GetComponent(); + if (teamMember == null) + { + teamMember = other.GetComponentInParent(); + } + + if (teamMember == null) + { + _triggerStates[other] = false; + return; + } + + // 쿨다운 체크 + GameObject targetObject = teamMember as MonoBehaviour != null ? (teamMember as MonoBehaviour).gameObject : other.gameObject; + + if (_lastTeleportTime.ContainsKey(targetObject)) + { + float timeSinceLastTeleport = Time.time - _lastTeleportTime[targetObject]; + if (timeSinceLastTeleport < teleportCooldown) + { + _triggerStates[other] = false; + return; // 쿨다운 중 + } + } + + // 통과 가능 여부 확인 + bool canPass = CanPassThrough(teamMember); + + if (canPass) + { + // 텔레포트 실행 + ExecuteTeleport(other.gameObject, exitPoint.position); + _lastTeleportTime[targetObject] = Time.time; + + // 이펙트 및 사운드 + PlayTeleportEffectClientRpc(other.transform.position); + PlayTeleportEffectClientRpc(exitPoint.position); + + if (teleportSound != null) + { + PlaySoundClientRpc(true); + } + + Debug.Log($"[TeamGate] {other.name} 텔레포트: {triggeredCollider.name} -> {exitPoint.name}"); + } + else + { + // 차단됨 + if (blockedSound != null) + { + PlaySoundClientRpc(false); + } + + Debug.Log($"[TeamGate] {other.name} 차단됨 - 팀: {teamMember.GetTeam()}"); + } + + _triggerStates[other] = false; + } + + private void OnTriggerExit(Collider other) + { + // 트리거 상태 정리 + if (_triggerStates.ContainsKey(other)) + { + _triggerStates[other] = false; + } + } + + /// + /// 팀원이 통과 가능한지 확인 + /// + private bool CanPassThrough(ITeamMember teamMember) + { + if (allowAllTeams) + return true; + + return teamMember.GetTeam() == allowedTeam; + } + + /// + /// 오브젝트를 목적지로 텔레포트 + /// + private void ExecuteTeleport(GameObject target, Vector3 destination) + { + // CharacterController를 사용하는 경우 + CharacterController cc = target.GetComponent(); + if (cc != null) + { + cc.enabled = false; + target.transform.position = destination; + cc.enabled = true; + return; + } + + // Rigidbody를 사용하는 경우 + Rigidbody rb = target.GetComponent(); + if (rb != null) + { + rb.position = destination; + rb.linearVelocity = Vector3.zero; // 속도 초기화 + return; + } + + // NavMeshAgent를 사용하는 경우 (적 AI) + UnityEngine.AI.NavMeshAgent agent = target.GetComponent(); + if (agent != null) + { + agent.Warp(destination); + return; + } + + // 기본 Transform 이동 + target.transform.position = destination; + } + + /// + /// 텔레포트 이펙트 재생 (모든 클라이언트) + /// + [ClientRpc] + private void PlayTeleportEffectClientRpc(Vector3 position) + { + if (teleportEffectPrefab == null) return; + + GameObject effect = Instantiate(teleportEffectPrefab, position, Quaternion.identity); + Destroy(effect, 3f); + } + + /// + /// 사운드 재생 (모든 클라이언트) + /// + [ClientRpc] + private void PlaySoundClientRpc(bool isTeleport) + { + if (_audioSource == null) return; + + AudioClip clip = isTeleport ? teleportSound : blockedSound; + if (clip != null) + { + _audioSource.PlayOneShot(clip); + } + } + + /// + /// 허용 팀 변경 (서버에서 호출) + /// + [ServerRpc(RequireOwnership = false)] + public void ChangeAllowedTeamServerRpc(TeamType newTeam) + { + allowedTeam = newTeam; + UpdateVisualClientRpc(newTeam); + + Debug.Log($"[TeamGate] 허용 팀 변경: {newTeam}"); + } + + /// + /// 비주얼 업데이트 (모든 클라이언트) + /// + [ClientRpc] + private void UpdateVisualClientRpc(TeamType team) + { + allowedTeam = team; + UpdateVisual(); + } + + private void UpdateVisual() + { + if (teamIndicatorRenderer == null) + return; + + if (allowAllTeams) + { + if (allowedTeamMaterial != null) + teamIndicatorRenderer.material = allowedTeamMaterial; + } + else + { + // 팀별 색상 설정 (옵션) + switch (allowedTeam) + { + case TeamType.Player: + if (allowedTeamMaterial != null) + teamIndicatorRenderer.material = allowedTeamMaterial; + break; + default: + if (blockedMaterial != null) + teamIndicatorRenderer.material = blockedMaterial; + break; + } + } + } + + private void OnDrawGizmos() + { + // 트리거 영역 시각화 + Gizmos.color = new Color(0f, 1f, 1f, 0.3f); + + if (frontTrigger != null) + { + DrawTriggerGizmo(frontTrigger, Color.cyan); + } + + if (backTrigger != null) + { + DrawTriggerGizmo(backTrigger, Color.magenta); + } + + // 출구 지점 시각화 + if (frontExitPoint != null) + { + Gizmos.color = Color.green; + Gizmos.DrawWireSphere(frontExitPoint.position, 0.5f); + Gizmos.DrawLine(frontExitPoint.position, frontExitPoint.position + frontExitPoint.forward * 1f); + } + + if (backExitPoint != null) + { + Gizmos.color = Color.yellow; + Gizmos.DrawWireSphere(backExitPoint.position, 0.5f); + Gizmos.DrawLine(backExitPoint.position, backExitPoint.position + backExitPoint.forward * 1f); + } + + // 텔레포트 경로 표시 + if (frontTrigger != null && frontExitPoint != null) + { + Gizmos.color = Color.cyan; + Gizmos.DrawLine(frontTrigger.bounds.center, frontExitPoint.position); + } + + if (backTrigger != null && backExitPoint != null) + { + Gizmos.color = Color.magenta; + Gizmos.DrawLine(backTrigger.bounds.center, backExitPoint.position); + } + } + + private void DrawTriggerGizmo(Collider col, Color color) + { + Gizmos.color = color; + + if (col is BoxCollider box) + { + Matrix4x4 oldMatrix = Gizmos.matrix; + Gizmos.matrix = col.transform.localToWorldMatrix; + Gizmos.DrawWireCube(box.center, box.size); + Gizmos.matrix = oldMatrix; + } + else if (col is SphereCollider sphere) + { + Gizmos.DrawWireSphere(col.transform.position + sphere.center, sphere.radius); + } + } + + private void OnDestroy() + { + // 정리 + _lastTeleportTime.Clear(); + _triggerStates.Clear(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/TeamGate.cs.meta b/Assets/Scripts/TeamGate.cs.meta new file mode 100644 index 0000000..71a499c --- /dev/null +++ b/Assets/Scripts/TeamGate.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 71da9742096858d4d97441d6b84f13dd \ No newline at end of file