OldBlueWater/BlueWater/Assets/RayFire/Scripts/Components/RayfireActivator.cs
2023-08-22 14:31:24 +09:00

700 lines
24 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RayFire
{
// TODO TAG LAYER FILTERS
// TODO OPTIMIZE OVERLAP || BBOX overlap
[AddComponentMenu ("RayFire/Rayfire Activator")]
[HelpURL ("https://rayfirestudios.com/unity-online-help/components/unity-activator-component/")]
public class RayfireActivator : MonoBehaviour
{
public enum ActivationType
{
OnTriggerEnter = 0,
OnTriggerExit = 1,
OnCollision = 2
}
public enum AnimationType
{
ByGlobalPositionList = 0,
ByStaticLine = 1,
ByDynamicLine = 2,
ByLocalPositionList = 5
}
public enum GizmoType
{
Box = 1,
Sphere = 0,
Collider = 2,
ParticleSystem = 5
}
// UI
public GizmoType gizmoType;
public float sphereRadius = 5f;
public Vector3 boxSize = new Vector3 (5f, 2f, 5f);
public bool checkRigid = true;
public bool checkRigidRoot = true;
public ActivationType type;
public float delay;
public bool demolishCluster;
public bool apply;
public Vector3 velocity;
public Vector3 spin;
public ForceMode mode;
public bool coord;
public bool showAnimation;
public float duration = 3f;
public float scaleAnimation = 1f;
public AnimationType positionAnimation;
public LineRenderer line;
public List<Vector3> positionList;
public bool showGizmo = true;
public Collider activatorCollider;
public ParticleSystem ps;
public List<ParticleCollisionEvent> collisionEvents;
// Non serialized
[NonSerialized] bool animating;
[NonSerialized] float pathRatio;
[NonSerialized] float lineLength;
[NonSerialized] float[] checkpoints;
[NonSerialized] float delta;
[NonSerialized] float deltaRatioStep;
[NonSerialized] float distDeltaStep;
[NonSerialized] float distRatio;
[NonSerialized] float timePassed;
[NonSerialized] int activeSegment;
[NonSerialized] Vector3 positionStart;
[NonSerialized] Vector3 scaleStart;
// TODO list of objects to check or all colliders objects
/// /////////////////////////////////////////////////////////
/// Common
/// /////////////////////////////////////////////////////////
// Start is called before the first frame update
void Awake()
{
// Check collider and triggers
if (gizmoType != GizmoType.ParticleSystem)
SetCollider();
else
SetParticleSystem();
// TODO no target check
positionStart = transform.position;
scaleStart = transform.localScale;
}
/// /////////////////////////////////////////////////////////
/// Collision
/// /////////////////////////////////////////////////////////
// Collider collision activation
void OnCollisionEnter (Collision collision)
{
if (type == ActivationType.OnCollision && gizmoType != GizmoType.ParticleSystem)
ActivationCheck (collision.collider);
}
// Particle collision activation
void OnParticleCollision (GameObject other)
{
if (type == ActivationType.OnCollision && gizmoType == GizmoType.ParticleSystem)
{
int numCollisionEvents = ps.GetCollisionEvents (other, collisionEvents);
for (int i = 0; i < numCollisionEvents; i++)
ActivationCheck (collisionEvents[i].colliderComponent as Collider);
}
}
/// /////////////////////////////////////////////////////////
/// Trigger
/// /////////////////////////////////////////////////////////
// Activate on collider enter
void OnTriggerEnter (Collider coll)
{
// TODO to avoid multiple checks at one frame
// After first frame EnEnter set check state and avoid other OnEnter check at the same frame.
// Use OverlapColliders to ge all colliders at once.
// Add Target list to compare colliders with target colliders.
// use for massive sims.
if (type == ActivationType.OnTriggerEnter)
ActivationCheck (coll);
}
// Activate on collider exit
void OnTriggerExit (Collider coll)
{
if (type == ActivationType.OnTriggerExit)
ActivationCheck (coll);
}
/// /////////////////////////////////////////////////////////
/// Set
/// /////////////////////////////////////////////////////////
// Prepare collider and triggers
void SetCollider()
{
// Sphere collider
if (gizmoType == GizmoType.Sphere)
{
SphereCollider col = gameObject.AddComponent<SphereCollider>();
col.isTrigger = type != ActivationType.OnCollision;
col.radius = sphereRadius;
activatorCollider = col;
}
// Box collider
if (gizmoType == GizmoType.Box)
{
BoxCollider col = gameObject.AddComponent<BoxCollider>();
col.isTrigger = type != ActivationType.OnCollision;
col.size = boxSize;
activatorCollider = col;
}
// Custom colliders
if (gizmoType == GizmoType.Collider)
{
Collider[] colliders = GetComponents<Collider>();
if (colliders.Length == 0)
Debug.Log ("RayFire Activator: " + name + " has no activation collider", gameObject);
if (type != ActivationType.OnCollision)
for (int i = 0; i < colliders.Length; i++)
colliders[i].isTrigger = type != ActivationType.OnCollision;
}
}
// Prepare particle system
void SetParticleSystem()
{
collisionEvents = new List<ParticleCollisionEvent>();
ps = GetComponent<ParticleSystem>();
if (ps != null)
{
ParticleSystem.CollisionModule cm = ps.collision;
cm.enabled = true;
cm.enableDynamicColliders = true;
cm.sendCollisionMessages = true;
}
if (type != ActivationType.OnCollision)
{
type = ActivationType.OnCollision;
Debug.Log ("Rayfire Activator: " + name + " Particle System Gizmo Type supports only On Collision Activation type. Set Activation Type to On Collision." , gameObject);
}
}
/// /////////////////////////////////////////////////////////
/// Activation
/// /////////////////////////////////////////////////////////
// Check for activation
void ActivationCheck (Collider coll)
{
if (checkRigid == true)
RigidListActivationCheck (coll);
if (checkRigidRoot == true)
RigidRootActivationCheck (coll);
}
/// /////////////////////////////////////////////////////////
/// Rigid Activation
/// /////////////////////////////////////////////////////////
// Check for Rigid activation
void RigidListActivationCheck(Collider coll)
{
// Get rigid from collider or rigid body
RayfireRigid rigid = coll.attachedRigidbody == null
? coll.GetComponent<RayfireRigid>()
: coll.attachedRigidbody.GetComponent<RayfireRigid>();
// Has no rigid
if (rigid == null)
return;
// Mesh Root rigid
if (rigid.objectType == ObjectType.MeshRoot)
return;
// Activation TODO ??? only for mesh type ???
if (rigid.activation.act == true)
if (rigid.simulationType == SimType.Inactive || rigid.simulationType == SimType.Kinematic)
{
if (delay <= 0)
Activate(rigid);
else
StartCoroutine (DelayedActivationCor (rigid));
}
// Connected cluster one fragment detach
if (rigid.objectType == ObjectType.ConnectedCluster)
if (demolishCluster == true)
{
if (delay <= 0)
{
RFDemolitionCluster.DemolishConnectedCluster (rigid, new[] { coll });
// Init particles
// RFParticles.InitDemolitionParticles(rigid);
}
else
StartCoroutine (DelayedClusterCor (rigid, coll));
}
}
// Exclude from simulation and keep object in scene
IEnumerator DelayedActivationCor (RayfireRigid rigid)
{
// Wait life time
yield return new WaitForSeconds (delay);
// Activate
if (rigid != null)
Activate(rigid);;
}
// Demolish cluster
IEnumerator DelayedClusterCor (RayfireRigid rigid, Collider coll)
{
// Wait life time
yield return new WaitForSeconds (delay);
// Activate
if (rigid != null && coll != null)
RFDemolitionCluster.DemolishConnectedCluster (rigid, new[] {coll});
}
// ActivateRigid
void Activate(RayfireRigid rigid)
{
// Activate
rigid.Activate();
// Add force
AddForce (rigid.physics.rigidBody);
}
/// /////////////////////////////////////////////////////////
/// Rigid Root Activation
/// /////////////////////////////////////////////////////////
// Check for Rigid activation
void RigidRootActivationCheck(Collider coll)
{
// TODO cache activated collider and skip them before get component in parent
// TODO register all RigidRoots and their gameobjects in manager and check for them by coll.gameobject
// Has no rigid root as parent
if (coll.transform.parent == null)
return;
// Get rigid root
RayfireRigidRoot rigidRoot = null;
if (coll.transform.parent != null)
rigidRoot = coll.transform.parent.GetComponentInParent<RayfireRigidRoot>();
// Has no rigid
if (rigidRoot == null)
return;
// Activation
if (rigidRoot.activation.act == true)
if (rigidRoot.simulationType == SimType.Inactive || rigidRoot.simulationType == SimType.Kinematic)
{
if (delay <= 0)
ActivateCollider(rigidRoot, coll);
else
StartCoroutine (DelayedActivationCor (rigidRoot, coll));
}
}
// Exclude from simulation and keep object in scene
IEnumerator DelayedActivationCor (RayfireRigidRoot rigidRoot, Collider coll)
{
// Wait life time
yield return new WaitForSeconds (delay);
// Activate
if (rigidRoot != null)
ActivateCollider(rigidRoot, coll);
}
// Activate shard by collider
void ActivateCollider (RayfireRigidRoot rigidRoot, Collider coll)
{
for (int i = rigidRoot.inactiveShards.Count - 1; i >= 0; i--)
{
if ( rigidRoot.inactiveShards[i].col == coll)
{
// Activate and remove if activated
if (RFActivation.ActivateShard (rigidRoot.inactiveShards[i], rigidRoot) == true)
{
// Add force
AddForce (rigidRoot.inactiveShards[i].rb);
// Remove from list
rigidRoot.inactiveShards.RemoveAt (i);
}
// Break because collider matched shard
break;
}
}
}
/// /////////////////////////////////////////////////////////
/// Force
/// /////////////////////////////////////////////////////////
// Add force to rigidbody
void AddForce(Rigidbody rb)
{
if (apply == true)
{
// Velocity
if (velocity != Vector3.zero)
{
if (coord == false)
rb.AddForce (velocity, mode);
else
rb.AddForce (transform.TransformDirection (velocity), mode);
}
// Angular velocity
if (spin != Vector3.zero)
{
rb.AddTorque (spin, mode);
}
}
}
/// /////////////////////////////////////////////////////////
/// Animation
/// /////////////////////////////////////////////////////////
// Trigger animation start
public void TriggerAnimation()
{
// Already animating
if (animating == true)
return;
// Set animation data
SetAnimation();
// Positions check
if (positionList.Count < 2 && scaleAnimation == 1f)
{
Debug.Log ("Position list is empty and scale is not animated");
return;
}
// Start animation
StartCoroutine (AnimationCor());
}
// Set animation adata
void SetAnimation()
{
// Set points
if (ByLine == true)
SetWorldPointsByLine();
// Set points
if (positionAnimation == AnimationType.ByLocalPositionList)
SetWorldPointsByLocal();
// Set ration checkpoints
SetCheckPoints();
}
// Set points by line
void SetWorldPointsByLine()
{
// Null check
if (line == null)
{
Debug.Log ("Path line is not defined");
return;
}
// Set points
positionList = new List<Vector3>();
for (int i = 0; i < line.positionCount; i++)
positionList.Add (line.transform.TransformPoint (line.GetPosition (i)));
// Add first point if looped
if (line.loop == true)
positionList.Add (positionList[0]);
}
// Set points by line
void SetWorldPointsByLocal()
{
// Positions check
if (positionList.Count < 2)
return;
// List of world positions with current position as start
List<Vector3> worldPoints = new List<Vector3>(){transform.position};
for (int i = 1; i < positionList.Count; i++)
worldPoints.Add (transform.position + positionList[i]);
// Set to position list
positionList.Clear();
positionList = worldPoints;
/*
// List of world positions with current position as start
positionList.Insert(0, transform.position);
for (int i = 1; i < positionList.Count; i++)
positionList[i] += transform.position;
*/
}
// Set ration checkpoints
void SetCheckPoints()
{
// Positions check
if (positionList.Count < 2)
return;
// Total and segments length
lineLength = 0f;
List<float> segmentsLength = new List<float>();
if (positionList.Count >= 2)
{
for (int i = 0; i < positionList.Count - 1; i++)
{
float length = Vector3.Distance (positionList[i], positionList[i + 1]);
segmentsLength.Add (length);
lineLength += length;
}
}
// Get segments ration checkpoints
float sum = 0f;
checkpoints = new float[segmentsLength.Count + 1];
for (int i = 0; i < segmentsLength.Count; i++)
{
float localRation = segmentsLength[i] / lineLength * 100f;
checkpoints[i] = sum;
sum += localRation;
}
checkpoints[segmentsLength.Count] = 100f;
}
//Animation over line coroutine
IEnumerator AnimationCor()
{
// Stop
if (animating == true)
yield break;
// Set state On
animating = true;
// Set starting position
if (positionList.Count >= 2)
transform.position = positionList[0];
while (timePassed < duration)
{
// Stop
if (animating == false)
yield break;
// Update all info for dynamic line
if (positionAnimation == AnimationType.ByDynamicLine)
SetAnimation();
// Prepare info
delta = Time.deltaTime;
timePassed += delta;
// Position animation
if (positionList.Count >= 2)
{
// Increase time and path ratio
deltaRatioStep = delta / duration;
distDeltaStep = lineLength * deltaRatioStep;
distRatio = distDeltaStep / lineLength * 100f;
pathRatio += distRatio;
// Get active line segment
activeSegment = GetSegment (pathRatio);
float segmentRate = (checkpoints[activeSegment + 1] - pathRatio) / (checkpoints[activeSegment + 1] - checkpoints[activeSegment]);
Vector3 stepPos = Vector3.Lerp (positionList[activeSegment + 1], positionList[activeSegment], segmentRate);
transform.position = stepPos;
}
// Scale animation
if (scaleAnimation > 1f)
{
float scaleRate = timePassed / duration;
Vector3 maxScale = new Vector3 (scaleAnimation, scaleAnimation, scaleAnimation);
Vector3 newScale = Vector3.Lerp (scaleStart, maxScale, scaleRate);
transform.localScale = newScale;
}
// Wait
yield return null;
}
// Reset data
ResetData();
}
// Get active segment id
int GetSegment (float ration)
{
if (checkpoints.Length > 2)
{
for (int i = 0; i < checkpoints.Length - 1; i++)
if (ration > checkpoints[i] && ration < checkpoints[i + 1])
return i;
return checkpoints.Length - 2;
}
return 0;
}
// Reset animation info
void ResetData()
{
animating = false;
pathRatio = 0f;
lineLength = 0f;
checkpoints = null;
delta = 0f;
deltaRatioStep = 0f;
distDeltaStep = 0f;
distRatio = 0f;
timePassed = 0f;
activeSegment = 0;
}
// Stop animation
public void StopAnimation()
{
animating = false;
}
// Stop animation
public void ResetAnimation()
{
// Reset info
ResetData();
// Reset position
transform.position = positionStart;
}
// Add new position
public void AddPosition (Vector3 newPos)
{
// Only for global and local
if (ByLine == true)
{
Debug.Log ("Position can be saved only for Global and Local Position animation type.");
return;
}
// Create list
if (positionList == null)
positionList = new List<Vector3>();
// Same position
if (positionList.Count > 0 && newPos == positionList[positionList.Count - 1])
{
Debug.Log ("Activator at the same position.");
return;
}
// Save global position
if (positionAnimation == AnimationType.ByGlobalPositionList)
{
// Check for empty list or same position
if (positionList.Count == 0 || newPos != positionList[positionList.Count - 1])
positionList.Add (newPos);
}
// Save global position
if (positionAnimation == AnimationType.ByLocalPositionList)
{
// First position in world space to save other position in local space relative to first position
if (positionList.Count == 0)
positionList.Add (newPos);
// Other positions in local space relative to first
else
positionList.Add (newPos - positionList[0]);
}
}
/// /////////////////////////////////////////////////////////
/// Gizmo
/// /////////////////////////////////////////////////////////
// Set gizmo
public void SetGizmoType (GizmoType gizmo)
{
gizmoType = gizmo;
// Set new collider
if (Application.isPlaying == true)
{
// Destroy existing collider
if (activatorCollider != null)
Destroy (activatorCollider);
// Set new collider
SetCollider();
}
}
/// /////////////////////////////////////////////////////////
/// Getters
/// /////////////////////////////////////////////////////////
public bool ByPositions
{
get
{
return positionAnimation == AnimationType.ByLocalPositionList ||
positionAnimation == AnimationType.ByGlobalPositionList;
}
}
public bool ByLine
{
get
{
return positionAnimation == AnimationType.ByStaticLine ||
positionAnimation == AnimationType.ByDynamicLine;
}
}
}
}