Exemplo n.º 1
0
        // Clusterize not connected groups
        void Clusterize()
        {
            for (int i = 0; i < cluster.childClusters.Count; i++)
            {
                // set demolishable state for child cluster
                cluster.demolishable = demolishable;

                // Set bound
                cluster.childClusters[i].bound = RFCluster.GetShardsBound(cluster.childClusters[i].shards);

                // Create cluster
                cluster.childClusters[i].shards[0].rigid.simulationType = SimType.Dynamic; // TODO IN BETTER WAY
                cluster.childClusters[i].shards[0].rigid.objectType     = ObjectType.ConnectedCluster;
                RFDemolitionCluster.CreateClusterRuntime(cluster.childClusters[i].shards[0].rigid, cluster.childClusters[i]);
                cluster.childClusters[i].shards[0].rigid.objectType = ObjectType.Mesh;

                // Copy preview
                cluster.childClusters[i].rigid.clusterDemolition.cn = showConnections;
                cluster.childClusters[i].rigid.clusterDemolition.nd = showNodes;

                // Destroy components
                for (int s = 0; s < cluster.childClusters[i].shards.Count; s++)
                {
                    Destroy(cluster.childClusters[i].shards[s].rigid.physics.rigidBody);
                    Destroy(cluster.childClusters[i].shards[s].rigid);
                }
            }
        }
        static void DrawGizmosSelected(RayfireConnectivity targ, GizmoType gizmoType)
        {
            // Connections
            if (targ.showConnections == true)
            {
                if (Application.isPlaying == true)
                {
                    Gizmos.color = Color.green;
                    if (targ.cluster != null && targ.cluster.shards.Count > 0)
                    {
                        foreach (var shard in targ.cluster.shards)
                        {
                            // Set color
                            Gizmos.color = shard.rigid.activation.unyielding == true
                                ? Color.red
                                : Color.green;

                            // draw sphere
                            if (targ.sphereSize > 0)
                            {
                                Gizmos.DrawWireSphere(shard.tm.position, shard.bound.size.magnitude / 13f * targ.sphereSize);
                            }

                            // Draw connection
                            foreach (var neibShard in shard.neibShards)
                            {
                                if (neibShard.rigid.activation.connect != null)
                                {
                                    Gizmos.DrawLine(shard.tm.position, neibShard.tm.position);
                                }
                            }
                        }
                    }
                }
            }

            // Gizmo preview
            if (targ.showGizmo == true)
            {
                // Gizmo properties
                Gizmos.color = wireColor;

                // Gizmo
                if (targ.source == RayfireConnectivity.ConnTargetType.Gizmo)
                {
                    Gizmos.matrix = targ.transform.localToWorldMatrix;
                    Gizmos.DrawWireCube(Vector3.zero, targ.size);
                }

                // Children
                if (targ.source == RayfireConnectivity.ConnTargetType.Children)
                {
                    if (targ.transform.childCount > 0)
                    {
                        Bounds bound = RFCluster.GetChildrenBound(targ.transform);
                        Gizmos.DrawWireCube(bound.center, bound.size);
                    }
                }
            }
        }
Exemplo n.º 3
0
        // Prepare shards. Set bounds, set neibs
        static void SetShardsByRigids(RFCluster cluster, List <RayfireRigid> rigidList, ConnectivityType connectivity)
        {
            for (int i = 0; i < rigidList.Count; i++)
            {
                // Get mesh filter
                MeshFilter mf = rigidList[i].GetComponent <MeshFilter>();

                // Child has no mesh
                if (mf == null)
                {
                    continue;
                }

                // Create new shard
                RFShard shard = new RFShard(rigidList[i].transform, i);
                shard.cluster = cluster;
                shard.rigid   = rigidList[i];
                shard.uny     = rigidList[i].activation.unyielding;
                shard.col     = rigidList[i].physics.meshCollider;

                // Set faces data for connectivity
                if (connectivity == ConnectivityType.ByMesh)
                {
                    RFTriangle.SetTriangles(shard, mf);
                }

                // Collect shard
                cluster.shards.Add(shard);
            }
        }
Exemplo n.º 4
0
        // Reinit shard's non serialized fields in case of prefab use
        public static void InitShards(List <RayfireRigid> rigids, RFCluster cluster)
        {
            if (cluster.initialized == false)
            {
                // Rigid list doesn't match shards. TODO compare per shard
                if (cluster.shards.Count != rigids.Count)
                {
                    cluster.shards.Clear();
                    return;
                }

                // Reinit
                for (int s = 0; s < cluster.shards.Count; s++)
                {
                    if (rigids[s] != null)
                    {
                        cluster.shards[s].rigid = rigids[s];
                        cluster.shards[s].uny   = rigids[s].activation.unyielding;
                    }

                    cluster.shards[s].cluster    = cluster;
                    cluster.shards[s].neibShards = new List <RFShard>();
                    for (int n = 0; n < cluster.shards[s].nIds.Count; n++)
                    {
                        cluster.shards[s].neibShards.Add(cluster.shards[cluster.shards[s].nIds[n]]);
                    }
                }
                cluster.initialized = true;
            }
        }
Exemplo n.º 5
0
        // Create root for cluster at shards center and set shards as children
        void CreateRoot(RFCluster childCluster, Transform parentTm)
        {
            // Get cluster bound
            Bounds childBound = GetShardsBound(childCluster.shards, childCluster.childClusters);

            // Set cluster bound
            childCluster.bound = childBound;

            // Create root for cluster
            GameObject childRoot = new GameObject();

            // Set cluster root position
            childCluster.tm          = childRoot.transform;
            childCluster.pos         = childBound.center;
            childCluster.tm.position = childBound.center;

            // Set cluster parent
            childCluster.tm.parent = parentTm;

            // Set cluster root as shards parent
            foreach (RFShard shard in childCluster.shards)
            {
                shard.tm.parent = childCluster.tm;
            }
        }
Exemplo n.º 6
0
        // Add solo shards to closest cluster
        void SetSoloClusterToCluster(List <RFCluster> soloClusters, List <RFCluster> childClusters)
        {
            // No solo clusters
            if (soloClusters.Count == 0)
            {
                return;
            }

            // Find neib cluster for solo cluster
            for (int i = soloClusters.Count - 1; i >= 0; i--)
            {
                int ind = soloClusters[i].GetNeibIndArea();
                if (ind >= 0)
                {
                    RFCluster neibCluster = soloClusters[i].neibClusters[ind];
                    for (int c = 0; c < childClusters.Count; c++)
                    {
                        if (childClusters[c].childClusters.Contains(neibCluster) == true)
                        {
                            childClusters[c].childClusters.Add(soloClusters[i]);
                            soloClusters.RemoveAt(i);
                            continue;
                        }
                    }
                }
            }
        }
Exemplo n.º 7
0
        // Set up main cluster and set shards
        RFCluster SetupMainCluster(ConnectivityType connect)
        {
            // Create Base cluster
            RFCluster cluster = new RFCluster();

            cluster.tm    = transform;
            cluster.depth = 0;
            cluster.pos   = transform.position;

            // Set cluster id
            cluster.id = 0;

            // Set shards for main cluster
            RFShard.SetShards(cluster, connectivity);

            clusterId = 0;

            // Collect all shards
            allShards.Clear();
            allShards.AddRange(cluster.shards);

            // TODO set bound

            return(cluster);
        }
Exemplo n.º 8
0
        /// /////////////////////////////////////////////////////////
        /// Roots
        /// /////////////////////////////////////////////////////////

        // Create deleted roots and restore their tm back
        static void ResetRootsRecursive(RFCluster cluster)
        {
            if (cluster.HasChildClusters == true)
            {
                for (int i = 0; i < cluster.childClusters.Count; i++)
                {
                    cluster.childClusters[i].tm.parent = null;

                    // Destroy rigid
                    cluster.childClusters[i].rigid = cluster.childClusters[i].tm.GetComponent <RayfireRigid>();
                    if (cluster.childClusters[i].rigid != null)
                    {
                        // Destroy rigid body
                        if (cluster.childClusters[i].rigid.physics.rigidBody != null)
                        {
                            Object.Destroy(cluster.childClusters[i].rigid.physics.rigidBody);
                        }

                        Object.Destroy(cluster.childClusters[i].rigid);
                    }

                    // Activate
                    cluster.childClusters[i].tm.gameObject.SetActive(true);

                    // Repeat for children
                    ResetRootsRecursive(cluster.childClusters[i]);
                }
            }
        }
Exemplo n.º 9
0
        // Init collapse after connection loss
        static void CollapseCluster(RayfireRigid scr)
        {
            // Collect solo shards, remove from cluster, no need to reinit
            List <RFShard> detachShards = new List <RFShard>();

            RFCluster.GetSoloShards(scr.clusterDemolition.cluster, detachShards);

            // Clear fragments in case of previous demolition
            if (scr.HasFragments == true)
            {
                scr.fragments.Clear();
            }

            // Dynamic cluster connectivity check, all clusters are equal, pick biggest to keep as original
            if (scr.simulationType == SimType.Dynamic || scr.simulationType == SimType.Sleeping)
            {
                // Check left cluster shards for connectivity and collect not connected child clusters. Should be before ClusterizeDetachShards
                RFCluster.ConnectivityCheck(scr.clusterDemolition.cluster);

                // Cluster is not connected. Set biggest child cluster shards to original cluster. Cant be 1 child cluster here
                RFCluster.ReduceChildClusters(scr.clusterDemolition.cluster);
            }

            // Kinematik/ Inactive cluster, Connectivity check if cluster has uny shards. Main cluster keeps all not activated
            else if (scr.simulationType == SimType.Kinematic || scr.simulationType == SimType.Inactive)
            {
                RFCluster.ConnectivityUnyCheck(scr.clusterDemolition.cluster);
            }

            // Init final cluster ops
            RFDemolitionCluster.PostDemolitionCluster(scr, detachShards);
        }
Exemplo n.º 10
0
        // Get not connected groups
        void Check()
        {
            // Get not connected clusters
            RFCluster.ConnectivityCheckUny(cluster);

            // TODO turn of if no objs to activate

            //  clusters
        }
Exemplo n.º 11
0
        /// /////////////////////////////////////////////////////////
        /// Constructor
        /// /////////////////////////////////////////////////////////

        // Constructor
        public RFDemolitionCluster()
        {
            meshDemolition = false;
            connectivity   = ConnectivityType.ByBoundingBox;
            contactRadius  = 15;

            cluster = null;

            Reset();
        }
Exemplo n.º 12
0
        // Set cluster
        void SetCluster()
        {
            cluster = new RFCluster();

            // Set shards for main cluster
            cluster.shards = RFShard.GetShards(rigidList, connectivityType);

            // Set shard neibs
            RFShard.SetShardNeibs(cluster.shards, connectivityType);
        }
Exemplo n.º 13
0
        // Create runtime clusters
        static void CreateClusterRuntime(RayfireRigid source, RFCluster cls)
        {
            // Cluster with solo shard. Add rigid component, reparent
            if (cls.shards.Count == 1)
            {
                AddRigidComponent(source, new List <Transform> (1)
                {
                    cls.shards[0].tm
                });
                cls.shards[0].tm.parent = RayfireMan.inst.transForm;
                return;
            }

            // Create root for left children
            GameObject leftRoot = new GameObject();

            // Turn off
            leftRoot.SetActive(false);

            leftRoot.name = source.gameObject.name + "_cls";
            leftRoot.transform.position = source.transForm.position;
            leftRoot.transform.rotation = source.transForm.rotation;
            leftRoot.transform.parent   = RayfireMan.inst.transForm;

            // Parent to main root
            for (int s = 0; s < cls.shards.Count; s++)
            {
                cls.shards[s].tm.parent = leftRoot.transform;
            }

            // Add rigid to object
            RayfireRigid target = leftRoot.gameObject.AddComponent <RayfireRigid>();

            target.initialization = RayfireRigid.InitType.AtStart;

            // Collect fragment
            source.fragments.Add(target);

            // Copy properties from parent to fragment node
            source.CopyPropertiesTo(target);

            // Copy particles
            RFParticles.CopyParticles(source, target);

            // Set to mesh
            target.objectType           = ObjectType.ConnectedCluster;
            target.physics.colliderType = RFColliderType.Mesh;

            // Set cluster
            target.clusterDemolition.cluster    = cls;
            target.clusterDemolition.cluster.id = 2;

            // Turn on
            leftRoot.SetActive(true);
        }
Exemplo n.º 14
0
 // Create deleted roots and restore their tm back
 static void ResetRootsParentsRecursive(RFCluster cluster)
 {
     if (cluster.HasChildClusters == true)
     {
         for (int i = 0; i < cluster.childClusters.Count; i++)
         {
             cluster.childClusters[i].tm.parent = cluster.tm;
             ResetRootsParentsRecursive(cluster.childClusters[i]);
         }
     }
 }
Exemplo n.º 15
0
        /// /////////////////////////////////////////////////////////
        /// Shards
        /// /////////////////////////////////////////////////////////

        // Prepare shards. Set bounds, set neibs
        public static void SetShards(RFCluster cluster, ConnectivityType connectivity, bool setRigid = false)
        {
            // Get all children tms
            List <Transform> tmList = new List <Transform>();

            for (int i = 0; i < cluster.tm.childCount; i++)
            {
                tmList.Add(cluster.tm.GetChild(i));
            }

            // Get child shards
            SetShardsByTransforms(cluster, tmList, connectivity, setRigid);
        }
Exemplo n.º 16
0
 // Clear all activated/demolished shards
 static void CleanUpActivatedShards(RFCluster cluster)
 {
     for (int i = cluster.shards.Count - 1; i >= 0; i--)
     {
         if (cluster.shards[i].rigid == null ||
             cluster.shards[i].rigid.activation.connect == null ||
             cluster.shards[i].rigid.limitations.demolished == true)
         {
             cluster.shards[i].cluster = null;
             cluster.shards.RemoveAt(i);
         }
     }
 }
Exemplo n.º 17
0
 static void GizmoDraw(RayfireConnectivity targ)
 {
     if (targ.showGizmo == true)
     {
         // Gizmo properties
         Gizmos.color = wireColor;
         if (targ.transform.childCount > 0)
         {
             Bounds bound = RFCluster.GetChildrenBound(targ.transform);
             Gizmos.DrawWireCube(bound.center, bound.size);
         }
     }
 }
Exemplo n.º 18
0
        // Check children for mesh or cluster root until all children will not be checked
        static void SetDeepShardColliders(RayfireRigid scr, RFCluster cluster)
        {
            // Set shard colliders
            SetShardColliders(scr, cluster);

            // Set child cluster colliders
            if (cluster.HasChildClusters == true)
            {
                for (int i = 0; i < cluster.childClusters.Count; i++)
                {
                    SetDeepShardColliders(scr, cluster.childClusters[i]);
                }
            }
        }
Exemplo n.º 19
0
        // Set range for area and size
        public static void SetRangeData(RFCluster cluster, int perc = 0, int seed = 0)
        {
            if (cluster.shards.Count == 0)
            {
                return;
            }

            // Start values
            cluster.maximumSize    = cluster.shards[0].sz;
            cluster.minimumSize    = cluster.shards[0].sz;
            cluster.maximumArea    = 0f;
            cluster.minimumArea    = 10000f;
            cluster.randomCollapse = perc;
            cluster.randomSeed     = seed;

            // Loop shards
            for (int i = 0; i < cluster.shards.Count; i++)
            {
                if (cluster.shards[i].sz > cluster.maximumSize)
                {
                    cluster.maximumSize = cluster.shards[i].sz;
                }
                if (cluster.shards[i].sz < cluster.minimumSize)
                {
                    cluster.minimumSize = cluster.shards[i].sz;
                }

                for (int j = 0; j < cluster.shards[i].nArea.Count; j++)
                {
                    if (cluster.shards[i].nArea[j] > cluster.maximumArea)
                    {
                        cluster.maximumArea = cluster.shards[i].nArea[j];
                    }

                    if (cluster.shards[i].nArea[j] < cluster.minimumArea)
                    {
                        cluster.minimumArea = cluster.shards[i].nArea[j];
                    }
                }
            }

            // Fix
            if (cluster.minimumArea < 0.001f)
            {
                cluster.minimumArea = 0f;
            }

            cluster.areaCollapse = cluster.minimumArea;
            cluster.sizeCollapse = cluster.minimumSize;
        }
Exemplo n.º 20
0
        static void DrawGizmosSelected(RayfireConnectivity targ, GizmoType gizmoType)
        {
            // Connections
            //if (targ.enabled == true)
            {
                if (RFCluster.IntegrityCheck(targ.cluster) == false)
                {
                    Debug.Log("RayFire Connectivity: " + targ.name + " has missing shards. Reset or Setup cluster.", targ.gameObject);
                }

                ClusterDraw(targ);
                GizmoDraw(targ);
            }
        }
Exemplo n.º 21
0
        // CLuster connection and nodes viewport preview
        static void ClusterDraw(RayfireRigid targ)
        {
            if (targ.objectType == ObjectType.ConnectedCluster)
            {
                if (targ.clusterDemolition.cluster != null && targ.clusterDemolition.cluster.shards.Count > 0)
                {
                    // Reinit connections
                    if (targ.clusterDemolition.cluster.initialized == false)
                    {
                        RFCluster.InitCluster(targ, targ.clusterDemolition.cluster);
                    }

                    // Draw
                    for (int i = 0; i < targ.clusterDemolition.cluster.shards.Count; i++)
                    {
                        if (targ.clusterDemolition.cluster.shards[i].uny == false)
                        {
                            if (targ.clusterDemolition.cluster.shards[i].nIds.Count > 0)
                            {
                                Gizmos.color = Color.blue;
                            }
                            else
                            {
                                Gizmos.color = Color.gray;
                            }
                        }
                        else
                        {
                            Gizmos.color = Color.red;
                        }

                        if (targ.clusterDemolition.nd == true)
                        {
                            Gizmos.DrawWireSphere(targ.clusterDemolition.cluster.shards[i].tm.position, targ.clusterDemolition.cluster.shards[i].sz / 12f);
                        }
                        if (targ.clusterDemolition.cn == true)
                        {
                            if (targ.clusterDemolition.cluster.shards[i].neibShards != null)
                            {
                                for (int j = 0; j < targ.clusterDemolition.cluster.shards[i].neibShards.Count; j++)
                                {
                                    Gizmos.DrawLine(targ.clusterDemolition.cluster.shards[i].tm.position, targ.clusterDemolition.cluster.shards[i].neibShards[j].tm.position);
                                }
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 22
0
        /// /////////////////////////////////////////////////////////
        /// Cluster Colliders
        /// /////////////////////////////////////////////////////////

        // Set cluster colliders by shards
        public static void CollectClusterColliders(RayfireRigid scr, RFCluster cluster)
        {
            // Reset original cluster colliders list
            if (scr.physics.clusterColliders == null)
            {
                scr.physics.clusterColliders = new List <Collider>();
            }
            else
            {
                scr.physics.clusterColliders.Clear();
            }

            // Collect all shards colliders
            CollectDeepColliders(scr, cluster);
        }
Exemplo n.º 23
0
        // Set up main cluster and set shards
        RFCluster SetupMainCluster(ConnectivityType connect)
        {
            // Create Base cluster
            RFCluster cluster = RFCluster.SetCluster(transform, connect);

            clusterId = 0;

            // Collect all shards
            allShards.Clear();
            allShards.AddRange(cluster.shards);

            // TODO set bound

            return(cluster);
        }
Exemplo n.º 24
0
        // Save cluster/shards tm
        static void RestoreClusterTmRecursive(RFCluster cluster)
        {
            // Save cluster tm
            cluster.tm.rotation = cluster.rot;
            cluster.tm.position = cluster.pos;

            // Repeat for child clusters
            if (cluster.HasChildClusters == true)
            {
                for (int i = 0; i < cluster.childClusters.Count; i++)
                {
                    RestoreClusterTmRecursive(cluster.childClusters[i]);
                }
            }
        }
Exemplo n.º 25
0
 // Set bound and size
 public static void SetBound(RayfireRigid scr)
 {
     if (scr.objectType == ObjectType.Mesh)
     {
         scr.limitations.bound = scr.meshRenderer.bounds;
     }
     else if (scr.objectType == ObjectType.SkinnedMesh)
     {
         scr.limitations.bound = scr.skinnedMeshRend.bounds;
     }
     else if (scr.objectType == ObjectType.NestedCluster || scr.objectType == ObjectType.ConnectedCluster)
     {
         scr.limitations.bound = RFCluster.GetChildrenBound(scr.transForm);
     }
     scr.limitations.bboxSize = scr.limitations.bound.size.magnitude;
 }
Exemplo n.º 26
0
        // Set cluster
        void SetCluster(List <Transform> tmList)
        {
            // In case of runtime add
            if (cluster == null)
            {
                cluster = new RFCluster();
            }

            // Main cluster cached, reinit non serialized vars
            if (cluster.shards.Count > 0)
            {
                InitShards(rigidList, cluster);
            }

            // Create main cluster
            if (cluster.shards.Count == 0)
            {
                cluster              = new RFCluster();
                cluster.id           = RFCluster.GetUniqClusterId(cluster);
                cluster.tm           = transform;
                cluster.depth        = 0;
                cluster.pos          = transform.position;
                cluster.initialized  = true;
                cluster.demolishable = demolishable;

                // Set shards for main cluster
                if (Application.isPlaying == true)
                {
                    SetShardsByRigids(cluster, rigidList, type);
                }
                else
                {
                    RFShard.SetShardsByTransforms(cluster, tmList, type);
                }

                // Set shard neibs
                RFShard.SetShardNeibs(cluster.shards, type, minimumArea, minimumSize, percentage, seed);

                // Set range for area and size
                RFCollapse.SetRangeData(cluster, percentage, seed);

                // Debug.Log ("SetCluster" + rigidList.Count);
            }
        }
Exemplo n.º 27
0
        // Prepare shards. Set bounds, set neibs
        public static void SetShardsByTransforms(RFCluster cluster, List <Transform> tmList, ConnectivityType connectivity, bool setRigid = false)
        {
            cluster.shards = new List <RFShard>();
            for (int i = 0; i < tmList.Count; i++)
            {
                // Get mesh filter
                MeshFilter mf = tmList[i].GetComponent <MeshFilter>();

                // Child has no mesh
                if (mf == null)
                {
                    continue;
                }

                // Has no mesh
                if (mf.sharedMesh == null)
                {
                    continue;
                }

                // Create new shard
                RFShard shard = new RFShard(tmList[i], i);
                shard.cluster = cluster;

                // Set faces data for connectivity
                if (connectivity == ConnectivityType.ByMesh || connectivity == ConnectivityType.ByBoundingBoxAndMesh)
                {
                    RFTriangle.SetTriangles(shard, mf);
                }

                // Collect shard
                cluster.shards.Add(shard);
            }

            // Set rigid component
            if (setRigid == true)
            {
                for (int i = 0; i < cluster.shards.Count; i++)
                {
                    cluster.shards[i].rigid = cluster.shards[i].tm.GetComponent <RayfireRigid>();
                }
            }
        }
Exemplo n.º 28
0
        // Check children for mesh or cluster root until all children will not be checked
        static void CollectDeepColliders(RayfireRigid scr, RFCluster cluster)
        {
            // Collect shards colliders
            for (int i = 0; i < cluster.shards.Count; i++)
            {
                scr.physics.clusterColliders.Add(cluster.shards[i].col);
            }

            // Set child cluster colliders
            if (scr.objectType == ObjectType.NestedCluster)
            {
                if (cluster.HasChildClusters == true)
                {
                    for (int i = 0; i < cluster.childClusters.Count; i++)
                    {
                        CollectDeepColliders(scr, cluster.childClusters[i]);
                    }
                }
            }
        }
Exemplo n.º 29
0
        /// /////////////////////////////////////////////////////////
        /// 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.objectType == ObjectType.NestedCluster)
            {
                if (cluster.HasChildClusters == true)
                {
                    for (int i = 0; i < cluster.childClusters.Count; i++)
                    {
                        ResetDeepShardRigid(scr, cluster.childClusters[i]);
                    }
                }
            }
        }
Exemplo n.º 30
0
        // Remove neibs by area
        static int RemNeibRandom(RFCluster cluster, int percent, int seed)
        {
            int removed = 0;

            cluster.randomSeed = seed;
            for (int s = 0; s < cluster.shards.Count; s++)
            {
                // Skip unyielding
                if (cluster.shards[s].uny == true)
                {
                    continue;
                }

                for (int n = cluster.shards[s].neibShards.Count - 1; n >= 0; n--)
                {
                    // Set random state for same pair
                    Random.InitState(cluster.shards[s].id + cluster.shards[s].neibShards[n].id + seed);
                    if (Random.Range(0, 100) < percent)
                    {
                        // Remove self in neib's neib list
                        for (int i = cluster.shards[s].neibShards[n].neibShards.Count - 1; i >= 0; i--)
                        {
                            if (cluster.shards[s].neibShards[n].neibShards[i] == cluster.shards[s])
                            {
                                cluster.shards[s].neibShards[n].nIds.RemoveAt(i);
                                cluster.shards[s].neibShards[n].nArea.RemoveAt(i);
                                cluster.shards[s].neibShards[n].neibShards.RemoveAt(i);
                                break;
                            }
                        }

                        // Remove in self
                        cluster.shards[s].nIds.RemoveAt(n);
                        cluster.shards[s].nArea.RemoveAt(n);
                        cluster.shards[s].neibShards.RemoveAt(n);
                        removed++;
                    }
                }
            }
            return(removed);
        }