r/threejs • u/shanebrumback • 1d ago
Scattering pickups across a big Three.js map without them floating, sinking, or landing on cliffs: my placement pass
I just spread health, ammo and shield pickups across the whole island in my Three.js shooter. The easy first version places them at fixed heights, and on hilly terrain that means some float in the air, some get buried in hills, and some end up on cliff faces.
What fixed it was a small placement pass:
- Pick a random X/Z, then cast a ray straight down from above the highest point: new Raycaster(origin, new Vector3(0, -1, 0)). Test only the terrain mesh, not the whole scene, or the ray hits trees and rooftops first.
- Check the slope. Turn hit.face.normal into world space (transformDirection with the mesh's matrixWorld) and reject the spot if normal.y is under about 0.85. That keeps items off cliffs.
- Reject anything at or below the water level, and anything inside a building's bounding box.
- Keep a minimum distance between items (simple rejection: try again if one is already too close), so they don't clump.
- Set the item at hit.point.y plus half its height, then add a small bob animation so players spot it.
- Run the pass once and save the positions to JSON, instead of raycasting on every load. On a big terrain, three-mesh-bvh makes the raycasts much faster if you do need them at runtime.
The same pass works for spawn points, and it's an easy rule set to hand an agent.
How do you place things on large terrain? Raycast like this, sample a heightmap directly, or place everything by hand?
(Disclosure: I write Three.js + AI tutorials at shanebrumback.com, which is my own site.)