Input
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.
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.
Warning
Be very careful in deciding what you consider input since this comes directly from the client and, as a result, could be tampered with by cheaters. For example, sending whether or not you’re pushing the “forward” key has limited usefulness to cheaters. On the other hand, sending the position of your character as part of your input would allow cheaters to teleport anywhere they want if they were to modify it.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();
}
}
}