436 lines
18 KiB
C#
436 lines
18 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.Serialization;
|
|
|
|
namespace Fragilem17.MirrorsAndPortals
|
|
{
|
|
[RequireComponent(typeof(Collider))]
|
|
public class PortalableObject : MonoBehaviour
|
|
{
|
|
[Tooltip("Leave empty to target this gameObject, use this to transport a PlayerController as soon as the 'head' passes through.")]
|
|
[FormerlySerializedAs("TransformToTransport")]
|
|
public Transform TransformToPortal;
|
|
|
|
[Tooltip("When true, this object will be transported by a portal when passing through it. unless PortalWithMasterPortalable is turned on, then it will only portal when the master is portalled. ")]
|
|
[FormerlySerializedAs("AllowTransporting")]
|
|
public bool PortallingEnabled = true;
|
|
|
|
[Space(10)] // 10 pixels of spacing here.
|
|
|
|
[Header("Events")]
|
|
public UnityEvent<Portal> OnEnterPortalCollider;
|
|
|
|
[FormerlySerializedAs("OnPreWarpEvent")]
|
|
public UnityEvent<PortalableObject, Portal> OnPrePortalEvent;
|
|
|
|
[FormerlySerializedAs("OnPostWarpEvent")]
|
|
public UnityEvent<PortalableObject, Portal> OnPostPortalEvent;
|
|
|
|
public UnityEvent<PortalableObject, Portal> OnExitPortalCollider;
|
|
|
|
|
|
[Space(10)] // 10 pixels of spacing here.
|
|
|
|
[Tooltip("Turn on if this is your playerController.")]
|
|
public bool IsMasterPortalableObject;
|
|
|
|
[Tooltip("When true, this object will transport keeping it's relative distance from the MasterPortalableObject (your PlayerController)")]
|
|
[FormerlySerializedAs("PortalWithMasterPortalable")]
|
|
public bool PortalAlongWithMasterPortalable;
|
|
|
|
|
|
protected Portal _inPortal;
|
|
protected TransformTargetOfPortalableObject _myTransformTargetOfPortalableObject;
|
|
protected Rigidbody _rigidbody;
|
|
protected CharacterController _characterController;
|
|
protected Collider[] _colliders;
|
|
public List<Rigidbody> RigidbodiesToPortal;
|
|
|
|
[HideInInspector]
|
|
public CloneRenderer[] childCloneRenderers;
|
|
|
|
private static readonly Quaternion halfTurn = Quaternion.Euler(0.0f, 180.0f, 0.0f);
|
|
|
|
private bool childOfAnotherPortalable = false;
|
|
|
|
public static PortalableObject MasterPortalable;
|
|
|
|
public bool EnablePortalAlongWithMasterPortalable
|
|
{
|
|
get => PortalAlongWithMasterPortalable;
|
|
set
|
|
{
|
|
PortalAlongWithMasterPortalable = value;
|
|
/*if (PortalAlongWithMasterPortalable != value)
|
|
{
|
|
PortalAlongWithMasterPortalable = value;
|
|
if (!PortalAlongWithMasterPortalable && _inPortal != null)
|
|
{
|
|
// if we're no longer gonna portal along, then make sure we exit the current portal
|
|
ExitPortal(_inPortal);
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
public bool SetPortallingEnabled { get => PortallingEnabled; set => PortallingEnabled = value; }
|
|
|
|
[Tooltip("Assign a transform and it will always be positioned on the other side of the nearest portal relative to this object")]
|
|
public Transform ClonePositionToClosestPortal;
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
//Debug.Log("AWAKE!! " + name);
|
|
if (!TransformToPortal)
|
|
{
|
|
TransformToPortal = GetComponent<Transform>();
|
|
}
|
|
|
|
if (MasterPortalable != null && IsMasterPortalableObject && MasterPortalable != this)
|
|
{
|
|
Debug.LogError("There appears to be a second MasterPortalableObject in the scene, there should be only one! (" + name + ")");
|
|
}
|
|
|
|
if (IsMasterPortalableObject)
|
|
{
|
|
MasterPortalable = this;
|
|
}
|
|
|
|
|
|
_characterController = TransformToPortal.GetComponentInChildren<CharacterController>();
|
|
|
|
_rigidbody = TransformToPortal.GetComponent<Rigidbody>();
|
|
_colliders = TransformToPortal.GetComponentsInChildren<Collider>(true);
|
|
}
|
|
|
|
protected virtual void LateUpdate()
|
|
{
|
|
if (ClonePositionToClosestPortal)
|
|
{
|
|
Portal closest = PortalRenderer.FindClosestPortalInAllRenderers(transform.position);
|
|
|
|
Transform _inTransform = closest.PortalSurface.transform;
|
|
Transform _outTransform = closest.OtherPortal.PortalSurface.transform;
|
|
|
|
float scaleFactor = (closest.OtherPortal.transform.lossyScale.x / closest.transform.lossyScale.x);
|
|
ClonePositionToClosestPortal.transform.localScale = transform.lossyScale * scaleFactor;
|
|
|
|
// Update position of clone.
|
|
Vector3 relativePos = _inTransform.InverseTransformPoint(transform.position);
|
|
relativePos = halfTurn * relativePos;
|
|
ClonePositionToClosestPortal.transform.position = _outTransform.TransformPoint(relativePos);
|
|
|
|
// Update rotation of clone.
|
|
Quaternion relativeRot = Quaternion.Inverse(_inTransform.rotation) * transform.rotation;
|
|
relativeRot = halfTurn * relativeRot;
|
|
ClonePositionToClosestPortal.transform.rotation = _outTransform.rotation * relativeRot;
|
|
}
|
|
}
|
|
|
|
protected void OnEnable()
|
|
{
|
|
// todo, if we change the TransformToTransport pointer, we need to remove the target below
|
|
_myTransformTargetOfPortalableObject = TransformToPortal.GetComponent<TransformTargetOfPortalableObject>();
|
|
if (_myTransformTargetOfPortalableObject == null)
|
|
{
|
|
_myTransformTargetOfPortalableObject = TransformToPortal.gameObject.AddComponent<TransformTargetOfPortalableObject>();
|
|
}
|
|
_myTransformTargetOfPortalableObject.PortalableObject = this;
|
|
|
|
if (IsMasterPortalableObject)
|
|
{
|
|
MasterPortalable = this;
|
|
}
|
|
|
|
if (MasterPortalable != null && MasterPortalable != this)
|
|
{
|
|
MasterPortalable.OnEnterPortalCollider.RemoveListener(OnMasterPortalEnterPortalCollider);
|
|
MasterPortalable.OnExitPortalCollider.RemoveListener(OnMasterPortalExitPortalCollider);
|
|
MasterPortalable.OnPrePortalEvent.RemoveListener(OnMasterPortalablePreWarp);
|
|
MasterPortalable.OnPostPortalEvent.RemoveListener(OnMasterPortalableWarped);
|
|
|
|
//Debug.Log("ADDING LISTENER! " + name);
|
|
MasterPortalable.OnEnterPortalCollider.AddListener(OnMasterPortalEnterPortalCollider);
|
|
MasterPortalable.OnExitPortalCollider.AddListener(OnMasterPortalExitPortalCollider);
|
|
MasterPortalable.OnPrePortalEvent.AddListener(OnMasterPortalablePreWarp);
|
|
MasterPortalable.OnPostPortalEvent.AddListener(OnMasterPortalableWarped);
|
|
}
|
|
|
|
OnTransformParentChanged();
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
// Awake and OnEnable are called together, Start is called when all other components' Awake and OnEnable have been Called
|
|
// only here are we sure a MasterPortalable exists.
|
|
OnEnable();
|
|
}
|
|
|
|
protected void OnDisable()
|
|
{
|
|
if (MasterPortalable != null && MasterPortalable != this)
|
|
{
|
|
MasterPortalable.OnEnterPortalCollider.RemoveListener(OnMasterPortalEnterPortalCollider);
|
|
MasterPortalable.OnExitPortalCollider.RemoveListener(OnMasterPortalExitPortalCollider);
|
|
MasterPortalable.OnPrePortalEvent.RemoveListener(OnMasterPortalablePreWarp);
|
|
MasterPortalable.OnPostPortalEvent.RemoveListener(OnMasterPortalableWarped);
|
|
}
|
|
if (IsMasterPortalableObject)
|
|
{
|
|
MasterPortalable = null;
|
|
}
|
|
}
|
|
|
|
|
|
protected virtual void OnMasterPortalExitPortalCollider(PortalableObject masterObject, Portal portal)
|
|
{
|
|
}
|
|
|
|
protected virtual void OnMasterPortalEnterPortalCollider(Portal portal)
|
|
{
|
|
if (PortalAlongWithMasterPortalable)
|
|
{
|
|
SetIsInPortal(portal);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnMasterPortalablePreWarp(PortalableObject masterObject, Portal fromPortal)
|
|
{
|
|
if (PortalAlongWithMasterPortalable)
|
|
{
|
|
PreWarp(fromPortal);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnMasterPortalableWarped(PortalableObject masterObject, Portal fromPortal)
|
|
{
|
|
if (PortalAlongWithMasterPortalable)
|
|
{
|
|
//Debug.Log("OnMasterPortalableWarped! i will go along " + name);
|
|
PortalFrom(fromPortal, true);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnTransformParentChanged()
|
|
{
|
|
CheckForParentPortalables();
|
|
}
|
|
|
|
protected void CheckForParentPortalables()
|
|
{
|
|
// check for the existance of a parent PortalableObject
|
|
TransformTargetOfPortalableObject[] parentPortalables = GetComponentsInParent<TransformTargetOfPortalableObject>();
|
|
//Debug.Log("check OnTransformParentChanged: " + name + " :" + parentPortalables.Length);
|
|
childOfAnotherPortalable = (parentPortalables.Length > 1);
|
|
}
|
|
|
|
public virtual void SetIsInPortal(Portal portal)
|
|
{
|
|
ExitPortal(portal.OtherPortal);
|
|
|
|
_inPortal = portal;
|
|
OnEnterPortalCollider?.Invoke(portal);
|
|
if (PortallingEnabled) // !childOfAnotherPortalable &&
|
|
{
|
|
//Debug.Log("SetIsInPortal " + this.name + " inPortal: " + portal.name + " turning OFF colliders ");
|
|
if (portal.wallCollider)
|
|
{
|
|
// if that's the case.. then we can turn of the colliders
|
|
// disable collisions with other portal
|
|
_colliders = TransformToPortal.GetComponentsInChildren<Collider>(true);
|
|
for (int i = 0; i < _colliders.Length; i++)
|
|
{
|
|
Physics.IgnoreCollision(_colliders[i], portal.wallCollider);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void ExitPortal(Portal portal)
|
|
{
|
|
if (_inPortal == portal)
|
|
{
|
|
_inPortal = null;
|
|
}
|
|
|
|
OnExitPortalCollider?.Invoke(this, portal);
|
|
//Debug.Log("ExitPortal " + this.name + " inPortal: " + portal.name + " turning ON colliders ");
|
|
|
|
|
|
if (portal.wallCollider)
|
|
{
|
|
for (int i = 0; i < _colliders.Length; i++)
|
|
{
|
|
Physics.IgnoreCollision(_colliders[i], portal.wallCollider, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
protected virtual void PreWarp(Portal fromPortal)
|
|
{
|
|
//Debug.Log(" PRE: " + name);
|
|
OnPrePortalEvent.Invoke(this, fromPortal);
|
|
if (_characterController)
|
|
{
|
|
_characterController.enabled = false;
|
|
}
|
|
|
|
}
|
|
protected virtual void PostWarp(Portal fromPortal)
|
|
{
|
|
//Debug.Log("POST: " + name);
|
|
OnPostPortalEvent.Invoke(this, fromPortal);
|
|
//Debug.Log("PortalableObject postWarp, gonna triggered teleport function in HVR");
|
|
if (_characterController)
|
|
{
|
|
_characterController.enabled = true;
|
|
}
|
|
|
|
|
|
// instant move clones to other portal
|
|
childCloneRenderers = FindChildCloneRenderers();
|
|
for (int i = 0; i < childCloneRenderers.Length; i++)
|
|
{
|
|
fromPortal.PortalTransporter.cloneObjects.Remove(childCloneRenderers[i]);
|
|
childCloneRenderers[i].ExitPortal(fromPortal, false);
|
|
|
|
Collider[] myColliders = childCloneRenderers[i].GetComponents<Collider>();
|
|
foreach (Collider c in myColliders) {
|
|
|
|
//Debug.DrawLine(childCloneRenderers[i].transform.position, fromPortal.OtherPortal.PortalTransporter.transform.position, Color.cyan, 10);
|
|
|
|
Vector3 direction;
|
|
float distance;
|
|
bool overlapped = Physics.ComputePenetration(
|
|
|
|
c, childCloneRenderers[i].transform.position, childCloneRenderers[i].transform.rotation,
|
|
fromPortal.OtherPortal.PortalTransporter.MyCollider, fromPortal.OtherPortal.PortalTransporter.transform.position, fromPortal.OtherPortal.PortalTransporter.transform.rotation,
|
|
out direction, out distance);
|
|
|
|
//Debug.Log(childCloneRenderers[i].name + " : " + direction + " : " + distance + " : " + overlapped);
|
|
|
|
if (overlapped)
|
|
{
|
|
//Debug.Log("AFTER WARP " + childCloneRenderers[i].name + " WOULD BE TOUCHING SO SET ME IN PORTAL: " + fromPortal.OtherPortal.name);
|
|
childCloneRenderers[i].SetIsInPortal(fromPortal.OtherPortal, false);
|
|
fromPortal.OtherPortal.PortalTransporter.cloneObjects.Add(childCloneRenderers[i]);
|
|
break;
|
|
}
|
|
/*else
|
|
{
|
|
Debug.Log("AFTER WARP not touching new portal: " + fromPortal.OtherPortal.name);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
protected virtual CloneRenderer[] FindChildCloneRenderers()
|
|
{
|
|
return TransformToPortal.GetComponentsInChildren<CloneRenderer>(true);
|
|
}
|
|
|
|
public virtual bool CanPortal() {
|
|
|
|
//Debug.Log("AllowTransporting canWarp?: " + name + " :" + AllowTransporting);
|
|
return (!childOfAnotherPortalable && PortallingEnabled && !PortalAlongWithMasterPortalable);
|
|
}
|
|
|
|
public virtual bool PortalFrom(Portal fromPortal, bool force = false)
|
|
{
|
|
if (!force && !CanPortal())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Debug.Log("WARP " + name + " from " + fromPortal.name + " childOfAnotherPortalable: " + childOfAnotherPortalable + " force: " + force);
|
|
|
|
// if we're forced, that means the Master got us here and a preWarp has already been done
|
|
if (!force) {
|
|
PreWarp(fromPortal);
|
|
}
|
|
|
|
if (force)
|
|
{
|
|
// master got us here.. we might have entered through physics.. but on the wrong side of the portal therefor not adding to the list in transporter
|
|
if (!fromPortal.OtherPortal.PortalTransporter.portalableObjects.Contains(this))
|
|
{
|
|
//Debug.Log("(from master) Adding to portal list of " + fromPortal.OtherPortal.name + " object:" + this.name);
|
|
fromPortal.OtherPortal.PortalTransporter.portalableObjects.Add(this);
|
|
}
|
|
}
|
|
|
|
// disable collisions with other portal
|
|
if (fromPortal.OtherPortal.wallCollider)
|
|
{
|
|
for (int i = 0; i < _colliders.Length; i++)
|
|
{
|
|
Physics.IgnoreCollision(_colliders[i], fromPortal.OtherPortal.wallCollider);
|
|
}
|
|
}
|
|
|
|
var inTransform = fromPortal.PortalSurface.transform;
|
|
var outTransform = fromPortal.OtherPortal.PortalSurface.transform;
|
|
|
|
// van scale p1.0 to p0.5 = player 0.5 = portal out / portal in
|
|
// van scale p0.5 to p1.0 = player 2 = portal out / portal in
|
|
float scaleFactor = (fromPortal.OtherPortal.transform.lossyScale.x / fromPortal.transform.lossyScale.x);
|
|
|
|
|
|
Vector3 originalScaleBeforeWarp = TransformToPortal.localScale;
|
|
TransformToPortal.localScale = TransformToPortal.localScale * scaleFactor;
|
|
|
|
|
|
// Position the camera behind the other portal.
|
|
Vector3 relativePos = inTransform.InverseTransformPoint(TransformToPortal.position);
|
|
relativePos = halfTurn * relativePos;
|
|
TransformToPortal.position = outTransform.TransformPoint(relativePos);
|
|
|
|
// Rotate the camera to look through the other portal.
|
|
Quaternion relativeRot = Quaternion.Inverse(inTransform.rotation) * TransformToPortal.rotation;
|
|
relativeRot = halfTurn * relativeRot;
|
|
TransformToPortal.rotation = outTransform.rotation * relativeRot;
|
|
|
|
// Update velocity of rigidbody.
|
|
if (_rigidbody)
|
|
{
|
|
Vector3 relativeVel = inTransform.InverseTransformDirection(_rigidbody.linearVelocity);
|
|
relativeVel = halfTurn * relativeVel;
|
|
_rigidbody.linearVelocity = outTransform.TransformDirection(relativeVel);
|
|
|
|
Vector3 relativeAngVel = inTransform.InverseTransformDirection(_rigidbody.angularVelocity);
|
|
relativeAngVel = halfTurn * relativeAngVel;
|
|
_rigidbody.angularVelocity = outTransform.TransformDirection(relativeAngVel);
|
|
|
|
_rigidbody.position = TransformToPortal.position; // so it works for RB with interpolation
|
|
_rigidbody.rotation = TransformToPortal.rotation; // so it works for RB with interpolation
|
|
}
|
|
|
|
if (RigidbodiesToPortal.Count > 0)
|
|
{
|
|
foreach (Rigidbody rb in RigidbodiesToPortal)
|
|
{
|
|
if (rb && rb != _rigidbody)
|
|
{
|
|
Vector3 relativeVel = inTransform.InverseTransformDirection(rb.linearVelocity);
|
|
relativeVel = halfTurn * relativeVel;
|
|
rb.linearVelocity = outTransform.TransformDirection(relativeVel);
|
|
|
|
Vector3 relativeAngVel = inTransform.InverseTransformDirection(rb.angularVelocity);
|
|
relativeAngVel = halfTurn * relativeAngVel;
|
|
rb.angularVelocity = outTransform.TransformDirection(relativeAngVel);
|
|
}
|
|
}
|
|
}
|
|
|
|
PostWarp(fromPortal);
|
|
|
|
SendMessage("OnWarped", transform.rotation, SendMessageOptions.DontRequireReceiver);
|
|
return true;
|
|
}
|
|
}
|
|
} |