Prefab anchored to the player entity renders at a different world position with the same local transform (SceneGraph)

Hi All,

Environment

  • UEFN 6.0.0-57316517+++Fortnite+Release-42.00, Windows 11 (25H2)
  • Verse / SceneGraph
  • Vehicle: vehicle_spawner_sports_car_device (Whiplash sports car)

What I’m doing

I anchor a prefab entity to the player’s entity (not to the vehicle — the car is only a visual reference for where the prop should sit):

Prefab.SetOrigin(entity_origin{Entity := PlayerEntity})
Prefab.SetLocalTransform(LocalOffset)

I set LocalOffset once, while the player is seated in one car (Car0) — at that point the prefab lands exactly where I want it.

The problem

When the player exits and then gets into a different car of the same type (Car1/Car2), and I apply the exact same LocalOffset to the same player entity, the prefab visibly ends up in a different position in the second car.

Meanwhile, according to the Verse logs, everything is identical:

  • GetLocalTransform() is bit-for-bit the same in both cases,
  • the prefab’s pose relative to the player entity (world → player-local, computed back) is also bit-for-bit the same.

So the numbers say the prop is in exactly the same spot relative to the player — but my eyes say otherwise. It’s as if it matters which Whiplash sports car the character sits into — even though it’s exactly the same car (the same asset) loading into both spawners.

Question

  • Why can the same local transform, anchored to the same player entity, render at a different world position, when the logged local and player-relative transforms are identical?
  • Does the seated character’s entity have some property (e.g. the entity_origin reference point, or the character’s pivot/scale while seated) that differs per car / per entry, and that I need to account for separately?

Thanks,
Gábor

Hey @Stanicz how are you?

I’ve been testing this with two Whiplash spawners and I think I found what’s happening!

Your PlayerEntity is probably the player, not the character

Since agents are entities now, the player (the “agent”) and the character it controls are two different entities. The agent entity has no “transform_component”, so its global transform is the world origin. If you anchor the prefab to it, the prefab isn’t following the character, it’s pinned to a fixed point in the world. (I could only guess this as I couldn’t see your code haha)

That matches everything you describe. In Car0 it lands exactly where you set it. When you get into Car1, the prefab stays where Car0 was, because the agent never moved. And your logs match bit for bit because you’re measuring the prefab against the same entity it’s anchored to, so that value is always identical.

To fix it you only need to do the following:

Get “PlayerEntity” from the character instead: “Agent.GetFortCharacter” and then “GetEntity”. In my tests the seated character lands in exactly the same spot relative to the car every time, so the same “LocalOffset” works in every car.

This is the device I tested. It prints the agent entity position so you can confirm it’s at 0, 0, 0 on your side too:

using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/BasicShapes }
using { /UnrealEngine.com/Temporary/Diagnostics }

# Anchors a prefab to the player's character while they are in a car
prefab_anchor_device := class(creative_device):

    @editable
    Car0 : vehicle_spawner_sports_car_device = vehicle_spawner_sports_car_device{}

    @editable
    Car1 : vehicle_spawner_sports_car_device = vehicle_spawner_sports_car_device{}

    # Offset from the character, in centimeters
    @editable
    OffsetLeft : float = -35.7

    @editable
    OffsetUp : float = 178.0

    @editable
    OffsetForward : float = 5.0

    var MaybePrefab : ?entity = false

    OnBegin<override>()<suspends>:void =
        Car0.AgentEntersVehicleEvent.Subscribe(OnAgentEntersVehicle)
        Car1.AgentEntersVehicleEvent.Subscribe(OnAgentEntersVehicle)
        Car0.AgentExitsVehicleEvent.Subscribe(OnAgentExitsVehicle)
        Car1.AgentExitsVehicleEvent.Subscribe(OnAgentExitsVehicle)

    OnAgentEntersVehicle(Agent:agent):void =
        # The agent has no transform_component, so it sits at the world origin
        AgentPosition := Agent.GetGlobalTransform().Translation
        Print("Agent entity position: L={AgentPosition.Left} U={AgentPosition.Up} F={AgentPosition.Forward}")

        if:
            Character := Agent.GetFortCharacter[]
            # The character's entity is the one that follows the player
            PlayerEntity := Character.GetEntity[]
            SimEntity := Self.GetSimulationEntity[]
        then:
            LocalOffset := transform{Translation := vector3{Left := OffsetLeft, Up := OffsetUp, Forward := OffsetForward}}
            Prefab := MakePrefab(SimEntity)
            Prefab.SetOrigin(entity_origin{Entity := PlayerEntity})
            Prefab.SetLocalTransform(LocalOffset)
            set MaybePrefab = option{Prefab}
            Print("Prefab local: {ToString(Prefab.GetLocalTransform())}")

    OnAgentExitsVehicle(Agent:agent):void =
        if (Prefab := MaybePrefab?):
            Prefab.RemoveFromParent()
            set MaybePrefab = false

    # Stand-in for your prefab: an entity with a cube mesh
    MakePrefab(SimEntity:entity):entity =
        Prefab := entity{}
        Mesh := cube{Entity := Prefab}
        set Mesh.Collidable = false
        Prefab.AddComponents(array{Mesh})
        SimEntity.AddEntities(array{Prefab})
        Prefab

The cube is just a placeholder, replace “MakePrefab” with your own mesh. You can tweak “OffsetLeft”, “OffsetUp” and “OffsetForward” in the device details.

One more thing: the seat

The pose does change with the seat. The passenger sits about 75 cm to the side of the driver. So if you set the offset as driver and then get in as passenger, the prop will be off by that much. Also, switching seats inside the car doesn’t fire AgentEntersVehicleEvent, so if you need to handle that, check Vehicle.GetSeats() and IsDriverSeat[].

This is how it looks:

Hope this works as you wanted! Let me know if you need more help!