475 lines
20 KiB
C#
475 lines
20 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace RayFire
|
|
{
|
|
[AddComponentMenu ("RayFire/Rayfire Unyielding")]
|
|
[HelpURL ("https://rayfirestudios.com/unity-online-help/components/unity-unyielding-component/")]
|
|
public class RayfireUnyielding : MonoBehaviour
|
|
{
|
|
public enum UnySimType
|
|
{
|
|
Original = 1,
|
|
Inactive = 2,
|
|
Kinematic = 3
|
|
}
|
|
|
|
// UI
|
|
public bool unyielding = true;
|
|
public bool activatable = false;
|
|
public UnySimType simulationType = UnySimType.Original;
|
|
public Vector3 centerPosition;
|
|
public Vector3 size = new Vector3 (1f, 1f, 1f);
|
|
|
|
// Hidden
|
|
public RayfireRigid rigidHost;
|
|
public List<RayfireRigid> rigidList;
|
|
public List<RFShard> shardList;
|
|
public bool showGizmo = true;
|
|
public bool showCenter;
|
|
public float alSz = 1f;
|
|
|
|
/// /////////////////////////////////////////////////////////
|
|
/// Connected Cluster setup
|
|
/// /////////////////////////////////////////////////////////
|
|
|
|
// Set clusterized rigids uny state and mesh root rigids
|
|
public static void ClusterSetup (RayfireRigid rigid)
|
|
{
|
|
if (rigid.simTp == SimType.Inactive || rigid.simTp == SimType.Kinematic)
|
|
{
|
|
RayfireUnyielding[] unyArray = rigid.GetComponents<RayfireUnyielding>();
|
|
for (int i = 0; i < unyArray.Length; i++)
|
|
if (unyArray[i].enabled == true)
|
|
{
|
|
unyArray[i].rigidHost = rigid;
|
|
ClusterOverlap (unyArray[i], rigid);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set uny state for mesh root rigids. Used by Mesh Root. Can be used for cluster shards
|
|
public static void ClusterOverlap (RayfireUnyielding uny, RayfireRigid rigid)
|
|
{
|
|
// Get target mask and overlap colliders
|
|
int finalMask = ClusterLayerMask(rigid);
|
|
Collider[] colliders = Physics.OverlapBox (uny.transform.TransformPoint (uny.centerPosition), uny.Extents, uny.transform.rotation, finalMask);
|
|
HashSet<Collider> collidersHash = new HashSet<Collider> (colliders);
|
|
|
|
// Check with connected cluster
|
|
uny.shardList = new List<RFShard>();
|
|
if (rigid.objTp == ObjectType.ConnectedCluster)
|
|
{
|
|
// Get simulation type for overlapped shards
|
|
SimType shardSimType = rigid.simTp;
|
|
if (uny.simulationType != UnySimType.Original)
|
|
shardSimType = (SimType)uny.simulationType;
|
|
|
|
// Get all overlapped shards and set uny, act and sim states
|
|
for (int i = 0; i < rigid.physics.cc.Count; i++)
|
|
{
|
|
if (rigid.physics.cc[i] != null)
|
|
if (collidersHash.Contains (rigid.physics.cc[i]) == true)
|
|
{
|
|
SetShardUnyState (rigid.clsDemol.cluster.shards[i], uny.unyielding, uny.activatable);
|
|
rigid.clsDemol.cluster.shards[i].sm = shardSimType;
|
|
uny.shardList.Add (rigid.clsDemol.cluster.shards[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get combined layer mask
|
|
static int ClusterLayerMask(RayfireRigid rigid)
|
|
{
|
|
int mask = 0;
|
|
if (rigid.objTp == ObjectType.ConnectedCluster)
|
|
for (int i = 0; i < rigid.physics.cc.Count; i++)
|
|
if (rigid.physics.cc[i] != null)
|
|
mask = mask | 1 << rigid.clsDemol.cluster.shards[i].tm.gameObject.layer;
|
|
return mask;
|
|
}
|
|
|
|
// Set unyielding state
|
|
static void SetShardUnyState (RFShard shard, bool unyielding, bool activatable)
|
|
{
|
|
shard.uny = unyielding;
|
|
shard.act = activatable;
|
|
}
|
|
|
|
/// /////////////////////////////////////////////////////////
|
|
/// Mesh Root setup
|
|
/// /////////////////////////////////////////////////////////
|
|
|
|
// Set clusterized rigids uny state and mesh root rigids
|
|
public static void MeshRootSetup (RayfireRigid mRoot)
|
|
{
|
|
// Get uny list
|
|
List<RayfireUnyielding> unyList = GetUnyList (mRoot.transform);
|
|
|
|
// Iterate every unyielding component
|
|
for (int i = 0; i < unyList.Count; i++)
|
|
SetMeshRootUnyRigidList (mRoot, unyList[i]);
|
|
|
|
// Set rigid list uny and sim states
|
|
SetMeshRootUnyState (mRoot.transform, unyList);
|
|
}
|
|
|
|
// Get uny list
|
|
static List<RayfireUnyielding> GetUnyList (Transform tm)
|
|
{
|
|
List<RayfireUnyielding> unyList = tm.GetComponents<RayfireUnyielding>().ToList();
|
|
for (int i = unyList.Count - 1; i >= 0; i--)
|
|
if (unyList[i].enabled == false)
|
|
unyList.RemoveAt (i);
|
|
return unyList;
|
|
}
|
|
|
|
// Set uny state for mesh root rigids. Used by Mesh Root. Can be used for cluster shards
|
|
static void SetMeshRootUnyRigidList (RayfireRigid mRoot, RayfireUnyielding uny)
|
|
{
|
|
// Get target mask
|
|
int finalMask = MeshRootLayerMask(mRoot);
|
|
Collider[] colliders = Physics.OverlapBox (uny.transform.TransformPoint (uny.centerPosition), uny.Extents, uny.transform.rotation, finalMask);
|
|
HashSet<Collider> collidersHash = new HashSet<Collider> (colliders);
|
|
|
|
// Check with connectivity rigids
|
|
uny.rigidList = new List<RayfireRigid>();
|
|
for (int i = 0; i < mRoot.fragments.Count; i++)
|
|
if (mRoot.fragments[i].physics.mc != null)
|
|
if (collidersHash.Contains (mRoot.fragments[i].physics.mc) == true)
|
|
uny.rigidList.Add (mRoot.fragments[i]);
|
|
}
|
|
|
|
// Get combined layer mask
|
|
static int MeshRootLayerMask(RayfireRigid mRoot)
|
|
{
|
|
int mask = 0;
|
|
for (int i = 0; i < mRoot.fragments.Count; i++)
|
|
if (mRoot.fragments[i].physics.mc != null)
|
|
mask = mask | 1 << mRoot.fragments[i].gameObject.layer;
|
|
return mask;
|
|
}
|
|
|
|
// Set rigid list uny and sim states
|
|
public static void SetMeshRootUnyState (Transform tm, List<RayfireUnyielding> unyList)
|
|
{
|
|
// Get uny list
|
|
if (unyList == null)
|
|
unyList = GetUnyList (tm);
|
|
|
|
// Iterate uny components list
|
|
for (int c = 0; c < unyList.Count; c++)
|
|
{
|
|
// No rigids
|
|
if (unyList[c].rigidList.Count == 0)
|
|
continue;
|
|
|
|
// Set uny and act states for Rigids
|
|
SetRigidUnyState (unyList[c]);
|
|
|
|
// Set simulation type by
|
|
SetRigidUnySim (unyList[c]);
|
|
}
|
|
}
|
|
|
|
// Set unyielding state
|
|
static void SetRigidUnyState (RayfireUnyielding uny)
|
|
{
|
|
// Common ops> Editor and Runtime
|
|
for (int i = 0; i < uny.rigidList.Count; i++)
|
|
{
|
|
uny.rigidList[i].activation.uny = uny.unyielding;
|
|
uny.rigidList[i].activation.atb = uny.activatable;
|
|
}
|
|
|
|
// Runtime ops.
|
|
if (Application.isPlaying == true)
|
|
for (int i = 0; i < uny.rigidList.Count; i++)
|
|
{
|
|
// Stop velocity and offset activation coroutines for not activatable uny objects
|
|
if (uny.unyielding == true && uny.activatable == false)
|
|
{
|
|
if (uny.rigidList[i].activation.velocityCorState == true)
|
|
RayfireMan.inst.RemoveVelocityActivationCor (uny.rigidList[i]);
|
|
if (uny.rigidList[i].activation.offsetCorState == true)
|
|
RayfireMan.inst.RemoveOffsetActivationCor (uny.rigidList[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set unyielding rigids sim type by
|
|
static void SetRigidUnySim (RayfireUnyielding uny)
|
|
{
|
|
if (Application.isPlaying == true && uny.simulationType != UnySimType.Original)
|
|
for (int i = 0; i < uny.rigidList.Count; i++)
|
|
{
|
|
uny.rigidList[i].simTp = (SimType)uny.simulationType;
|
|
RFPhysic.SetSimulationType (uny.rigidList[i].physics.rb, uny.rigidList[i].simTp,
|
|
ObjectType.Mesh, uny.rigidList[i].physics.gr, uny.rigidList[i].physics.si, uny.rigidList[i].physics.st);
|
|
}
|
|
}
|
|
|
|
// Set clusterized rigids uny state and mesh root rigids
|
|
public static void ResetMeshRootSetup (RayfireRigid mRoot)
|
|
{
|
|
RayfireUnyielding[] unyList = mRoot.GetComponents<RayfireUnyielding>();
|
|
for (int i = unyList.Length - 1; i >= 0; i--)
|
|
unyList[i].rigidList = null;
|
|
}
|
|
|
|
/// /////////////////////////////////////////////////////////
|
|
/// Runtime Fragments
|
|
/// /////////////////////////////////////////////////////////
|
|
|
|
// Check for overlap with mesh Rigid. Copies Uny component in case of slice, so new slices will have uny gizmo.
|
|
public static void SetUnyieldingFragments (RayfireRigid scr, bool slice)
|
|
{
|
|
// Not mesh demolition
|
|
if (scr.objTp != ObjectType.Mesh)
|
|
return;
|
|
|
|
// Skip if convert to Connected Cluster and fragments wont need uny, rigid, rb components
|
|
if (scr.mshDemol.cnv == RFDemolitionMesh.ConvertType.ConnectedCluster)
|
|
return;
|
|
|
|
// No fragments
|
|
if (scr.HasFragments == false)
|
|
return;
|
|
|
|
// TODO collect layer mask by all layers -> int finalMask = RayfireUnyielding.ClusterLayerMask(scr);
|
|
int layerMask = 1 << scr.gameObject.layer;
|
|
|
|
// Overlapped objects: Copy uny, stay kinematic
|
|
RayfireUnyielding[] unyArr = scr.GetComponents<RayfireUnyielding>();
|
|
|
|
// Get all overlapped fragments
|
|
Collider[] colliders;
|
|
HashSet<Collider> collidersHash;
|
|
for (int u = 0; u < unyArr.Length; u++)
|
|
{
|
|
// Get box overlap colliders
|
|
// Physics.OverlapBoxNonAlloc (unyArr[u].transform.TransformPoint (unyArr[u].centerPosition), unyArr[u].Extents, colliders, unyArr[u].transform.rotation, layerMask);
|
|
colliders = Physics.OverlapBox (unyArr[u].transform.TransformPoint (unyArr[u].centerPosition), unyArr[u].Extents, unyArr[u].transform.rotation, layerMask);
|
|
collidersHash = new HashSet<Collider> (colliders);
|
|
|
|
// Activate if do not overlap
|
|
for (int i = 0; i < scr.fragments.Count; i++)
|
|
{
|
|
// Activate not overlapped and copy to overlapped
|
|
if (collidersHash.Contains (scr.fragments[i].physics.mc) == true)
|
|
{
|
|
// Copy overlap uny to overlapped object
|
|
SetRigidUnyState (scr.fragments[i], unyArr[u].unyielding, unyArr[u].activatable);
|
|
|
|
// Set simulation type
|
|
if (unyArr[u].simulationType != UnySimType.Original)
|
|
scr.fragments[i].simTp = (SimType)unyArr[u].simulationType;
|
|
|
|
// Set simulation type
|
|
RFPhysic.SetSimulationType (scr.fragments[i].physics.rb, scr.fragments[i].simTp,
|
|
scr.fragments[i].objTp, scr.fragments[i].physics.gr, scr.fragments[i].physics.si, scr.fragments[i].physics.st);
|
|
|
|
// Copy rigid to overlapped fragments for further slices.
|
|
if (slice == true)
|
|
CopyUny (unyArr[u], scr.fragments[i].gameObject);
|
|
|
|
// Start inactive coroutines
|
|
scr.fragments[i].InactiveCors();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set unyielding state
|
|
static void SetRigidUnyState (RayfireRigid rigid, bool uny, bool act)
|
|
{
|
|
rigid.activation.uny = uny;
|
|
rigid.activation.atb = act;
|
|
|
|
// Stop velocity and offset activation coroutines for not activatable uny objects
|
|
if (uny == true && act == false)
|
|
{
|
|
RayfireMan.inst.RemoveVelocityActivationCor (rigid);
|
|
RayfireMan.inst.RemoveOffsetActivationCor (rigid);
|
|
}
|
|
}
|
|
|
|
// Copy unyielding component
|
|
static void CopyUny (RayfireUnyielding source, GameObject target)
|
|
{
|
|
RayfireUnyielding newUny = target.AddComponent<RayfireUnyielding>();
|
|
|
|
// Copy position
|
|
Vector3 globalCenter = source.transform.TransformPoint (source.centerPosition);
|
|
newUny.centerPosition = newUny.transform.InverseTransformPoint (globalCenter);
|
|
|
|
// Copy size
|
|
Vector3 localScale = source.transform.localScale;
|
|
newUny.size = source.size;
|
|
newUny.size.x *= localScale.x;
|
|
newUny.size.y *= localScale.y;
|
|
newUny.size.z *= localScale.z;
|
|
|
|
// Copy properties
|
|
newUny.simulationType = source.simulationType;
|
|
newUny.unyielding = source.unyielding;
|
|
newUny.activatable = source.activatable;
|
|
}
|
|
|
|
/// /////////////////////////////////////////////////////////
|
|
/// Rigid Root Setup
|
|
/// /////////////////////////////////////////////////////////
|
|
|
|
// Set uny state for mesh root rigids. Used by Mesh Root. Can be used for cluster shards
|
|
void GetRigidRootUnyShardList(RayfireRigidRoot rigidRoot)
|
|
{
|
|
// Uny disabled
|
|
if (enabled == false)
|
|
return;
|
|
|
|
// Get target mask TODO check fragments layer
|
|
int mask = 0;
|
|
|
|
// Check with rigid root shards colliders
|
|
for (int i = 0; i < rigidRoot.cluster.shards.Count; i++)
|
|
if (rigidRoot.cluster.shards[i].col != null)
|
|
mask = mask | 1 << rigidRoot.cluster.shards[i].tm.gameObject.layer;
|
|
|
|
// Get box overlap colliders
|
|
Collider[] colliders = Physics.OverlapBox (transform.TransformPoint (centerPosition), Extents, transform.rotation, mask);
|
|
HashSet<Collider> collidersHash = new HashSet<Collider> (colliders);
|
|
|
|
// Check with rigid root shards colliders
|
|
shardList = new List<RFShard>();
|
|
for (int i = 0; i < rigidRoot.cluster.shards.Count; i++)
|
|
if (rigidRoot.cluster.shards[i].col != null)
|
|
if (collidersHash.Contains (rigidRoot.cluster.shards[i].col) == true)
|
|
shardList.Add (rigidRoot.cluster.shards[i]);
|
|
}
|
|
|
|
// Set sim amd uny states for cached shards
|
|
public void SetRigidRootUnyShardList()
|
|
{
|
|
// No shards
|
|
if (shardList.Count == 0)
|
|
return;
|
|
|
|
// Iterate cached shards
|
|
for (int i = 0; i < shardList.Count; i++)
|
|
{
|
|
// Set uny states
|
|
shardList[i].uny = unyielding;
|
|
shardList[i].act = activatable;
|
|
|
|
// Set sim states
|
|
if (simulationType != UnySimType.Original)
|
|
shardList[i].sm = (SimType)simulationType;
|
|
}
|
|
|
|
// TODO Stop velocity and offset activation coroutines for not activatable uny objects (copy from above)
|
|
}
|
|
|
|
// Set unyielding shards. Should be After collider set and Before SetPhysics because Changes simType.
|
|
public static void SetUnyielding(RayfireRigidRoot rr)
|
|
{
|
|
// Set by rigid root
|
|
for (int i = 0; i < rr.rigidRootShards.Count; i++)
|
|
{
|
|
rr.rigidRootShards[i].uny = rr.activation.uny;
|
|
rr.rigidRootShards[i].act = rr.activation.atb;
|
|
}
|
|
|
|
// Set by mesh root
|
|
for (int i = 0; i < rr.meshRootShards.Count; i++)
|
|
{
|
|
rr.meshRootShards[i].uny = rr.meshRootShards[i].rigid.activation.uny;
|
|
rr.meshRootShards[i].act = rr.meshRootShards[i].rigid.activation.atb;
|
|
}
|
|
|
|
// Set by connected cluster
|
|
for (int i = 0; i < rr.connClusterShards.Count; i++)
|
|
{
|
|
rr.connClusterShards[i].uny = rr.connClusterShards[i].rigid.activation.uny;
|
|
rr.connClusterShards[i].act = rr.connClusterShards[i].rigid.activation.atb;
|
|
}
|
|
|
|
// Set by uny components
|
|
if (rr.HasUny == true)
|
|
{
|
|
for (int i = 0; i < rr.unyList.Length; i++)
|
|
{
|
|
rr.unyList[i].GetRigidRootUnyShardList (rr);
|
|
rr.unyList[i].SetRigidRootUnyShardList();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// /////////////////////////////////////////////////////////
|
|
/// Activate
|
|
/// /////////////////////////////////////////////////////////
|
|
|
|
// Activate inactive\kinematic shards/fragments
|
|
public void Activate()
|
|
{
|
|
// Activate all rigids, init connectivity check after last activation, nullify connectivity for every
|
|
if (HasRigids == true)
|
|
{
|
|
for (int i = 0; i < rigidList.Count; i++)
|
|
{
|
|
// Activate if activatable
|
|
if (rigidList[i].activation.atb == true)
|
|
{
|
|
rigidList[i].Activate (i == rigidList.Count - 1);
|
|
rigidList[i].activation.cnt = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Activate connected clusters shards
|
|
if (HasShards == true)
|
|
{
|
|
// Collect shards colliders
|
|
Collider[] colliders = new Collider[shardList.Count];
|
|
for (int i = 0; i < shardList.Count; i++)
|
|
if (shardList[i].col != null)
|
|
colliders[i] = shardList[i].col;
|
|
|
|
// No colliders
|
|
if (colliders.Length == 0)
|
|
return;
|
|
|
|
// Get Unyielding shards FIXME doesn't demolish cluster root if all shards activated
|
|
List<RFShard> shards = RFDemolitionCluster.DemolishConnectedCluster (rigidHost, colliders);
|
|
|
|
// Activate
|
|
if (shards != null && shards.Count > 0)
|
|
for (int i = 0; i < shards.Count; i++)
|
|
RFActivation.ActivateShard (shards[i], null);
|
|
}
|
|
}
|
|
|
|
/// /////////////////////////////////////////////////////////
|
|
/// Getters
|
|
/// /////////////////////////////////////////////////////////
|
|
|
|
// Had child cluster
|
|
bool HasRigids { get { return rigidList != null && rigidList.Count > 0; } }
|
|
bool HasShards { get { return shardList != null && shardList.Count > 0; } }
|
|
|
|
// Get final extents
|
|
Vector3 Extents
|
|
{
|
|
get
|
|
{
|
|
Vector3 ext = size / 2f;
|
|
Vector3 lossyScale = transform.lossyScale;
|
|
ext.x *= lossyScale.x;
|
|
ext.y *= lossyScale.y;
|
|
ext.z *= lossyScale.z;
|
|
return ext;
|
|
}
|
|
}
|
|
}
|
|
} |