forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			361 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using FishNet.Managing;
 | 
						|
using System.Collections.Generic;
 | 
						|
using UnityEngine;
 | 
						|
using UnityEngine.SceneManagement;
 | 
						|
 | 
						|
namespace FishNet.Component.Prediction
 | 
						|
{
 | 
						|
    /// <summary>
 | 
						|
    /// Pauses and unpauses rigidbodies. While paused rigidbodies cannot be interacted with or simulated.
 | 
						|
    /// </summary>
 | 
						|
    public class RigidbodyPauser
 | 
						|
    {
 | 
						|
        #region Types.
 | 
						|
        /// <summary>
 | 
						|
        /// Data for a rigidbody before being set kinematic.
 | 
						|
        /// </summary>
 | 
						|
        private struct RigidbodyData
 | 
						|
        {
 | 
						|
            /// <summary>
 | 
						|
            /// Rigidbody for data.
 | 
						|
            /// </summary>
 | 
						|
            public Rigidbody Rigidbody;
 | 
						|
            /// <summary>
 | 
						|
            /// Cached velocity when being set kinematic.
 | 
						|
            /// </summary>
 | 
						|
            public Vector3 Velocity;
 | 
						|
            /// <summary>
 | 
						|
            /// Cached velocity when being set kinematic.
 | 
						|
            /// </summary>
 | 
						|
            public Vector3 AngularVelocity;
 | 
						|
            /// <summary>
 | 
						|
            /// Scene of this rigidbody when being set kinematic.
 | 
						|
            /// </summary>
 | 
						|
            public Scene SimulatedScene;
 | 
						|
            /// <summary>
 | 
						|
            /// True if the rigidbody was kinematic prior to being paused.
 | 
						|
            /// </summary>
 | 
						|
            public bool IsKinematic;
 | 
						|
 | 
						|
            public RigidbodyData(Rigidbody rb)
 | 
						|
            {
 | 
						|
                Rigidbody = rb;
 | 
						|
                Rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
 | 
						|
                Velocity = Vector3.zero;
 | 
						|
                AngularVelocity = Vector3.zero;
 | 
						|
                SimulatedScene = rb.gameObject.scene;
 | 
						|
                IsKinematic = rb.isKinematic;
 | 
						|
            }
 | 
						|
 | 
						|
            public void Update(Rigidbody rb)
 | 
						|
            {
 | 
						|
                Velocity = rb.velocity;
 | 
						|
                AngularVelocity = rb.angularVelocity;
 | 
						|
                SimulatedScene = rb.gameObject.scene;
 | 
						|
                IsKinematic = rb.isKinematic;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// Data for a rigidbody2d before being set kinematic.
 | 
						|
        /// </summary>
 | 
						|
        private struct Rigidbody2DData
 | 
						|
        {
 | 
						|
            /// <summary>
 | 
						|
            /// Rigidbody for data.
 | 
						|
            /// </summary>
 | 
						|
            public Rigidbody2D Rigidbody2d;
 | 
						|
            /// <summary>
 | 
						|
            /// Cached velocity when being set kinematic.
 | 
						|
            /// </summary>
 | 
						|
            public Vector2 Velocity;
 | 
						|
            /// <summary>
 | 
						|
            /// Cached velocity when being set kinematic.
 | 
						|
            /// </summary>
 | 
						|
            public float AngularVelocity;
 | 
						|
            /// <summary>
 | 
						|
            /// Scene of this rigidbody when being set kinematic.
 | 
						|
            /// </summary>
 | 
						|
            public Scene SimulatedScene;
 | 
						|
            /// <summary>
 | 
						|
            /// True if the rigidbody was simulated prior to being paused.
 | 
						|
            /// </summary>
 | 
						|
            public bool Simulated;
 | 
						|
 | 
						|
            public Rigidbody2DData(Rigidbody2D rb)
 | 
						|
            {
 | 
						|
                Rigidbody2d = rb;
 | 
						|
                Rigidbody2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
 | 
						|
                Velocity = Vector2.zero;
 | 
						|
                AngularVelocity = 0f;
 | 
						|
                SimulatedScene = rb.gameObject.scene;
 | 
						|
                Simulated = rb.simulated;
 | 
						|
            }
 | 
						|
 | 
						|
            public void Update(Rigidbody2D rb)
 | 
						|
            {
 | 
						|
                Velocity = rb.velocity;
 | 
						|
                AngularVelocity = rb.angularVelocity;
 | 
						|
                SimulatedScene = rb.gameObject.scene;
 | 
						|
                Simulated = rb.simulated;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        #endregion
 | 
						|
 | 
						|
        #region Public.
 | 
						|
        /// <summary>
 | 
						|
        /// True if the rigidbodies are considered paused.
 | 
						|
        /// </summary>
 | 
						|
        public bool Paused { get; private set; }
 | 
						|
        #endregion
 | 
						|
 | 
						|
        #region Private.
 | 
						|
        /// <summary>
 | 
						|
        /// Rigidbody datas for found rigidbodies.
 | 
						|
        /// </summary>
 | 
						|
        private List<RigidbodyData> _rigidbodyDatas = new List<RigidbodyData>();
 | 
						|
        /// <summary>
 | 
						|
        /// Rigidbody2D datas for found rigidbodies;
 | 
						|
        /// </summary>
 | 
						|
        private List<Rigidbody2DData> _rigidbody2dDatas = new List<Rigidbody2DData>();
 | 
						|
        /// <summary>
 | 
						|
        /// Type of prediction movement which is being used.
 | 
						|
        /// </summary>
 | 
						|
        private RigidbodyType _rigidbodyType;
 | 
						|
        /// <summary>
 | 
						|
        /// 
 | 
						|
        /// </summary>
 | 
						|
        private static Scene _kinematicSceneCache;
 | 
						|
        /// <summary>
 | 
						|
        /// Scene used to simulate kinematic rigidbodies.
 | 
						|
        /// </summary>
 | 
						|
        private static Scene _kinematicScene
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                if (!_kinematicSceneCache.IsValid())
 | 
						|
                    _kinematicSceneCache = SceneManager.CreateScene("RigidbodyPauser_Kinematic", new CreateSceneParameters(LocalPhysicsMode.Physics2D | LocalPhysicsMode.Physics3D));
 | 
						|
                return _kinematicSceneCache;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// Parent of GraphicalObject prior to unparenting.
 | 
						|
        /// </summary>
 | 
						|
        private Transform _graphicalParent;
 | 
						|
        /// <summary>
 | 
						|
        /// GraphicalObject to unparent when pausing.
 | 
						|
        /// </summary>
 | 
						|
        private Transform _graphicalObject;
 | 
						|
        #endregion
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Assigns rigidbodies.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="rbs">Rigidbodies2D to use.</param>
 | 
						|
        public void UpdateRigidbodies(Transform t, RigidbodyType rbType, bool getInChildren, Transform graphicalObject)
 | 
						|
        {
 | 
						|
            _rigidbodyType = rbType;
 | 
						|
            _rigidbodyDatas.Clear();
 | 
						|
            _rigidbody2dDatas.Clear();
 | 
						|
 | 
						|
            //3D.
 | 
						|
            if (rbType == RigidbodyType.Rigidbody)
 | 
						|
            {   
 | 
						|
                if (getInChildren)
 | 
						|
                {
 | 
						|
                    Rigidbody[] rbs = t.GetComponentsInChildren<Rigidbody>();
 | 
						|
                    for (int i = 0; i < rbs.Length; i++)
 | 
						|
                        _rigidbodyDatas.Add(new RigidbodyData(rbs[i]));
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    Rigidbody rb = t.GetComponent<Rigidbody>();
 | 
						|
                    if (rb != null)
 | 
						|
                        _rigidbodyDatas.Add(new RigidbodyData(rb));
 | 
						|
                }
 | 
						|
 | 
						|
                //Make sure all added datas are not the graphical object.
 | 
						|
                for (int i = 0; i < _rigidbodyDatas.Count; i++)
 | 
						|
                {
 | 
						|
                    if (_rigidbodyDatas[i].Rigidbody.transform == graphicalObject)
 | 
						|
                    {
 | 
						|
                        NetworkManager.StaticLogError($"GameObject {t.name} has it's GraphicalObject as a child or on the same object as a Rigidbody object. The GraphicalObject must be a child of root, and not sit beneath or on any rigidbodies.");
 | 
						|
                        graphicalObject = null;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            //2D.
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (getInChildren)
 | 
						|
                {
 | 
						|
                    Rigidbody2D[] rbs = t.GetComponentsInChildren<Rigidbody2D>();
 | 
						|
                    for (int i = 0; i < rbs.Length; i++)
 | 
						|
                        _rigidbody2dDatas.Add(new Rigidbody2DData(rbs[i]));
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    Rigidbody2D rb = t.GetComponent<Rigidbody2D>();
 | 
						|
                    if (rb != null)
 | 
						|
                        _rigidbody2dDatas.Add(new Rigidbody2DData(rb));
 | 
						|
                }
 | 
						|
 | 
						|
                //Make sure all added datas are not the graphical object.
 | 
						|
                for (int i = 0; i < _rigidbody2dDatas.Count; i++)
 | 
						|
                {
 | 
						|
                    if (_rigidbody2dDatas[i].Rigidbody2d.transform == graphicalObject)
 | 
						|
                    {
 | 
						|
                        NetworkManager.StaticLogError($"GameObject {t.name} has it's GraphicalObject as a child or on the same object as a Rigidbody object. The GraphicalObject must be a child of root, and not sit beneath or on any rigidbodies.");
 | 
						|
                        graphicalObject = null;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (graphicalObject != null)
 | 
						|
            {
 | 
						|
                _graphicalObject = graphicalObject;
 | 
						|
                _graphicalParent = graphicalObject.parent;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Unpauses rigidbodies allowing them to interact normally.
 | 
						|
        /// </summary>
 | 
						|
        public void Unpause()
 | 
						|
        {
 | 
						|
            if (!Paused)
 | 
						|
                return;
 | 
						|
            Paused = false;
 | 
						|
 | 
						|
            //3D.
 | 
						|
            if (_rigidbodyType == RigidbodyType.Rigidbody)
 | 
						|
            {
 | 
						|
                for (int i = 0; i < _rigidbodyDatas.Count; i++)
 | 
						|
                {
 | 
						|
                    if (!UnpauseRigidbody(i))
 | 
						|
                    {
 | 
						|
                        _rigidbodyDatas.RemoveAt(i);
 | 
						|
                        i--;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                //Sets isKinematic status and returns if successful.
 | 
						|
                bool UnpauseRigidbody(int index)
 | 
						|
                {
 | 
						|
                    RigidbodyData rbData = _rigidbodyDatas[index];
 | 
						|
                    Rigidbody rb = rbData.Rigidbody;
 | 
						|
                    if (rb == null)
 | 
						|
                        return false;
 | 
						|
 | 
						|
                    rb.velocity = rbData.Velocity;
 | 
						|
                    rb.angularVelocity = rbData.AngularVelocity;
 | 
						|
                    rb.isKinematic = rbData.IsKinematic;
 | 
						|
                    SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, rbData.SimulatedScene);
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            //2D.
 | 
						|
            else
 | 
						|
            {
 | 
						|
                for (int i = 0; i < _rigidbody2dDatas.Count; i++)
 | 
						|
                {
 | 
						|
                    if (!UnpauseRigidbody(i))
 | 
						|
                    {
 | 
						|
                        _rigidbody2dDatas.RemoveAt(i);
 | 
						|
                        i--;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                //Sets isKinematic status and returns if successful.
 | 
						|
                bool UnpauseRigidbody(int index)
 | 
						|
                {
 | 
						|
                    Rigidbody2DData rbData = _rigidbody2dDatas[index];
 | 
						|
                    Rigidbody2D rb = rbData.Rigidbody2d;
 | 
						|
                    if (rb == null)
 | 
						|
                        return false;
 | 
						|
 | 
						|
                    rb.velocity = rbData.Velocity;
 | 
						|
                    rb.angularVelocity = rbData.AngularVelocity;
 | 
						|
                    rb.simulated = rbData.Simulated;
 | 
						|
                    rb.isKinematic = !rbData.Simulated;
 | 
						|
                    SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, rbData.SimulatedScene);
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            //Parent went null, then graphicalObject needs to be destroyed.
 | 
						|
            if (_graphicalParent == null && _graphicalObject != null)
 | 
						|
                MonoBehaviour.Destroy(_graphicalObject.gameObject);
 | 
						|
            else
 | 
						|
                _graphicalObject?.SetParent(_graphicalParent);
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Pauses rigidbodies preventing them from interacting.
 | 
						|
        /// </summary>
 | 
						|
        public void Pause()
 | 
						|
        {
 | 
						|
            if (Paused)
 | 
						|
                return;
 | 
						|
            Paused = true;
 | 
						|
 | 
						|
            _graphicalObject?.SetParent(null);
 | 
						|
            Scene kinematicScene = _kinematicScene;
 | 
						|
 | 
						|
            //3D.
 | 
						|
            if (_rigidbodyType == RigidbodyType.Rigidbody)
 | 
						|
            {
 | 
						|
                for (int i = 0; i < _rigidbodyDatas.Count; i++)
 | 
						|
                {
 | 
						|
                    if (!PauseRigidbody(i))
 | 
						|
                    {
 | 
						|
                        _rigidbodyDatas.RemoveAt(i);
 | 
						|
                        i--;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                //Sets isKinematic status and returns if successful.
 | 
						|
                bool PauseRigidbody(int index)
 | 
						|
                {
 | 
						|
                    RigidbodyData rbData = _rigidbodyDatas[index];
 | 
						|
                    Rigidbody rb = rbData.Rigidbody;
 | 
						|
                    if (rb == null)
 | 
						|
                        return false;
 | 
						|
 | 
						|
                    rbData.Update(rb);
 | 
						|
                    _rigidbodyDatas[index] = rbData;
 | 
						|
                    SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, kinematicScene);
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            //2D.
 | 
						|
            else
 | 
						|
            {
 | 
						|
                for (int i = 0; i < _rigidbody2dDatas.Count; i++)
 | 
						|
                {
 | 
						|
                    if (!PauseRigidbody(i))
 | 
						|
                    {
 | 
						|
                        _rigidbody2dDatas.RemoveAt(i);
 | 
						|
                        i--;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                //Sets isKinematic status and returns if successful.
 | 
						|
                bool PauseRigidbody(int index)
 | 
						|
                {
 | 
						|
                    Rigidbody2DData rbData = _rigidbody2dDatas[index];
 | 
						|
                    Rigidbody2D rb = rbData.Rigidbody2d;
 | 
						|
                    if (rb == null)
 | 
						|
                        return false;
 | 
						|
 | 
						|
                    rbData.Update(rb);
 | 
						|
                    _rigidbody2dDatas[index] = rbData;
 | 
						|
                    SceneManager.MoveGameObjectToScene(rb.transform.root.gameObject, kinematicScene);
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
} |