You don't really need the
Terrain.SampleHeight() function, since
hit.point already contains the information (i.e. the position on the Terrain) you're looking for.
public void SpawnThroughPlane()
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (plane.GetComponent<TerrainCollider>().Raycast(ray, out hit, Mathf.Infinity))
GameObject cube = Instantiate(prefabToSpawn);
cube.transform.position = hit.point;
TerrainCollider instead of
MeshCollider because I assumed you were working with Terrains, but the same criteria apply to Mesh colliders as well, provided some minor code edits.
The previous function places cubes on the terrain without applying an offset to
hit.point, and this results in cubes penetrating the Terrain after positioning:
You can solve this new problem in different ways, but the main idea is moving the cube towards a certain direction for a certain distance so that it touches the Terrain without intersecting it.
When a cube is placed, its bottom half is "inside" the Terrain but its top
half is actually visible. Then, we can move it upwards for half its height with respect to the
hit.point. To do so, just add an offset value when setting the
cube.transform.position = hit.point + 0.5f * Vector3.up;
0.5 to a vector to define a distance along a given direction in space. Rather than hard-coding values, you can also compute object heights programmatically by using
Mesh.bounds and then
Vector3.Scale() to account for scaling as well.
This small tweak improves the final result, although polygons still penetrate each other when slopes are steep:
You could even use
hit.normal in place of
Vector3.up and apply the offset along a direction perpendicular to the hit surface. Rotating the cubes will look nice as well (I don't know if this is what you're looking for though):
Instead of positioning the cubes and adjust their position, you may use a different approach:
- Raycast onto the Terrain, and find out the
hit.point of interest
- Assume a "safe" height from the terrain, e.g. 10 units, and consider the new position
Vector3 dropFrom = hit.point + 10f * Vector3.up;
- Use the
Physics.BoxCast function, with starting position
dropFrom and direction
Vector3.down (or a Ray pointing downwards).
This way, you are "dropping" a cube from atop of the Terrain and casting a geometric proxy downwards until a collision occurs. The cast results in a position a cube can be placed in without intersecting the Terrain: