- Home /
Instantiate a Prefab along the surface of a Mesh
var prefab : GameObject;
function spawn() { var position = Random.onUnitSphere*2.5000; var spawnPreferences = Instantiate(prefab, position, Quaternion.identity); spawnPreferences.transform.eulerAngles.y = Random.Range(0, 360); }
I've managed to create a simple script to instantiate a prefab along the surface of a sphere, but i'm trying to use the same principle but create the instances along the surface of a mesh.
Any suggestions on how to do this. Sadly onUnitMesh doesn't exist!
Cheers - C
again loving the diagram. Very helpful to explain what you want. I believe as a last resort you can put several spheres together to make the mesh on the left then restrict instantiation along the bits of each sphere. This is quite time consu$$anonymous$$g and fiddley as you need to make sure you have the random range set right for each seperate sphere so that you don't instantiate things inside the mesh. If you cant find an easier method this one atleast works with the script you already have (aslong as you configure the random.range)
Thanks @Bobadebob that sounds like a nice quick fix solution, which I'm going to use for now. Although I am looking for a solution for the long-run, planning of using a variety of different shaped meshes. Once again cheers.
Answer by Bunny83 · Mar 14, 2011 at 02:36 PM
The easiest way to get a random point on the surface of an irregular mesh in Unity would be to cast a random ray against the mesh. Every collider have a dedicated Raycast function to test only against this collider.
Both functions are not tested yet
function GetPointOnMesh() : RaycastHit{
var length : float = 100.0;
var direction : Vector3 = Random.onUnitSphere;
var ray : Ray = Ray(transform.position + direction*length,-direction);
var hit : RaycastHit;
collider.Raycast (ray, hit, length*2);
return hit;
}
The function returns a RaycastHit with all information on the point.
Another way would be to calculate a random triangle index and random barycentric coordinates and calculate the world position from this information.
function GetPointOnMesh() : RaycastHit{ var hit : RaycastHit; var mesh : Mesh = GetComponent.<MeshFilter>().mesh; hit.triangleIndex = Random.Range(0,mesh.triangles.Length/3); var BC : Vector3; BC.x = Random.Range(0.0,1.0); BC.y = Random.Range(0.0,1.0-BC.x); BC.z = Random.Range(0.0,1.0-BC.x-BC.y); hit.barycentricCoordinate = BC;
var P1 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 0]];
var P2 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 1]];
var P3 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 2]];
hit.point = transform.TransformPoint(P1*BC.x + P2*BC.y + P3*BC.z);
// Interpolated vertex normal
var N1 : Vector3 = mesh.normals[mesh.triangles[hit.triangleIndex + 0]];
var N2 : Vector3 = mesh.normals[mesh.triangles[hit.triangleIndex + 1]];
var N3 : Vector3 = mesh.normals[mesh.triangles[hit.triangleIndex + 2]];
hit.normal = N1*BC.x + N2*BC.y + N3*BC.z;
return hit;
}
If you need the face normal instead of the interpolated vertex normal you have to calculate it.
[...] var P1 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 0]]; var P2 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 1]]; var P3 : Vector3 = mesh.vertices[mesh.triangles[hit.triangleIndex + 2]]; hit.point = P1*BC.x + P2*BC.y + P3*BC.z;
// Face normal
hit.normal = Vector3.Cross((P1-P2),(P3-P2)).normalized;
[...]
Both functions returns a RaycastHit structure. Keep in mind that the second method doesn't set all fields of hit
.
If your mesh is more or less convex but with very different triangle sizes, the better point distribution you will get with the first function.
If the mesh have almost equal sized triangles but a very custom non convex shape the second would be better.
The first one is like a spherical projection onto the mesh. The second will covers all mesh triangles, even when they are hidden be other parts of the mesh.
To use this function do something like that:
var prefab : GameObject;
function spawn() { var randomPoint = GetPointOnMesh(); var spawnPreferences = Instantiate(prefab, randomPoint.point, Quaternion.identity); spawnPreferences.transform.eulerAngles.y = Random.Range(0, 360); }
You can use hit.normal with Quaternion.LookRotation to align the spawned object to the surface if you want but the axis that will point away from the surface have to be z in this case.
Bunny83 thats an extremely informative answer, absolutely brilliant, I'm going to use the first method as I understand it better. Also the added information on using Quaternion.LookRotation is brilliant, I've been using a sperate LookAt script, not very practical. cheers +rep
It's a very good and common question (way better than a lots of questions just asking for help on their own specific problems). I'm glad I could help. ;)
" hit.triangleIndex = Random.Range(0,mesh.triangles.Length/3);" Returns: BCE0053: Property 'UnityEngine.RaycastHit.triangleIndex' is read only.
Hmm, well, i've not checked the members of RaycastHit. I thought because it's a struct it's not a problem to setup your own RaycastHit but unfortunately the public members are all properties and some of them are read only. I'm not sure if they changed that recently or if they always have been properties. Anyways it's just a struct to return some information. If there's no way around that just use your own, custom struct/class. I don't see any good reason for a pure data holding structure to have readonly members...
Yea thanks I found this to be a really good read as I am just simply instancing objects on a plane and restricting them to its surface. Thanks for the indepth answer!
Answer by ScienceFiction · Jan 12, 2014 at 08:25 PM
You could also instantiate on the vertices of the mesh itself, picking from them randomly.
Your answer
Follow this Question
Related Questions
Combining Meshes? 1 Answer
Changing game object based on variable state? 1 Answer
Find prefab from Instantiation 2 Answers
Reference a prefab to be instantiated in another scope 0 Answers
Attribute inheritance problem when using Instantiate 1 Answer