Create a simple player entity
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.
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.
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.
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.
Note
Overlap events won’t be dispatched as normal due to SnapNet’s rollback and resimulation functionality. Additionally, because overlaps are expensive to calculate and are updated every time an object is moved, they can become a performance bottleneck. Make sure overlap events are disabled for all components attached to entities.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.
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.
Note
An entity renderer component must be present on any actor used for visualizing a SnapNet entity. It handles automatically updating the actor’s transform based on the underlying simulation and provides hooks to update any additional visuals from the simulation at the appropriate point in the frame—after the simulation has run but before actors in the main world tick.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.
Compile and save the blueprint.
Link the entity with its renderer
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.
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.
Note
For more control over which entities are loaded and when, games can manually register entities using the corresponding member functions on the SnapNet Subsystem. Advanced usage should look to Unreal’s asset management for ways to dynamically enumerate and register their entity classes.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.