- Home /
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); } }
Well that is an awful lot of instantiation going on during a per-triangle iteration.
If you comment out the "AddForce" call, what happens to your profile?
The object that's supposed to explode is duplicated and both remain there.
i'm clueless on how to do this but what if i'd only instantiate half of the objects? would they split in bigger pieces and cause less of an issue?
Oh, I meant what happens to your profiler log? I asked to try and narrow down the bulkiest task you're asking the engine to do.
Can you expand that "Physics.Processing" node, so that we can see its specific contents?
If you could clarify your question a bit and explain what you actually want to accomplish so we don't have to trawl through the code then you may get better answers.
I think you are probably approaching this the wrong way, but it is hard to tell without a clear description and pictures. I THIN$$anonymous$$ you could probably get something good enough using particles ins$$anonymous$$d of entire game objects for every triangle. If you can't then maybe an object pool might be a better fit than instantiating and destroying tons of game objects.
actually, if you just want to try a dirty fix then try putting
yield return null;
on line 41. you could also let it run say x iterations and then yield.
a vid will work better than a pic in this case so i uploaded 1 to youtube: https://youtu.be/cjanf$$anonymous$$VDWgw
i tried adding yield return null; on line 41 and pieces of the mesh shoot out of the model.
What are you trying to do here?
Time.timeScale = 1.0f;
yield return new WaitForSeconds(0.8f);
Time.timeScale = 1.0f;
i don't understand it either, my friend found this script online.
Answer by aditya · Apr 04, 2017 at 05:51 AM
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
This is a clever idea. Also, you could increase the scale of each resulting gameobject by as much as you increase the iteration interval to make it look as about the same total amount of triangles.
It's += 3 because of the fact that your indices in that list are the governing indices for triangles.
Changing it to += 10 might give you very strange values, but a larger multiple of 3 could work.
Whilst this would probably speed things up, as TreyH says, the 3 is there specifically because it's stepping over the indices 3 at a time - 3 verts per triangle.
You could increment the index by some multiple of 3 - say 15. That'd basically mean you only took every 5th triangle.
10 would go pretty crazy though as it'll start getting confused about which indices belong to which triangles - the 2nd one you tried to build would be made up of the last vert of the 3rd triangle and the first 2 verts of the fourth one :)
Also, you could move those lines to the top (along with the other variable declarations):
Vector3[] newVerts = new Vector3[3];
Vector3[] newNormals = new Vector3[3];
Vector2[] newUvs = new Vector2[3];
This way, you will re-use those vectors, ins$$anonymous$$d of creating a new in each loop (as you will be re-populating them anyway).
Answer by hrgchris · Apr 05, 2017 at 08:04 AM
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 :)
-Chris
Hey man
Thanks for sharing those other approaches, we might look into that in the future and it's definitely good to know for future projects. I apreciate it!