Entities

Learn about entities and their configuration options

SnapNet’s entities are at the heart of gameplay. They are networked objects that automatically synchronize their state and tick during simulation.

A step-by-step example of creating and setting up a simple entity and its renderer can be found in the Quick Start.

SnapNet Entities

When using the Unreal Engine SDK, any actor subclass with a USnapNetEntityComponent is considered a SnapNet entity and has a number of options that can be configured:

Transform Sync Mode

Set the entity component’s transform sync mode to control what transform data, if any, should be synchronized:

None: Do not sync position, rotation, or any rigid body properties

Position Only: Sync only position

Rotation Only: Sync only rotation

Position and Rotation (default): Sync both position and rotation

Rigid Body: Sync both position and rotation as well as angular velocity, linear velocity, and whether the rigid body is sleeping

AGameStateEntity::AGameStateEntity( const FObjectInitializer& ObjectInitializer /* = FObjectInitializer::Get() */ )
{
    EntityComponent = CreateDefaultSubobject<USnapNetEntityComponent>( EntityComponentName );

    // This entity syncs state related to the current game mode.
    // It has no visual representation and thus doesn't need to sync its transform.
    EntityComponent->SetTransformSyncMode( ESnapNetTransformSyncMode::None );
}

Prediction At Spawn

Set which players will simulate/predict the entity by default when spawned:

Default: Entity will use the default specified in the project settings

None: The server will simulate the entity, all clients will interpolate it

Owner: The server and owning client will simulate the entity, all other clients will interpolate it

All (default): Entity will be simulated on the server and all clients

AExampleEntity::AExampleEntity( const FObjectInitializer& ObjectInitializer /* = FObjectInitializer::Get() */ )
{
    EntityComponent = CreateDefaultSubobject<USnapNetEntityComponent>( EntityComponentName );
    EntityComponent->SetPredictionAtSpawn( ESnapNetEntityPredictionAtSpawn::Owner );
}

Prediction Mask

Once spawned, the server can control which players predict which entities. For example, you may want to change who owns an entity and then configure it to be predicted only by this new owner.

void UExampleServerScript::ChangeEntityOwnerAndPrediction( USnapNetEntityComponent* EntityComponent, int32 NewOwnerPlayerIndex )
{
    // Set new owner
    EntityComponent->SetOwnerPlayerIndex( NewOwnerPlayerIndex );

    // Set entity interpolated for all players
    EntityComponent->SetPredictedForAllPlayers( false );

    // Then set entity predicted only for the new owner
    EntityComponent->SetPredictedForPlayer( NewOwnerPlayerIndex, true );
}

Prediction Error Reduction Rate

Set the entity component’s prediction error reduction rate to control the entity’s behavior on the client after a misprediction.

Consider a scenario in which you’re running forward but then an enemy hits you with a dart that immobilizes you. When playing under latency, you will hear about the dart hitting you late and therefore you will have moved further forward than the server says you should have. When the Prediction Error Reduction Rate is set to 0 (turned off), you will instantly snap backwards to the corrected position. However when the rate is set above zero (turned on), you will smoothly move backwards to the corrected position over multiple frames. The rate controls the duration of the correction.

The number of seconds it takes for the prediction error to reach halfway to zero will be 1 / PredictionErrorReductionRate. The default is 32.

AExampleEntity::AExampleEntity( const FObjectInitializer& ObjectInitializer /* = FObjectInitializer::Get() */ )
{
    EntityComponent = CreateDefaultSubobject<USnapNetEntityComponent>( EntityComponentName );

    // Set to 0 to disable
    EntityComponent->PredictionErrorReductionRate = 0.0f;
}

Use Prediction Error Reduction for Remote Owners

Set this flag to control whether prediction error reduction occurs when the entity’s owner is not a local player.

For remotely controlled entities, often smoothing via the Entity Renderer Component yields better results than using prediction error reduction. Because the two smoothing methods are incompatible, it is recommended to turn this off when using Position Smoothing on the corresponding entity renderer. The default is on.

AExampleEntity::AExampleEntity( const FObjectInitializer& ObjectInitializer /* = FObjectInitializer::Get() */ )
{
    EntityComponent = CreateDefaultSubobject<USnapNetEntityComponent>( EntityComponentName );
    EntityComponent->bUsePredictionErrorReductionForRemoteOwners = false;
}

Interpolation

Entity transforms are automatically interpolated when rendering frames between fixed simulation ticks. Sometimes, like when a character respawns, you want to prevent this interpolation so that the character instantly appears at the new location. This can be accomplished by calling MarkTeleported() on the entity component.

void UExampleServerScript::RespawnCharacter( USnapNetEntityComponent* EntityComponent, const FTransform& SpawnTransform )
{
    EntityComponent->GetOwner()->SetActorTransform( SpawnTransform );
    EntityComponent->MarkTeleported();
}

Send Mask

The server can control which players receive which entities. By default, when an entity spawns, it is configured to be sent to all players. However, the entity component exposes functions to control which players will receive the entity. This can be useful for both optimizing bandwidth and security. For example, you can create an entity that represents an objective marker or description and send it to only a subset of all players.

void UExampleServerScript::SendOnlyToPlayer( USnapNetEntityComponent* EntityComponent, int32 PlayerIndex )
{
    if ( PlayerIndex >= 0 )
    {
        // Send to no one
        EntityComponent->SendToNoPlayers();
        // Then enable sending only to the specified player
        EntityComponent->SendToPlayer( PlayerIndex, true );
    }
    else
    {
        // No valid player index specified, send to all players
        EntityComponent->SendToAllPlayers();
    }
}

SnapNet Entity Renderers

Any actor subclass with a USnapNetEntityRendererComponent can be assigned as an entity’s SnapNet Entity Renderer. These actors are created automatically in the main UWorld to represent the entity visually and receive updates once per frame after the client simulation is complete.

Use the USnapNetEntityRendererComponent to configure the actor’s smoothing rates and hook into its OnUpdateFromEntity delegate to update the actor from its simulated entity each frame.

Position and Rotation Smoothing Rate

Set the position and rotation smoothing rates if you want the renderer to smoothly approach the simulated entity’s true position and rotation over time. In effect, this causes the rendered object to lag slightly behind the entity’s true transform and smooth over sharp discontinuities. As a result, it would not be appropriate for something like a bouncing ball where you don’t want to lose the sharp discontinuities when it bounces. On the other hand, it can be useful for avoiding overshoot for player-controlled entities and hiding network corrections for entities that change velocity smoothly.

The number of seconds it takes for the position or rotation to reach halfway to the target will be 1 / Rate. Smoothing is off by default (set to 0) but rates of 24 or so are a good place to start experimenting. Note that smoothing is only applied if the entity is being simulated and is not owned by a local player.

AExampleRenderer::AExampleRenderer( const FObjectInitializer& ObjectInitializer /* = FObjectInitializer::Get() */ )
{
    EntityRendererComponent = CreateDefaultSubobject<USnapNetEntityRendererComponent>( EntityRendererComponentName );
    EntityRendererComponent->PositionSmoothingRate = 24.0f;
    EntityRendererComponent->RotationSmoothingRate = 24.0f;
}

Update From Entity Delegate

The main mechanism for updating your renderer from its corresponding entity is via the entity renderer component’s UpdateFromEntity delegate. The delegate will be executed each frame after the simulation is executed but before actors in the main world tick.

AExampleRenderer::AExampleRenderer( const FObjectInitializer& ObjectInitializer /* = FObjectInitializer::Get() */ )
{
    EntityRendererComponent = CreateDefaultSubobject<USnapNetEntityRendererComponent>( EntityRendererComponentName );
    EntityRendererComponent->OnUpdateFromEntity.AddUObject( this, &AExampleRenderer::OnUpdateFromEntity );
}

void AExampleRenderer::OnUpdateFromEntity( const USnapNetEntityComponent* EntityComponent, float DeltaSeconds, bool bTeleported )
{
    const AExampleEntity* ExampleEntity = CastChecked<AExampleEntity>( EntityComponent->GetOwner() );
    SetHealth( ExampleEntity->GetHealth() );
}