Create a simple player entity

Follow these steps to create a simple entity that can be controlled by the player.

In order to synchronize an Unreal actor using SnapNet, it must be an entity. This means that it must have a USnapNetEntityComponent and be registered with the SnapNet Subsystem. In this section we will create a new entity type that the player can control.

Set up input bindings

The first step to handling player input in SnapNet is declaring which bindings you want to synchronize.

If you navigate to Edit → Project Settings → Engine → Input you can see the pre-existing input axes that are mapped under Bindings → Axis Mappings. In this case, Unreal has pre-populated two axis bindings for us, MoveForward and MoveRight, which are bound to S/W and D/A keys, respectively. That’s great because that’s all we’ll need for this example. If you want to bind additional inputs like gamepad thumbsticks to those axes, this would be the place to do it.

Engine input settings screenshot

Synchronize input bindings

SnapNet can automatically sync any of the actions and axes mapped in the engine’s input settings above. Navigate to Edit → Project Settings → Plugins → SnapNet and expand the Common → Input section. Under Input Axes add two elements, MoveForward and MoveRight, making sure the names match those from the previous section.

SnapNet input settings screenshot

Create the entity class

Let’s create a simple entity class that moves based on player input.

#pragma once

#include "GameFramework/Actor.h"

#include "SimplePlayerEntity.generated.h"

class USnapNetEntityComponent;

UCLASS( abstract )
class ASimplePlayerEntity : public AActor
{
    GENERATED_BODY()

public:
    ASimplePlayerEntity( const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get() );

    virtual void Tick( float DeltaSeconds ) override;

    static const FName EntityComponentName;
    static const FName MoveForwardAxisName;
    static const FName MoveRightAxisName;

protected:
    UPROPERTY( Category = "Simple Player Entity", VisibleAnywhere, BlueprintReadOnly )
    USnapNetEntityComponent* EntityComponent;

    UPROPERTY( Category = "Simple Player Entity", EditDefaultsOnly, BlueprintReadOnly )
    float Speed;
};
#include "SimplePlayerEntity.h"

#include "SnapNetEntityComponent.h"
#include "SnapNetSimulation.h"

const FName ASimplePlayerEntity::EntityComponentName( "SnapNetEntityComponent" );
const FName ASimplePlayerEntity::MoveForwardAxisName( "MoveForward" );
const FName ASimplePlayerEntity::MoveRightAxisName( "MoveRight" );

ASimplePlayerEntity::ASimplePlayerEntity( const FObjectInitializer& ObjectInitializer /* = FObjectInitializer::Get() */ )
    : Speed( 400.0f )
{
    EntityComponent = CreateDefaultSubobject<USnapNetEntityComponent>( EntityComponentName );
    EntityComponent->SetRequiresOwnerInput( true );
    EntityComponent->SetTranformSyncMode( ESnapNetTransformSyncMode::PositionAndRotation );

    PrimaryActorTick.bCanEverTick = true;
}

void ASimplePlayerEntity::Tick( float DeltaSeconds )
{
    Super::Tick( DeltaSeconds );

    if ( USnapNetSimulation* Simulation = USnapNetSimulation::Get( this ) )
    {
        const float MoveForwardAxisValue = Simulation->GetInputAxis( EntityComponent->GetOwnerPlayerIndex(), MoveForwardAxisName );
        const float MoveRightAxisValue = Simulation->GetInputAxis( EntityComponent->GetOwnerPlayerIndex(), MoveRightAxisName );
        const FVector MovementDelta = ( FVector::ForwardVector * MoveForwardAxisValue + FVector::RightVector * MoveRightAxisValue ) * Speed * DeltaSeconds;

        AddActorWorldOffset( MovementDelta, true );
    }
}

With these new files added, regenerate the project files and compile.

Create the entity blueprint

The next step is to create a blueprint for the entity. Open the editor and use the Content Browser to create a new folder named Entities. With this folder open, click Add/Import → Blueprint Class and select SimplePlayerEntity as the parent class for the new blueprint.

Create SimplePlayerEntity blueprint screenshot

Name it BP_SimplePlayerEntity. Now double-click on this new blueprint to open it up and navigate to Add Component → Collision → Sphere Collision. Click and drag this new component over the DefaultSceneRoot that was created to make it the new root component. This component will provide spherical collision for when the entity moves around.

With the new sphere collision component selected, take a look at the Details panel. Because we will use the default sphere mesh to represent this entity visually, and that has a radius of 50 units, set the Sphere Radius parameter in the Shape category to 50. Under the Collision category, open the dropdown beside Collision Presets, and select Pawn. This will ensure the entity collides against the environment as intended. Finally, under that same Collision category, uncheck Generate Overlap Events.

SimplePlayerEntity blueprint screenshot

You’ll notice there are no visual components attached to the entity because it is used for simulation only. This separation allows the simulation actors to have fewer components, their own physics simulation, and to optimize their behavior for best performance. See the architecture overview for more information.

Compile and save the blueprint.

Create the entity renderer blueprint

In order to visually represent the player, an entity actor must have a corresponding renderer actor. We’ll create a simple blueprint-based actor for this purpose. Back in the Content Browser, navigate to the Entities folder, and create a new blueprint class by clicking Add/Import → Blueprint Class and selecting Actor as the parent class for the new blueprint.

Create SimplePlayerEntity renderer blueprint screenshot

Name it BP_SimplePlayerEntity_Renderer. Now double-click on this new blueprint to open it up and click Add Component → SnapNet → Snap Net Entity Renderer.

Now, add a sphere component to represent the visuals by clicking Add Component → Common → Sphere. Click and drag this new component over the DefaultSceneRoot that was created to make it the new root component.

Create SimplePlayerEntity renderer blueprint screenshot

Compile and save the blueprint.

In order for SnapNet to spawn the appropriate renderer for an entity, the entity must specify which class to use. To link the entity with its renderer, open BP_SimplePlayerEntity and select its entity component. In the Details panel, expand the SnapNet Entity category, open the Entity Renderer Class dropdown, and select BP_SimplePlayerEntity_Renderer.

SimplePlayerEntity blueprint screenshot

Now, whenever a BP_SimplePlayerEntity exists in the SnapNet simulation, a corresponding BP_SimplePlayerEntity_Renderer will be created and kept up to date in the main rendered world.

Compile and save the blueprint.

Register the entity with SnapNet

Any entity that you want to spawn during a SnapNet session must be registered with the SnapNet Subsystem prior to session start, and the list of registered entities must be identical between clients and servers in order to successfully connect. The easiest way to do this is to list the entity in SnapNet’s project settings. Go to Edit → Project Settings → Plugins → SnapNet → Common → Registration → Entity Classes. Click on the + sign to add an entry to the list and then, in the dropdown, select BP_SimplePlayerEntity.

Screenshot showing registration of BP_SimplePlayerEntity in the SnapNet project settings

Next steps

Now that you have a networked entity that can be controlled by a player, you need to add logic that spawns the entity for each player when they connect and destroys it when they disconnect. Head over to the next section to create a server script that does just that.