Master Spatial Validation & Dynamic Spawning
Tutorial intermediate

Master Spatial Validation & Dynamic Spawning

Updated intermediate

What you'll learn

In this guide, you will master the art of Spatial Point Validation. You'll learn how to query the SceneGraph for existing items, calculate safe spawn points using vector math, and dynamically instantiate new props only when strict spatial rules are met. We'll also wire up a live Player UI to give creators immediate feedback on whether a spawn attempt succeeded or was blocked.

How it works

Dynamic spawning isn't just about calling SpawnProp; it's about ensuring the result fits your design intent. The workflow follows three critical steps:

  1. Query: Retrieve the current state of the world by iterating through GetPlayspace().GetEntities() and extracting positional data via GetGlobalTransform().
  2. Validate: Compare your candidate spawn coordinate against existing items. We use coordinate delta checks to ensure no object violates your MinSpacing threshold.
  3. Execute & Feedback: If the point is clear, call SpawnProp inside a <transacts> context. Simultaneously, update the Player UI (via GetPlayerUI[] and AddWidget) so the player knows exactly what happened.

Let's build it

This device creates a "Loot Validator" button on the player's screen. Clicking it attempts to spawn a prop 100 units in front of the player. The script checks all existing entities; if any are too close, the spawn is denied and the console logs why. Otherwise, the prop appears and the counter increments.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/Temporary/SpatialMath }

spatial_loot_validator := class<concrete>(creative_device):

    @editable LootAsset : creative_prop_asset = creative_prop_asset{}
    @editable MinSpacing : float = 200.0

    SpawnedCount : int = 0

    OnBegin<override>()<suspends>: void =
        # Initialize Player UI once when the device begins
        if (FirstPlayer : GetPlayspace().GetPlayers()[0]):
            if (PlayerUI : FirstPlayer.GetPlayerUI[]):
                # Construct a real widget instance
                SpawnButton := button_loud_device{}
                
                # Add the widget to the player's canvas
                PlayerUI.AddWidget(SpawnButton)
                
                # Wire up the interaction to our validation logic
                _ := SpawnButton.InteractedWithEvent.Subscribe(
                    _(:void)<suspends>: void =>
                        AttemptValidationAndSpawn[PlayerUI]
                )

    AttemptValidationAndSpawn<transacts>(TargetUI:player_ui)<suspends>: void =
        # 1. Query: Get player position to calculate candidate spawn point
        if (Character : GetPlayspace().GetPlayers()[0].GetFortCharacter[]):
            PlayerPos := Character.GetGlobalTransform().Translation
            
            # Define candidate point (100 units forward from player)
            CandidatePos := vector3{
                Left := PlayerPos.Left + 100.0,
                Up := PlayerPos.Up,
                Forward := PlayerPos.Forward
            }
            
            # 2. Validate: Check against all existing entities in the playspace
            IsClear := true
            for (Entity : GetPlayspace().GetEntities()):
                # Only check entities that implement the positional interface
                if (PosInterface : Entity.GetComponent[positional][]):
                    ExistingPos := PosInterface.GetGlobalTransform().Translation
                    
                    # Perform coordinate delta validation
                    LowerBound := CandidatePos.Left - MinSpacing
                    UpperBound := CandidatePos.Left + MinSpacing
                    
                    # Check if existing item overlaps our exclusion zone
                    if (ExistingPos.Left >= LowerBound && ExistingPos.Left <= UpperBound &&
                        ExistingPos.Up >= CandidatePos.Up - MinSpacing && ExistingPos.Up <= CandidatePos.Up + MinSpacing &&
                        ExistingPos.Forward >= CandidatePos.Forward - MinSpacing && ExistingPos.Forward <= CandidatePos.Forward + MinSpacing):
                        IsClear := false
                        break
            
            # 3. Execute: Spawn only if validation passed
            if (IsClear):
                Result := SpawnProp[LootAsset, (/UnrealEngine.com/Temporary/SpatialMath:)FromVector3(CandidatePos)]
                if (SpawnedProp : Result(0)):
                    SpawnedCount += 1
                    Print("Success! Spawned count: {SpawnedCount}")
                else:
                    Print("SpawnProp returned failure.")
            else:
                Print("Blocked: Candidate point too close to existing geometry.")

Try it yourself

  1. Adjust Spacing: Change MinSpacing to 500.0 and observe how the validator becomes much stricter.
  2. Visualize Zones: Create a trigger_volume_device matching your MinSpacing and visualize its bounds in-game to see the exclusion area.
  3. Multiple Players: Modify the OnBegin loop to attach the UI button to every player in GetPlayspace().GetPlayers() instead of just index [0].

Recap

You've successfully implemented a robust spatial validation system. By querying the SceneGraph, performing precise coordinate checks, and handling the SpawnProp result safely, you've automated balanced loot distribution. Remember: always validate your coordinates before mutating state to keep your island performant and bug-free.

Check your understanding

Test yourself with an interactive quiz and track your progress + earn XP — free for members.

Turn this into a guided course

Add Spatial Point Validation and Dynamic SceneGraph Spawning to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.

Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.

Comments

    Sign in to vote, comment, or suggest an edit. Sign in