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:

#include "ExampleEntity.h"

#include "SnapNetSimulation.h"

void AExampleEntity::Tick( float DeltaSeconds )
{
    if ( USnapNetSimulation* Simulation = USnapNetSimulation::Get( this ) )
    {
        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.

Custom Input

For use-cases that don’t fit neatly into the legacy axes/actions paradigm, or when using the Enhanced Input Plugin, 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. To reuse the example from above:

#include "ExampleEntity.h"

#include "MyCustomInput.h"
#include "SnapNetSimulation.h"

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

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