I’m trying to spawn a series of game objects, which I call obstacles, on Terrain surface when starting the game. The terrain has some elevations and its settings are the terrain’s default:
Set Height in Paint Terrain section is default 0
Pixel Error: 1
Terrain Width/Length: 1000
Terrain Height: 600
Detail Resolution Per Patch: 32
Detail Resolution: 1024
.
Suppose I spawn 10-20 objects. From this amount, some of them are spawned correctly on the terrain’s surface(where surface is flat or little rough) which is ok. but Where the ground is slightly sloping, objects are spawned above the height of the terrain, or below(inside of) the terrain’s surface. And my problem is exactly here that I want all the objects to spawn on the terrain’s surface more precisely, whether the point to be spawned is a mountain or a valley.
my script:
public class LevelBasedObjectSpawner : MonoBehaviour
{
GameplayManager _gameplayManager;
PathPointsSpawner _pathPointsSpawner;
[SerializeField] List<LevelConfig> levelConfigs;
int _levelIndex;
[SerializeField] Transform _parent_PhysicalDangerous;
void On_SpawnPathPoints_Completed()
{
LevelConfig config = levelConfigs[_levelIndex];
SpawnObjects(config.dangerousPhysicalObjects, _parent_PhysicalDangerous);
}
Vector3 GetRandomPositionBetweenPoints(Vector3 start, Vector3 end)
{
// Find a random point along the line between start and end
float t = Random.Range(0f, 1f); // Random value between 0 and 1
Vector3 randomPoint = Vector3.Lerp(start, end, t);
// Adjust height based on terrain (using SampleHeight solution)
float terrainHeight = _gameplayManager.terrain.SampleHeight(randomPoint);
randomPoint.y = terrainHeight;
return randomPoint;
}
void SpawnObjects(List<ObjectSpawnConfig> objectConfigs, Transform parent)
{
var spawnedPoints = _pathPointsSpawner._spawnedPoints;
for (int i = 0; i < spawnedPoints.Count - 1; i++)
{
Transform start = spawnedPoints[i];
Transform end = spawnedPoints[i + 1];
Vector3 segmentDirection = (end.position - start.position).normalized;
Vector3 perpendicularDirection = Vector3.Cross(segmentDirection, Vector3.up).normalized; // Perpendicular vector for "left-right" offset
foreach (var objConfig in objectConfigs)
{
int _spawnCount = Random.Range(0, objConfig.spawnNumber);
for (int j = 0; j < _spawnCount; j++)
{
Vector3 spawnPos = GetRandomPositionBetweenPoints(start.position, end.position);
// Apply random offset in the perpendicular "left-right" direction
float lateralOffset = Random.Range(-objConfig.horizontalOffset, objConfig.horizontalOffset);
spawnPos += perpendicularDirection * lateralOffset + new Vector3(0, objConfig.yOffset, 0); // Adding yOffset
Instantiate(objConfig.prefab, spawnPos, Quaternion.identity, parent);
}
}
}
}
}
No matter how much I tried and changed my code, I could not get the result.
.
I also used another method to check from above to bottom with ray casting to calculate the surface of terrain, but the result was the same and again the objects are below or above the height of the terrain.
code:
Vector3 GetRandomPositionBetweenPoints(Vector3 start, Vector3 end)
{
// Find a random point along the line between start and end
float t = Random.Range(0f, 1f);
Vector3 randomPoint = Vector3.Lerp(start, end, t);
// Cast a ray downward to find the terrain surface
RaycastHit hit;
Vector3 rayOrigin = randomPoint + Vector3.up * 100f; // Starting the ray above the random point
if (Physics.Raycast(rayOrigin, Vector3.down, out hit, Mathf.Infinity, terrainLayerMask))
{
randomPoint.y = hit.point.y; // Set the y-coordinate to the hit point's y position
}
else
{
// If no terrain hit, keep the original y value or set a default height if necessary
randomPoint.y = _gameplayManager.terrain.SampleHeight(randomPoint);
}
return randomPoint;
}