Improve Script Performance Physics

My friend found this script and it works pretty well but there’s some framedrops so i wanted to ask if you guys have any ideas on how to improve it’s performance.

We’re using this script + OnTriggerEnter on our enemies so they explode when the player dashes through them. The enemy is shattered into many pieces(±900) and that causes performance issues, especially on the CPU with physics.processing, i think shattering in 100 pieces would be better but not sure how to make that work.

+Edit: I now set Time.timeScale to 0.1f at the start and back to 1 at the end and this seems to help a little but i’m still stuck at reducing the amount of triangles

IEnumerator SplitMesh()
    {
        MeshFilter MF = GetComponent<MeshFilter>();
        MeshRenderer MR = GetComponent<MeshRenderer>();
        Mesh M = MF.mesh;
        Vector3[] verts = M.vertices;
        Vector3[] normals = M.normals;
        Vector2[] uvs = M.uv;
        for (int submesh = 0; submesh < M.subMeshCount; submesh++)
        {
            int[] indices = M.GetTriangles(submesh);
            for (int i = 0; i < indices.Length; i += 3)
            {
                Vector3[] newVerts = new Vector3[3];
                Vector3[] newNormals = new Vector3[3];
                Vector2[] newUvs = new Vector2[3];
                for (int n = 0; n < 3; n++)
                {
                    int index = indices[i + n];
                    newVerts[n] = verts[index];
                    newUvs[n] = uvs[index];
                    newNormals[n] = normals[index];
                }
                Mesh mesh = new Mesh();
                mesh.vertices = newVerts;
                mesh.normals = newNormals;
                mesh.uv = newUvs;

                mesh.triangles = new int[] { 0, 1, 2, 2, 1, 0 };

                GameObject GO = new GameObject("Triangle " + (i / 3));
                GO.transform.position = transform.position;
                GO.transform.rotation = transform.rotation;
                //GO.AddComponent<MeshRenderer>().material = MR.materials[submesh];
                GO.AddComponent<MeshRenderer>().materials = MR.materials;
                GO.AddComponent<MeshFilter>().mesh = mesh;
                GO.AddComponent<BoxCollider>();
                GO.AddComponent<Rigidbody>().AddExplosionForce(100, transform.position, 30);
               

                Destroy(GO, 5 + Random.Range(0.0f, 5.0f));
            }
        }
        MR.enabled = false;

        Time.timeScale = 1.0f;
        yield return new WaitForSeconds(0.8f);
        Time.timeScale = 1.0f;
        Destroy(gameObject);
    }
    /*void OnMouseDown()
    {
        StartCoroutine(SplitMesh());
    }*/
}

Thanks in advance

  • Edit2: Here’s the full working script:

    using UnityEngine;
    using System.Collections;

    public class ShatterScript : MonoBehaviour{
    //Change this value to split into bigger pieces
    int size = 15;
    Vector3 newVerts = new Vector3[3];
    Vector3 newNormals = new Vector3[3];
    Vector2 newUvs = new Vector2[3];

     //Check for collision with player or enemy if this is an ally
     private void OnTriggerEnter(Collider other)
     {
         if (this.name == "Virus" && other.name == "Player")
         {
                 PlayerControl playerControl = other.GetComponent<PlayerControl>();
                 if (playerControl.boosting) StartCoroutine(SplitMesh());
         } else if(this.name == "Drone" && other.name == "Virus") {
             StartCoroutine(SplitMesh());
         }
     }
     IEnumerator SplitMesh()
     {
         MeshFilter MF = GetComponent<MeshFilter>();
         MeshRenderer MR = GetComponent<MeshRenderer>();
         Mesh M = MF.mesh;
         Vector3[] verts = M.vertices;
         Vector3[] normals = M.normals;
         Vector2[] uvs = M.uv;
         //Half timeScale to create a minor slow-mo effect
         Time.timeScale = 0.5f;
         for (int submesh = 0; submesh < M.subMeshCount; submesh++)
         {
             int[] indices = M.GetTriangles(submesh);
             for (int i = 0; i < indices.Length; i += size)
             {
                 for (int n = 0; n < 3; n++)
                 {
                     int index = indices[i + n];
                     newVerts[n] = verts[index];
                     newUvs[n] = uvs[index];
                     newNormals[n] = normals[index];
                 }
    
                 Mesh mesh = new Mesh();
                 mesh.vertices = newVerts;
                 mesh.normals = newNormals;
                 mesh.uv = newUvs;
    
                 mesh.triangles = new int[] { 0, 1, 2, 2, 1, 0 };
    
                 GameObject GO = new GameObject("Triangle " + (i / size));
                 GO.transform.position = transform.position;
                 GO.transform.rotation = transform.rotation;
                 //GO.AddComponent<MeshRenderer>().material = MR.materials[submesh];
                 GO.AddComponent<MeshRenderer>().materials = MR.materials;
                 GO.AddComponent<MeshFilter>().mesh = mesh;
                 GO.AddComponent<BoxCollider>();
                 GO.AddComponent<Rigidbody>().AddExplosionForce(100, transform.position, 75);
     //Comment to use gravity again
                 GO.GetComponent<Rigidbody>().useGravity = false;
                
    
                 Destroy(GO, 2 + Random.Range(0.0f, 3.0f));
             }
         }
         MR.enabled = false;
         yield return new WaitForSeconds(0.3f);
         Time.timeScale = 1.0f;
         Destroy(gameObject);
     }
    

    }

simply change the value of the second for loop like this for (int i = 0; i < indices.Length; i += 15) but this value should always be a MULTIPLE OF 3 (Read comments for WHY) … instead of 3 change it to something bigger, bigger value means less pieces … and remove those two Time.TimeScale lines from there as they are doing nothing but don’t remove waitForSeconds line

Important : You might also need to change the value of third for loop with the same value as of second for loop. For Eg, if you changed the value of second for loop by 15 then change the value of third for loop by 15 as well

Hey there

The info in the previous example suggests how to reduce the number of triangles in order to reduce the cost of the function, so I won’t go over that again! However I would like to suggest an alternate approach in case its useful.

This kind of thing is always going to be horrendously expensive, as you’re instantiating loads of stuff. You might be able to save on a bit of time by removing the box collider unless you really want these bits to bounce off things.

However a mesh explosion effect is typically achieved with a shader. On explosion, you generate a mesh that is similar to the input one, however for each triangle in the source you generate 3 unique vertices + 2 triangles (back/forward faces). Your code is already doing something similar to this, when it generates a mini mesh with these vertices/triangles in.

Once you have your ‘explosion mesh’ (which you can generate in advance to save time), you can then use a vertex shader to animate the vertices flying outwards from their triangle’s normal.

That all takes a bit of work, so I appreciate it might be out of the scope of this question, but if you come back to it again and want another go at making it super efficient, that’s the general approach to mesh explosion effects, as the heavy work can be done on load, and the effect is then performed extremely efficiently as part of the shader - you could detonate 100s of meshes at once at see it work fine.

As a middle ground, if you weren’t worried about it looking identical, you might even get good results just using the unity particle system, setting up a roughly triangle shaped particle and emitting one from the centre of each particle on your enemy. It won’t look the same, but might still look cool :slight_smile:

-Chris