Drag to orbit · Press Esc to exit

SCATTER PICKUPS
ACROSS A THREE.JS
TERRAIN

By Shane Brumback · · Intermediate Three.js tutorial

To scatter pickups across a Three.js terrain fairly, build one spot-finder that raycasts down onto the ground, rejects water and tree trunks, and throws out any candidate too close to a pack of the same type. This tutorial pulls that pattern from Super Soldier Battle's real pickup module.

Raycast placement, rejection sampling with squared distances, a near-trees cover rule for rare items, fallback tiers so the map is never empty, and a guarded loop that tops up ammo from banked packs — simplified so you can drop it into your own game.

Top-down tactical map showing health, ammo and shield pickups spread across a Three.js terrain with spacing rings

SEE IT LIVE

The scattered markers drifting behind this page are a live run of this exact spot-finder: random points raycast onto a terrain, rejected when they clump, with the rare amber markers kept near the trees. Or drop into Super Soldier Battle and hunt the real health, ammo and shield packs across the island. Both are free and run right in your browser, no download.

Play the Game

The markers behind this page are the real placement code: spacing-rejected, raycast onto the ground, shields near trees.

Introduction

Super Soldier Battle spreads health packs, ammo crates and shield packs across a big island. The first version just picked random spots, and random isn't the same as fair: packs could land on top of each other while whole areas had nothing. In this tutorial we'll build the placement code the game uses now, simplified from the real module.

It comes down to one spot-finder function with three optional rules, plus a few fallback passes:

  • Land on real ground. Raycast straight down onto the terrain, and reject water and tree trunks.
  • Keep same-type packs apart. Reject a candidate if it's within minDist of a pack of the same type.
  • Hide rare items in cover. For shields, only accept spots near a tree.
  • Never leave the map empty. If the rules are too strict, relax them step by step.

Then we'll add the ammo top-up loop that pulls banked packs from the inventory as the gun drains. Read the dev log for the gameplay side: Health, Ammo and Shield Pickups Now Spread Across the Island.

What you should know first: this is an intermediate build. The game runs on Three.js r128, and everything here works there; the runnable example and the live background demo use a current CDN build.

What You'll Learn

  • Finding terrain height at a random XZ point with a downward THREE.Raycaster
  • Rejection sampling with squared distances (no Math.sqrt)
  • Requiring a spot to be near something (trees) while also keeping it away from it (trunk colliders)
  • Fallback tiers so spawning never silently fails
  • A guarded while loop that consumes inventory items to keep a value topped up

Prerequisites

Comfortable with Three.js basics (scene, meshes, Raycaster) and plain JavaScript. The game runs on Three.js r128, and everything here works there. Names follow the game code, simplified. The runnable example and the live background demo use a current CDN build.

Frequently Asked Questions

How do I find the ground height at a point in Three.js?

Cast a THREE.Raycaster straight down from above the terrain and read hit.point.y from the first intersection. Super Soldier Battle casts from y = 1200 with direction (0, -1, 0) and rejects hits below -2 so packs never land in the water.

How do I keep random spawn points from clustering?

Use rejection sampling: pick a random point and throw it away if it's within a minimum distance of an existing item of the same type. Compare squared distances, and run that check before the expensive raycast.

What if my spacing rule is too strict to place everything?

Add fallback tiers. Super Soldier Battle tries with all rules first, then drops the near trees rule, then drops spacing entirely, so a crowded map still gets its packs.

How do I spawn items near trees but not inside them?

Loop over the tree trunk positions once per candidate. Reject the spot if it's inside a trunk's margin (3.5 units in the game), and only accept it if at least one trunk is within your cover radius (22 units).

How should automatic ammo refills work?

Refill in fixed amounts and consume a banked pack only when there's room for it. The game adds 250 per pack up to 1,000, and auto-pulls a pack whenever ammo drops to 750 or below, in a loop with a guard counter.

How It Works

  1. Pick a random point and raycast down — a random X and Z inside the playable area, then a ray from high above straight down gives the ground height.
  2. One spot-finder, three optional rules — minDist + avoid for spacing, nearTree for cover, and the trunk loop that rejects trees either way.
  3. Spawn with spacing, then relax — each type builds its avoid list from live packs, and the spawner falls back in tiers instead of giving up.
  4. Keep the gun topped up — each pack adds a fixed amount, and a guarded while loop pulls banked packs as the gun drains.

That's the whole system. The detailed code breakdown and the full copy-paste example are below — sign in free to unlock them.

How To Use It In Your Game

  1. Write one randomSpot(opts) that picks a random XZ, rejects by distance first, then raycasts down and rejects water and obstacles.
  2. Keep one list of live positions per pickup type and pass it as avoid.
  3. Give rare, powerful items a bigger minDist and a cover rule like nearTree.
  4. Wrap every spawn in fallback tiers that relax the rules, so the map never ends up empty.
  5. Make "use item" return true or false, and drive auto-use with a guarded loop.

SEE THESE PICKUPS IN A REAL GAME

This placement system ships in Super Soldier Battle's health, ammo and shield packs. Read the dev log on the pickups update to see the gameplay side of the same feature.

Health, ammo and shield pickups spread across the island in Super Soldier Battle Read the Pickups Dev Log