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;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
| } |