Input

Learn how to synchronize and retrieve input using SnapNet

SnapNet can automatically use any input axis or action defined in your project settings. Because it is typical to have a number of input axes and actions that are only used for non-networked gameplay like navigating menus, SnapNet does not network all of your input axes and actions by default and those you want to synchronize must be explicitly specified.

Configuration

You can configure the inputs available to your SnapNet simulation by opening the editor, navigating to Edit → Project Settings → Plugins → SnapNet, and expanding the Common → Input section. The names of your input axes and actions must match those specified in Project Settings → Engine → Input.

Screenshot of SnapNet input project settings

You can also check Include Control Rotation to make the Control Rotation from each player’s local player controller available as an input to the simulation which is commonly used for first-person and follow cameras.

Accessing Player Input

Player input can be accessed directly from the SnapNet Simulation object. Typically, you will be retrieving input from within an entity’s tick function:

void AExampleEntity::Tick( float DeltaSeconds )
{
    if ( USnapNetSimulation* Simulation = EntityComponent->GetSimulation() )
    {
        const int32 PlayerIndex = EntityComponent->GetOwnerPlayerIndex();
        
        const float MoveForwardValue = Simulation->GetInputAxis( PlayerIndex, "MoveForward" );
        
        const bool bIsJumpHeld = Simulation->IsInputActionDown( PlayerIndex, "Jump" );

        const bool bWasJumpPressed = Simulation->WasInputActionPressed( PlayerIndex, "Jump" );

        const bool bWasJumpReleased = Simulation->WasInputActionReleased( PlayerIndex, "Jump" );
    }
}

Note that SnapNet automatically tracks both the current and previous inputs so that you can directly query whether an action was pressed or released without the need to track that state manually.

Enhanced Input

In addition to the legacy input system, SnapNet also supports the Enhanced Input plugin from Epic Games.

Configuring Enhanced Input

Enhanced input actions can be registered with SnapNet by navigating to Edit → Project Settings → Plugins → SnapNet, expanding the Common → Input section, and registering UInputAction assets under the Enhanced Input Actions property. Input Mapping Contexts are not networked; each client is responsible for applying Input Mapping Contexts to their own ULocalPlayers based on the state of the game. A single Input Mapping Context applied at game start is often sufficient.

Accessing Enhanced Input

Once registered, the values corresponding to Enhanced Input actions can be accessed directly from the SnapNet Simulation object:

void AExampleEntity::Tick( float DeltaSeconds )
{
    if ( USnapNetSimulation* Simulation = EntityComponent->GetSimulation() )
    {
        const int32 PlayerIndex = EntityComponent->GetOwnerPlayerIndex();
        
        const FVector2D MoveValue = Simulation->GetEnhancedInputActionValue<FVector2D>( PlayerIndex, MoveAction );
        
        const bool bIsJumpHeld = Simulation->GetEnhancedInputActionValue<bool>( PlayerIndex, JumpAction );

        const bool bWasJumpPressed = Simulation->WasEnhancedInputActionPressed( PlayerIndex, JumpAction );

        const bool bWasJumpReleased = Simulation->WasEnhancedInputActionReleased( PlayerIndex, JumpAction );
    }
}

Custom Input

For use-cases that don’t fit neatly into the legacy axes/actions or Enhanced Input paradigms, SnapNet supports completely custom input.

Configuring Custom Input

To implement custom input, first subclass USnapNetCustomInput. The following example traces the cursor into the world and then syncs that 3D world position as part of the player’s input.

#pragma once

#include "SnapNetCustomInput.h"
#include "SnapNetPropertyVector.h"

#include "MyCustomInput.generated.h"

UCLASS()
class UMyCustomInput : public USnapNetCustomInput
{
    GENERATED_BODY()

public:
    FORCEINLINE FVector GetCursorPosition() const { return CursorPosition.GetValue(); }

    virtual void Populate( class APlayerController* LocalPlayerController ) override;

protected:
    UPROPERTY()
    FSnapNetPropertyPosition CursorPosition;
};
#include "MyCustomInput.h"

#include "GameFramework/PlayerController.h"

void UMyCustomInput::Populate( APlayerController* LocalPlayerController )
{
    FHitResult HitResult;
    if ( LocalPlayerController->GetHitResultUnderCursorByChannel( UEngineTypes::ConvertToTraceType( ECC_Visibility ), true, HitResult ) )
    {
        CursorPosition.SetValue( HitResult.ImpactPoint );
    }
}

Then, configure UMyCustomInput as your custom input class in the editor via Edit → Project Settings → SnapNet → Common → Input → Custom Input Class.

Accessing Custom Input

Like other input, custom input can be retrieved directly from the simulation:

void AExampleEntity::Tick( float DeltaSeconds )
{
    if ( USnapNetSimulation* Simulation = EntityComponent->GetSimulation() )
    {
        const int32 PlayerIndex = EntityComponent->GetOwnerPlayerIndex();

        if ( const UMyCustomInput* MyCustomInput = Simulation->GetCustomInput<UMyCustomInput>( PlayerIndex ) )
        {
            const FVector CursorPosition = MyCustomInput->GetCursorPosition();
        }
    }
}