Random Prop Spawner
Random Prop Spawner
A creative device that periodically spawns a random prop from a list at random spawn points, on a randomized cooldown, optionally avoiding back-to-back repeats.
Original, compile-verified Verse by Bizanator (Biloxi Studios). Clean-room sample you can drop into a UEFN project and adapt.
# Random Prop Spawner — Biloxi Studios Inc.
#
# A self-contained creative device that periodically spawns a RANDOM prop from a
# configurable list at one of several spawn-point props, on a randomized cooldown.
# Optionally avoids playing the exact same prop twice in a row.
#
# Teaches: @editable arrays, GetRandomInt/GetRandomFloat, a self-rescheduling
# <suspends> loop, safe array indexing in a failure context, and SpawnProp.
#
# Drop this on a creative_device, fill in PropPalette with creative_prop_asset
# references and SpawnPoints with placed props that mark where to spawn.
using { /Fortnite.com/Devices }
using { /Verse.org/Random }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
log_random_prop_spawner := class(log_channel){}
random_prop_spawner := class<concrete>(creative_device):
Logger : log = log{Channel := log_random_prop_spawner}
# The pool of prop assets we can spawn. Add as many as you like.
@editable
PropPalette : []creative_prop_asset = array{}
# Placed props that mark WHERE we may spawn. We read their transforms and
# spawn at a randomly chosen one each time.
@editable
SpawnPoints : []creative_prop = array{}
# Random wait (seconds) between spawns.
@editable
CooldownMin : float = 5.0
@editable
CooldownMax : float = 12.0
# When true (and the palette has >1 entry), never spawn the same asset twice
# in a row, so the sequence feels less repetitive.
@editable
AvoidImmediateRepeat : logic = true
# Index of the prop we spawned last, so AvoidImmediateRepeat can skip it.
var LastSpawnedIndex : int = -1
OnBegin<override>()<suspends> : void =
if (PropPalette.Length <= 0):
Logger.Print("PropPalette is empty — nothing to spawn.", ?Level := log_level.Warning)
return
if (SpawnPoints.Length <= 0):
Logger.Print("SpawnPoints is empty — nowhere to spawn.", ?Level := log_level.Warning)
return
SpawnLoop()
# Waits a random cooldown, spawns one random prop, then repeats forever.
SpawnLoop<private>()<suspends> : void =
loop:
Wait := GetRandomFloat(CooldownMin, CooldownMax)
Sleep(Wait)
SpawnOneRandomProp()
SpawnOneRandomProp<private>() : void =
AssetIndex := PickAssetIndex()
# Choose a random spawn point. GetRandomInt is inclusive on both ends.
PointIndex := GetRandomInt(0, SpawnPoints.Length - 1)
if:
Asset := PropPalette[AssetIndex]
Point := SpawnPoints[PointIndex]
then:
Transform := Point.GetTransform()
Result := SpawnProp(Asset, Transform.Translation, Transform.Rotation)
if (Spawned := Result(0)?):
set LastSpawnedIndex = AssetIndex
Logger.Print("Spawned prop {AssetIndex} at point {PointIndex}.")
else:
Logger.Print("SpawnProp failed for asset {AssetIndex}.", ?Level := log_level.Warning)
# Pick the palette index to spawn. When AvoidImmediateRepeat is on and we
# have a choice, re-roll until we land on a different index than last time.
PickAssetIndex<private>() : int =
if (PropPalette.Length <= 1 or not AvoidImmediateRepeat?):
return GetRandomInt(0, PropPalette.Length - 1)
# Add a random 1..N-1 offset to the last index and wrap. This guarantees
# a different index without an unbounded re-roll loop.
Offset := GetRandomInt(1, PropPalette.Length - 1)
Candidate := LastSpawnedIndex + Offset
if (Wrapped := Mod[Candidate, PropPalette.Length]):
return Wrapped
return GetRandomInt(0, PropPalette.Length - 1)