Welcome back to South Shores! So far you have wired up devices that were already sitting on the sand. Today Coral teaches you the trick every treasure hunt needs: making props appear out of thin air. SpawnProp takes a creative_prop_asset (the recipe for a prop) plus a transform (the where), and stamps a live creative_prop onto the island at runtime. When the round ends, Dispose clears them off again. No spawner device required — your Verse code is the spawner.
What you will build
A shell conjurer device for the Shell Hunt capstone: each round it shuffles a set of spawn-point markers, spawns N collectible shell props at the first N markers, waits out the round, then disposes every shell so the next round starts on a clean beach. This is exactly the "runtime-spawning the N collectible shells at shuffled spawn points" piece the South Shores capstone minigame is built around.
You will learn to:
- Reference a prop asset with
@editable ShellAsset : creative_prop_asset = DefaultCreativePropAsset - Call
SpawnProp(Asset, Transform)and understand its return value:tuple(?creative_prop, spawn_prop_result) - Check spawn success by unwrapping element 0 with
if (Shell := SpawnResult(0)?) - Track spawned props in a
vararray andDispose()them at round end - Shuffle spawn points with
Shufflefrom/Verse.org/Random
Walkthrough
Step 1 — Set the stage in UEFN
- Drop a shell prop asset into your project (any small prop works — a conch from the Fortnite props gallery is perfect).
- Place a handful of small marker props along the beach where shells are allowed to appear (8-12 markers gives the shuffle room to breathe). You can hide the markers under the sand or make them invisible — we only read their transforms.
- Create a new Verse device with the code below, drag it into the level, then wire up the Details panel: the shell asset into ShellAsset, and every marker prop into the SpawnMarkers array.
Step 2 — The complete device
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Random }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
# Spawns a round of collectible shells at shuffled marker positions,
# then clears them away so the next round starts fresh.
shell_conjurer_device := class(creative_device):
# The shell prop to spawn. Drag your shell asset into this
# field in the UEFN Details panel.
@editable
ShellAsset : creative_prop_asset = DefaultCreativePropAsset
# Marker props placed on the beach. Each marker's transform
# is a possible spawn point for a shell.
@editable
SpawnMarkers : []creative_prop = array{}
# How many shells each round should conjure.
@editable
ShellsPerRound : int = 5
# How long a round lasts before the shells are cleared.
@editable
RoundSeconds : float = 20.0
# Every shell spawned this round, so we can Dispose them later.
var SpawnedShells : []creative_prop = array{}
OnBegin<override>()<suspends>:void =
loop:
SpawnRound()
Sleep(RoundSeconds)
CleanupRound()
Sleep(2.0) # a short beat between rounds
# Shuffle the markers and spawn a shell at the first ShellsPerRound.
SpawnRound():void =
Shuffled := Shuffle(SpawnMarkers)
for (Index := 0..ShellsPerRound - 1, Marker := Shuffled[Index]):
SpawnShellAt(Marker.GetTransform())
# Spawn one shell and record it if the spawn succeeded.
SpawnShellAt(SpawnTransform : transform):void =
# SpawnProp returns tuple(?creative_prop, spawn_prop_result).
SpawnResult := SpawnProp(ShellAsset, SpawnTransform)
if (Shell := SpawnResult(0)?):
# Element 0 held a real prop - the spawn worked.
set SpawnedShells += array{Shell}
Print("Shell conjured! {SpawnedShells.Length} on the beach.")
else:
# Element 0 was false - element 1 says why (see spawn_prop_result).
Print("A shell refused to appear. Check the spawn_prop_result.")
# Dispose every shell that is still on the island.
CleanupRound():void =
for (Shell : SpawnedShells, Shell.IsValid[]):
Shell.Dispose()
set SpawnedShells = array{}
Print("Beach swept clean for the next round.")
Step 3 — How the spawn call works, line by line
| Piece | What it does |
|---|---|
ShellAsset : creative_prop_asset = DefaultCreativePropAsset |
An @editable asset reference. DefaultCreativePropAsset is the safe placeholder default until you drag in a real asset. |
Shuffle(SpawnMarkers) |
From /Verse.org/Random — returns a new array with the markers in random order, so every round uses different spawn points. |
for (Index := 0..ShellsPerRound - 1, Marker := Shuffled[Index]) |
Iterates the first N shuffled markers. The failable index Shuffled[Index] quietly skips out-of-range values, so asking for 5 shells with only 3 markers spawns 3 — no crash. |
SpawnProp(ShellAsset, SpawnTransform) |
The star of the show. Returns a tuple: element 0 is ?creative_prop (the spawned prop, or false on failure), element 1 is a spawn_prop_result enum explaining the outcome. |
if (Shell := SpawnResult(0)?) |
The success check: unwrapping the option succeeds only when a prop was actually created. Inside the if, Shell is a real creative_prop. |
Shell.IsValid[] |
Succeeds only if the prop has not already been disposed (a collected shell might already be gone). |
Shell.Dispose() |
Destroys the prop and removes it from the island. |
Why the result enum matters
spawn_prop_result tells you why a spawn failed: Ok, UnknownError, InvalidSpawnPoint (NaN/Inf coordinates), SpawnPointOutOfBounds (outside the island), InvalidAsset, or TooManyProps. That last one is the big one — the island rules currently allow 100 spawned props per script device and 200 total per island. A round-based cleanup with Dispose, like ours, is how you stay comfortably under that budget.
Common patterns
Pattern 1 — Scatter spawning with the Position + Rotation overload
SpawnProp has a second overload that takes a vector3 position and a rotation instead of a whole transform. Handy when you want random scatter around a point instead of exact marker positions:
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Random }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
scatter_spawner_device := class(creative_device):
@editable
ShellAsset : creative_prop_asset = DefaultCreativePropAsset
var SpawnedShells : []creative_prop = array{}
OnBegin<override>()<suspends>:void =
# Scatter 3 shells around this device's own position.
BasePosition := GetTransform().Translation
for (Count := 0..2):
SpawnScattered(BasePosition)
# Spawn a shell near a base position with random offset,
# using the Position + Rotation overload of SpawnProp.
SpawnScattered(BasePosition : vector3):void =
Offset := vector3{
X := GetRandomFloat(-150.0, 150.0),
Y := GetRandomFloat(-150.0, 150.0),
Z := 0.0
}
SpawnResult := SpawnProp(ShellAsset, BasePosition + Offset, IdentityRotation())
if (Shell := SpawnResult(0)?):
set SpawnedShells += array{Shell}
Pattern 2 — Reading the result enum for diagnostics
When spawns fail mysteriously during testing, compare element 1 against the spawn_prop_result cases:
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
diagnostic_spawner_device := class(creative_device):
@editable
ShellAsset : creative_prop_asset = DefaultCreativePropAsset
OnBegin<override>()<suspends>:void =
SpawnWithDiagnostics(GetTransform())
SpawnWithDiagnostics(SpawnTransform : transform):void =
Result := SpawnProp(ShellAsset, SpawnTransform)
if (Result(1) = spawn_prop_result.Ok):
Print("Spawn OK.")
else if (Result(1) = spawn_prop_result.TooManyProps):
Print("Prop budget hit: 100 per script device, 200 per island.")
else:
Print("Spawn failed - check InvalidAsset / SpawnPointOutOfBounds.")
Where this goes next
This device is the beating heart of the Shell Hunt capstone at the end of South Shores. The capstone imports this exact spawning skill: each game round it calls the same shuffle-then-SpawnProp routine to place the collectible shells, listens for players collecting them, and reuses CleanupRound between rounds so the beach — and the island prop budget — stays clean. Nail the SpawnResult(0)? success check now, and the capstone's round loop will feel like a warm tide coming in.