105 lines
3.6 KiB
C#
105 lines
3.6 KiB
C#
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.AI;
|
||
|
|
||
|
public class TeleportManager : MonoBehaviour
|
||
|
{
|
||
|
public static TeleportManager Instance { get; private set; }
|
||
|
|
||
|
[Header("Optional fade (CanvasGroup with RaycastBlock)")]
|
||
|
public TeleportScreenFader screenFader; // drag a CanvasGroup-based fader (below)
|
||
|
[Range(0f, 2f)] public float fadeDuration = 0.25f;
|
||
|
|
||
|
// Prevent immediate re-triggering after arrival
|
||
|
private readonly Dictionary<GameObject, float> _entityCooldownUntil = new();
|
||
|
private readonly Dictionary<GameObject, string> _entityLastPortalId = new();
|
||
|
|
||
|
private void Awake()
|
||
|
{
|
||
|
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
|
||
|
Instance = this;
|
||
|
DontDestroyOnLoad(gameObject);
|
||
|
}
|
||
|
|
||
|
public bool CanUsePortal(GameObject entity, string portalId, float cooldownSeconds)
|
||
|
{
|
||
|
if (cooldownSeconds <= 0f) return true;
|
||
|
|
||
|
var now = Time.time;
|
||
|
if (_entityCooldownUntil.TryGetValue(entity, out float until) && now < until)
|
||
|
return false;
|
||
|
|
||
|
// If just came out of this portal pair, block one frame of re-trigger
|
||
|
if (_entityLastPortalId.TryGetValue(entity, out string lastId) && lastId == portalId)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public void MarkUsed(GameObject entity, string portalId, float cooldownSeconds)
|
||
|
{
|
||
|
if (cooldownSeconds > 0f)
|
||
|
_entityCooldownUntil[entity] = Time.time + cooldownSeconds;
|
||
|
|
||
|
_entityLastPortalId[entity] = portalId;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Teleport entity to destination. Handles CharacterController, Rigidbody, and NavMeshAgent safely.
|
||
|
/// </summary>
|
||
|
public void Teleport(GameObject entity, Transform destination, bool matchRotation = true)
|
||
|
{
|
||
|
if (entity == null || destination == null) return;
|
||
|
StartCoroutine(TeleportRoutine(entity, destination, matchRotation));
|
||
|
}
|
||
|
|
||
|
private IEnumerator TeleportRoutine(GameObject entity, Transform destination, bool matchRotation)
|
||
|
{
|
||
|
// Fade out (optional)
|
||
|
if (screenFader != null && fadeDuration > 0f)
|
||
|
yield return screenFader.FadeTo(1f, fadeDuration);
|
||
|
|
||
|
// Disable common movers safely
|
||
|
var cc = entity.GetComponent<CharacterController>();
|
||
|
var rb = entity.GetComponent<Rigidbody>();
|
||
|
var agent = entity.GetComponent<NavMeshAgent>();
|
||
|
|
||
|
bool ccWasEnabled = false;
|
||
|
bool agentWasEnabled = false;
|
||
|
|
||
|
if (cc != null) { ccWasEnabled = cc.enabled; cc.enabled = false; }
|
||
|
if (agent != null) { agentWasEnabled = agent.enabled; agent.enabled = false; }
|
||
|
if (rb != null)
|
||
|
{
|
||
|
rb.isKinematic = true; // pause physics for an instant
|
||
|
rb.velocity = Vector3.zero;
|
||
|
rb.angularVelocity = Vector3.zero;
|
||
|
}
|
||
|
|
||
|
// Move + rotate
|
||
|
if (agent != null)
|
||
|
{
|
||
|
agent.Warp(destination.position);
|
||
|
if (matchRotation) entity.transform.rotation = destination.rotation;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
entity.transform.position = destination.position;
|
||
|
if (matchRotation) entity.transform.rotation = destination.rotation;
|
||
|
}
|
||
|
|
||
|
// Small frame delay to settle
|
||
|
yield return null;
|
||
|
|
||
|
// Re-enable components
|
||
|
if (rb != null) rb.isKinematic = false;
|
||
|
if (cc != null) cc.enabled = ccWasEnabled;
|
||
|
if (agent != null) agent.enabled = agentWasEnabled;
|
||
|
|
||
|
// Fade in (optional)
|
||
|
if (screenFader != null && fadeDuration > 0f)
|
||
|
yield return screenFader.FadeTo(0f, fadeDuration);
|
||
|
}
|
||
|
}
|