516 lines
21 KiB
C#
516 lines
21 KiB
C#
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
using Object = UnityEngine.Object;
|
|||
|
|
|||
|
namespace RayFire
|
|||
|
{
|
|||
|
public class RFBackupCluster
|
|||
|
{
|
|||
|
// Connected
|
|||
|
public RFCluster cluster;
|
|||
|
bool saved;
|
|||
|
|
|||
|
// Constructor
|
|||
|
RFBackupCluster()
|
|||
|
{
|
|||
|
saved = false;
|
|||
|
}
|
|||
|
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
/// Save / Restore
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// Save backup cluster to restore it later
|
|||
|
public static void BackupConnectedCluster (RayfireRigid scr)
|
|||
|
{
|
|||
|
// No need to save
|
|||
|
if (scr.reset.action != RFReset.PostDemolitionType.DeactivateToReset)
|
|||
|
return;
|
|||
|
|
|||
|
// Do not backup child clusters
|
|||
|
if (scr.clsDemol.cluster.id > 1)
|
|||
|
return;
|
|||
|
|
|||
|
// Create backup if not exist
|
|||
|
if (scr.clsDemol.backup == null)
|
|||
|
scr.clsDemol.backup = new RFBackupCluster();
|
|||
|
|
|||
|
// Already saved
|
|||
|
if (scr.clsDemol.backup.saved == true)
|
|||
|
return;
|
|||
|
|
|||
|
// Copy class
|
|||
|
scr.clsDemol.backup.cluster = new RFCluster(scr.clsDemol.cluster);
|
|||
|
|
|||
|
// Init shards: set non serialized vars
|
|||
|
RFCluster.InitCluster (scr, scr.clsDemol.backup.cluster);
|
|||
|
|
|||
|
// Save nested clusters shards and clusters position and rotation
|
|||
|
SaveTmRecursive (scr.clsDemol.backup.cluster);
|
|||
|
|
|||
|
// Backup created, do not create again at next reset
|
|||
|
scr.clsDemol.backup.saved = true;
|
|||
|
|
|||
|
// Debug.Log ("Saved");
|
|||
|
}
|
|||
|
|
|||
|
// Restore cluster using backup cluster
|
|||
|
public static void ResetRigidCluster (RayfireRigid scr)
|
|||
|
{
|
|||
|
if (scr.reset.action == RFReset.PostDemolitionType.DeactivateToReset)
|
|||
|
{
|
|||
|
// Do not restore child clusters
|
|||
|
if (scr.clsDemol.cluster.id > 1)
|
|||
|
return;
|
|||
|
|
|||
|
// Has no backup
|
|||
|
if (scr.clsDemol.backup == null)
|
|||
|
return;
|
|||
|
|
|||
|
// Cluster was not demolished. Stop
|
|||
|
if (scr.objTp == ObjectType.ConnectedCluster)
|
|||
|
if (scr.clsDemol.cluster.shards.Count == scr.clsDemol.backup.cluster.shards.Count)
|
|||
|
return;
|
|||
|
|
|||
|
// TODO check if nested cluster was demolished
|
|||
|
// if (false) if (scr.objectType == ObjectType.NestedCluster)
|
|||
|
// if (scr.clusterDemolition.cluster.tm.gameObject.activeSelf == true)
|
|||
|
//return;
|
|||
|
|
|||
|
// Completely demolished child clusters do not deactivates if saved
|
|||
|
// Unyielding component with inactive overlap bug
|
|||
|
|
|||
|
// Reset fragments list
|
|||
|
scr.fragments = null;
|
|||
|
|
|||
|
// Remove particles
|
|||
|
DestroyParticles (scr);
|
|||
|
|
|||
|
// Reset local shard rigid, destroy components TODO INPUT ORIGINAL CLUSTER, GET RIGIDS
|
|||
|
ResetDeepShardRigid (scr, scr.clsDemol.backup.cluster);
|
|||
|
|
|||
|
// Create new child clusters roots destroy by nested cluster. BEFORE reparent shards
|
|||
|
if (scr.objTp == ObjectType.NestedCluster)
|
|||
|
{
|
|||
|
ResetNestedRootsRecursive (scr.clsDemol.backup.cluster);
|
|||
|
ResetNestedTransformRecursive (scr.clsDemol.backup.cluster);
|
|||
|
ResetNestedParentsRecursive (scr.clsDemol.backup.cluster);
|
|||
|
}
|
|||
|
|
|||
|
// Restore shards parent, position and rotation
|
|||
|
RestoreShardTmRecursive (scr.clsDemol.backup.cluster);
|
|||
|
|
|||
|
// Destroy new child clusters roots created by connected cluster. AFTER reparent shards
|
|||
|
if (scr.objTp == ObjectType.ConnectedCluster)
|
|||
|
DestroyRoots (scr);
|
|||
|
|
|||
|
// Copy class
|
|||
|
scr.clsDemol.cluster = new RFCluster(scr.clsDemol.backup.cluster);
|
|||
|
|
|||
|
// Reset colliders
|
|||
|
RFPhysic.CollectClusterColliders (scr, scr.clsDemol.cluster);
|
|||
|
|
|||
|
// Init shards: set non serialized vars
|
|||
|
RFCluster.InitCluster (scr, scr.clsDemol.cluster);
|
|||
|
|
|||
|
scr.clsDemol.collapse.inProgress = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Remove particles
|
|||
|
static void DestroyParticles(RayfireRigid scr)
|
|||
|
{
|
|||
|
if (scr.HasDebris == true)
|
|||
|
for (int i = 0; i < scr.debrisList.Count; i++)
|
|||
|
{
|
|||
|
for (int c = scr.debrisList[i].children.Count - 1; c >= 0; c--)
|
|||
|
{
|
|||
|
if (scr.debrisList[i].children[c] != null)
|
|||
|
{
|
|||
|
if (scr.debrisList[i].children[c].hostTm != null)
|
|||
|
Object.Destroy (scr.debrisList[i].children[c].hostTm.gameObject);
|
|||
|
Object.Destroy (scr.debrisList[i].children[c]);
|
|||
|
}
|
|||
|
scr.debrisList[i].children.RemoveAt (c);
|
|||
|
}
|
|||
|
scr.debrisList[i].children = null;
|
|||
|
}
|
|||
|
if (scr.HasDust == true)
|
|||
|
for (int i = 0; i < scr.dustList.Count; i++)
|
|||
|
{
|
|||
|
for (int c = scr.dustList[i].children.Count - 1; c >= 0; c--)
|
|||
|
{
|
|||
|
if (scr.dustList[i].children[c] != null)
|
|||
|
{
|
|||
|
if (scr.dustList[i].children[c].hostTm != null)
|
|||
|
Object.Destroy (scr.dustList[i].children[c].hostTm.gameObject);
|
|||
|
Object.Destroy (scr.dustList[i].children[c]);
|
|||
|
}
|
|||
|
scr.dustList[i].children.RemoveAt (c);
|
|||
|
}
|
|||
|
scr.dustList[i].children = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
/// Reset shard rigid
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// Reset local shard rigid, destroy components
|
|||
|
static void ResetDeepShardRigid (RayfireRigid scr, RFCluster cluster)
|
|||
|
{
|
|||
|
// Collect shards colliders
|
|||
|
for (int i = 0; i < cluster.shards.Count; i++)
|
|||
|
ResetShardRigid (cluster.shards[i]);
|
|||
|
|
|||
|
// Set child cluster colliders
|
|||
|
if (scr.objTp == ObjectType.NestedCluster)
|
|||
|
if (cluster.HasChildClusters == true)
|
|||
|
for (int i = 0; i < cluster.childClusters.Count; i++)
|
|||
|
ResetDeepShardRigid (scr, cluster.childClusters[i]);
|
|||
|
}
|
|||
|
|
|||
|
// Reset local shard rigid, destroy components
|
|||
|
static void ResetShardRigid (RFShard shard)
|
|||
|
{
|
|||
|
// Set parent
|
|||
|
shard.tm.parent = shard.cluster.tm;
|
|||
|
|
|||
|
// Enable collider in case of fade disable
|
|||
|
if (shard.col.enabled == false)
|
|||
|
shard.col.enabled = true;
|
|||
|
|
|||
|
// Get shard's rigid
|
|||
|
if (shard.rigid == null)
|
|||
|
shard.rigid = shard.tm.GetComponent<RayfireRigid>();
|
|||
|
|
|||
|
if (shard.rigid != null && shard.rigid.initialized == true)
|
|||
|
{
|
|||
|
// Stop all cors in case object restarted
|
|||
|
shard.rigid.StopAllCoroutines();
|
|||
|
shard.rigid.initialized = false;
|
|||
|
|
|||
|
// Destroy rigid body
|
|||
|
if (shard.rigid.physics.rb != null)
|
|||
|
{
|
|||
|
shard.rigid.physics.rb.velocity = Vector3.zero;
|
|||
|
Object.Destroy (shard.rigid.physics.rb);
|
|||
|
}
|
|||
|
|
|||
|
// Save faded/demolished state before reset
|
|||
|
int faded = shard.rigid.fading.state;
|
|||
|
bool demolished = shard.rigid.limitations.demolished;
|
|||
|
|
|||
|
// Reset activation TODO check if it was Kinematic
|
|||
|
if (shard.rigid.activation.activated == true)
|
|||
|
shard.rigid.simTp = SimType.Inactive;
|
|||
|
|
|||
|
// Reset rigid props
|
|||
|
shard.rigid.physics.LocalReset();
|
|||
|
shard.rigid.activation.LocalReset();
|
|||
|
shard.rigid.limitations.LocalReset();
|
|||
|
shard.rigid.mshDemol.LocalReset();
|
|||
|
shard.rigid.clsDemol.LocalReset();
|
|||
|
shard.rigid.fading.LocalReset();
|
|||
|
shard.rigid.damage.LocalReset();
|
|||
|
|
|||
|
// Reset if object fading/faded
|
|||
|
if (faded >= 1)
|
|||
|
RFReset.ResetFade(shard.rigid);
|
|||
|
|
|||
|
// Demolished. Restore
|
|||
|
if (demolished == true)
|
|||
|
RFReset.ResetMeshDemolition (shard.rigid);
|
|||
|
|
|||
|
// Remove particles
|
|||
|
// DestroyParticles (shard.rigid);
|
|||
|
|
|||
|
// shard.rigid.initialized = false;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// TODO COPY RIGID TO BACKUP CLUSTER SHARD.RIGID TO REUSE AT NEXT RESET. AVOID GETCOMPONENT
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
/// Transform / parent
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// Save cluster/shards tm
|
|||
|
public static void SaveTmRecursive(RFCluster cluster)
|
|||
|
{
|
|||
|
// Save cluster tm
|
|||
|
cluster.pos = cluster.tm.position;
|
|||
|
cluster.rot = cluster.tm.rotation;
|
|||
|
cluster.scl = cluster.tm.lossyScale;
|
|||
|
|
|||
|
// Save shards tm
|
|||
|
for (int i = 0; i < cluster.shards.Count; i++)
|
|||
|
{
|
|||
|
cluster.shards[i].pos = cluster.shards[i].tm.position;
|
|||
|
cluster.shards[i].rot = cluster.shards[i].tm.rotation;
|
|||
|
cluster.shards[i].scl = cluster.shards[i].tm.localScale;
|
|||
|
}
|
|||
|
|
|||
|
// Repeat for child clusters
|
|||
|
if (cluster.HasChildClusters == true)
|
|||
|
for (int i = 0; i < cluster.childClusters.Count; i++)
|
|||
|
SaveTmRecursive (cluster.childClusters[i]);
|
|||
|
}
|
|||
|
|
|||
|
// Save cluster/shards tm
|
|||
|
static void RestoreShardTmRecursive(RFCluster cluster)
|
|||
|
{
|
|||
|
// Save shards tm
|
|||
|
for (int i = 0; i < cluster.shards.Count; i++)
|
|||
|
{
|
|||
|
cluster.shards[i].tm.SetParent (null);
|
|||
|
cluster.shards[i].tm.SetPositionAndRotation (cluster.shards[i].pos, cluster.shards[i].rot);
|
|||
|
cluster.shards[i].tm.SetParent (cluster.tm, true);
|
|||
|
cluster.shards[i].tm.localScale = cluster.shards[i].scl;
|
|||
|
}
|
|||
|
|
|||
|
// Repeat for child clusters
|
|||
|
if (cluster.HasChildClusters == true)
|
|||
|
for (int i = 0; i < cluster.childClusters.Count; i++)
|
|||
|
RestoreShardTmRecursive (cluster.childClusters[i]);
|
|||
|
}
|
|||
|
|
|||
|
// Save cluster/shards tm
|
|||
|
static void ResetNestedTransformRecursive(RFCluster cluster)
|
|||
|
{
|
|||
|
// Save cluster tm
|
|||
|
cluster.tm.rotation = cluster.rot;
|
|||
|
cluster.tm.position = cluster.pos;
|
|||
|
|
|||
|
// TODO FIND PROPER ORDER cluster.tm.localScale = cluster.scl;
|
|||
|
|
|||
|
// Repeat for child clusters
|
|||
|
if (cluster.HasChildClusters == true)
|
|||
|
for (int i = 0; i < cluster.childClusters.Count; i++)
|
|||
|
ResetNestedTransformRecursive (cluster.childClusters[i]);
|
|||
|
}
|
|||
|
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
/// Roots
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// Create deleted roots and restore their tm back
|
|||
|
static void ResetNestedRootsRecursive(RFCluster cluster)
|
|||
|
{
|
|||
|
if (cluster.HasChildClusters == true)
|
|||
|
{
|
|||
|
for (int i = 0; i < cluster.childClusters.Count; i++)
|
|||
|
{
|
|||
|
// Reset parent
|
|||
|
cluster.childClusters[i].tm.parent = null;
|
|||
|
//cluster.tm.localScale = cluster.scl;
|
|||
|
|
|||
|
// Get rigid component
|
|||
|
if (cluster.childClusters[i].rigid == null)
|
|||
|
cluster.childClusters[i].rigid = cluster.childClusters[i].tm.GetComponent<RayfireRigid>();
|
|||
|
|
|||
|
// Destroy rigid
|
|||
|
if (cluster.childClusters[i].rigid != null)
|
|||
|
{
|
|||
|
// Destroy rigid body
|
|||
|
if (cluster.childClusters[i].rigid.physics.rb != null)
|
|||
|
Object.Destroy (cluster.childClusters[i].rigid.physics.rb);
|
|||
|
|
|||
|
// Destroy rigid
|
|||
|
Object.Destroy (cluster.childClusters[i].rigid);
|
|||
|
}
|
|||
|
|
|||
|
// Activate
|
|||
|
cluster.childClusters[i].tm.gameObject.SetActive (true);
|
|||
|
|
|||
|
// Repeat for children
|
|||
|
ResetNestedRootsRecursive (cluster.childClusters[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Create deleted roots and restore their tm back
|
|||
|
static void ResetNestedParentsRecursive(RFCluster cluster)
|
|||
|
{
|
|||
|
if (cluster.HasChildClusters == true)
|
|||
|
for (int i = cluster.childClusters.Count - 1; i >= 0; i--)
|
|||
|
{
|
|||
|
cluster.childClusters[i].tm.parent = cluster.tm;
|
|||
|
ResetNestedParentsRecursive (cluster.childClusters[i]);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Destroy new child clusters roots created by connected cluster
|
|||
|
static void DestroyRoots (RayfireRigid scr)
|
|||
|
{
|
|||
|
for (int i = 0; i < scr.clsDemol.minorClusters.Count; i++)
|
|||
|
{
|
|||
|
if (scr.clsDemol.minorClusters[i].tm != null)
|
|||
|
{
|
|||
|
scr.clsDemol.minorClusters[i].tm.gameObject.SetActive (false);
|
|||
|
Object.Destroy (scr.clsDemol.minorClusters[i].tm.gameObject);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
/// Connectivity
|
|||
|
/// /////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// Backup abd restore state
|
|||
|
static bool BackupState (RayfireConnectivity scr, bool warning = false)
|
|||
|
{
|
|||
|
// Not for in editor setup. Runtime only
|
|||
|
if (Application.isPlaying == false)
|
|||
|
return false;
|
|||
|
|
|||
|
// Has no connectivity
|
|||
|
if (scr == null)
|
|||
|
return false;
|
|||
|
|
|||
|
// No Rigid or Rigid Root
|
|||
|
if (scr.meshRootHost == null && scr.rigidRootHost == null)
|
|||
|
return false;
|
|||
|
|
|||
|
// Rigid not mesh root
|
|||
|
if (scr.meshRootHost != null && scr.meshRootHost.objTp != ObjectType.MeshRoot)
|
|||
|
return false;
|
|||
|
|
|||
|
// Backup disabled
|
|||
|
if (scr.meshRootHost != null && scr.meshRootHost.reset.connectivity == false)
|
|||
|
{
|
|||
|
if (warning == true)
|
|||
|
Debug.Log ("RayFire Rigid: " + scr.name + " can not reset Connectivity. Enable Connectivity Reset in Reset properties", scr.gameObject);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// Backup disabled
|
|||
|
if (scr.rigidRootHost != null && scr.rigidRootHost.reset.connectivity == false)
|
|||
|
{
|
|||
|
if (warning == true)
|
|||
|
Debug.Log ("RayFire Rigid Root: " + scr.name + " can not reset Connectivity. Enable Connectivity Reset in Reset properties", scr.gameObject);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
// Save backup cluster to restore it later
|
|||
|
public static void BackupConnectivity (RayfireConnectivity scr)
|
|||
|
{
|
|||
|
// Checks state
|
|||
|
if (BackupState(scr) == false)
|
|||
|
return;
|
|||
|
|
|||
|
// Create backup if not exist
|
|||
|
if (scr.backup == null)
|
|||
|
scr.backup = new RFBackupCluster();
|
|||
|
|
|||
|
// Already saved
|
|||
|
if (scr.backup.saved == true)
|
|||
|
return;
|
|||
|
|
|||
|
// Copy class TODO copy shards stress
|
|||
|
scr.backup.cluster = new RFCluster(scr.cluster);
|
|||
|
|
|||
|
// TODO init non serialized vars
|
|||
|
|
|||
|
// Save nested clusters shards and clusters position and rotation
|
|||
|
SaveTmRecursive (scr.backup.cluster);
|
|||
|
|
|||
|
// Backup created, do not create again at next reset
|
|||
|
scr.backup.saved = true;
|
|||
|
}
|
|||
|
|
|||
|
// Restore cluster using backup cluster
|
|||
|
public static void RestoreConnectivity (RayfireConnectivity scr)
|
|||
|
{
|
|||
|
// Checks state
|
|||
|
if (BackupState(scr, true) == false)
|
|||
|
return;
|
|||
|
|
|||
|
// Has no backup
|
|||
|
if (scr.backup == null)
|
|||
|
return;
|
|||
|
|
|||
|
// Stop all coroutines
|
|||
|
scr.StopAllCoroutines();
|
|||
|
|
|||
|
// Reset connectivity
|
|||
|
scr.ResetConnectivity();
|
|||
|
|
|||
|
// Mesh Root Rigid init
|
|||
|
if (scr.meshRootHost != null)
|
|||
|
{
|
|||
|
// Restore shards parent, position and rotation
|
|||
|
RestoreShardTmRecursive (scr.backup.cluster);
|
|||
|
|
|||
|
// Copy class
|
|||
|
scr.cluster = new RFCluster(scr.backup.cluster);
|
|||
|
|
|||
|
// Shards were cached, reinit non serialized vars, clear list otherwise
|
|||
|
if (RayfireConnectivity.InitCachedShardsByRigidList (scr.rigidList, scr.cluster) == true)
|
|||
|
scr.cluster.shards.Clear();
|
|||
|
|
|||
|
// Set this connectivity as main connectivity node
|
|||
|
for (int i = 0; i < scr.rigidList.Count; i++)
|
|||
|
scr.rigidList[i].activation.cnt = scr;
|
|||
|
}
|
|||
|
|
|||
|
// Rigid Root' Connectivity cluster shards init
|
|||
|
if (scr.rigidRootHost != null)
|
|||
|
{
|
|||
|
// Copy connection data from backup cluster back to RigidRoot cluster
|
|||
|
for (int i = 0; i < scr.rigidRootHost.cluster.shards.Count; i++)
|
|||
|
{
|
|||
|
// if (scr.rigidRootHost.cluster.shards[i].sm == SimType.Inactive || scr.rigidRootHost.cluster.shards[i].sm == SimType.Kinematic)
|
|||
|
{
|
|||
|
// Neib ids
|
|||
|
scr.rigidRootHost.cluster.shards[i].nIds = new List<int> (scr.backup.cluster.shards[i].nIds.Count);
|
|||
|
for (int j = 0; j < scr.backup.cluster.shards[i].nIds.Count; j++)
|
|||
|
scr.rigidRootHost.cluster.shards[i].nIds.Add (scr.backup.cluster.shards[i].nIds[j]);
|
|||
|
|
|||
|
// Neib areas
|
|||
|
scr.rigidRootHost.cluster.shards[i].nArea = new List<float> (scr.backup.cluster.shards[i].nArea.Count);
|
|||
|
for (int j = 0; j < scr.backup.cluster.shards[i].nArea.Count; j++)
|
|||
|
scr.rigidRootHost.cluster.shards[i].nArea.Add (scr.backup.cluster.shards[i].nArea[j]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (scr.stress.enable == true)
|
|||
|
{
|
|||
|
for (int s = 0; s < scr.rigidRootHost.cluster.shards.Count; s++)
|
|||
|
{
|
|||
|
scr.rigidRootHost.cluster.shards[s].sIds = new List<int> (scr.backup.cluster.shards[s].sIds.Count);
|
|||
|
for (int i = 0; i < scr.backup.cluster.shards[s].sIds.Count; i++)
|
|||
|
scr.rigidRootHost.cluster.shards[s].sIds.Add (scr.backup.cluster.shards[s].sIds[i]);
|
|||
|
|
|||
|
scr.rigidRootHost.cluster.shards[s].nSt = new List<float>();
|
|||
|
for (int i = 0; i < scr.backup.cluster.shards[s].nSt.Count; i++)
|
|||
|
scr.rigidRootHost.cluster.shards[s].nSt.Add (scr.backup.cluster.shards[s].nSt[i]);
|
|||
|
|
|||
|
scr.rigidRootHost.cluster.shards[s].sSt = scr.backup.cluster.shards[s].sSt;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Shards were cached, reinit non serialized vars, clear list otherwise
|
|||
|
RayfireConnectivity.InitCachedShardsByRigidRoot (scr.rigidRootHost, scr.cluster);
|
|||
|
|
|||
|
// Set range for area and size
|
|||
|
RFCollapse.CopyRangeData (scr.cluster, scr.backup.cluster);
|
|||
|
}
|
|||
|
|
|||
|
// Reset coroutines
|
|||
|
scr.childrenChanged = false;
|
|||
|
scr.connectivityCheckNeed = false;
|
|||
|
scr.collapse.inProgress = false;
|
|||
|
scr.stress.inProgress = false;
|
|||
|
|
|||
|
// Start all coroutines
|
|||
|
scr.StartAllCoroutines();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|