Get SpatialOS

Sites

Menu

The SpatialOS Unreal integration is currently in beta. We’re very open to feedback - don’t hesitate to get in touch on the forums if you have any thoughts.

Interacting with entity components

This page details the ways that an Unreal worker can interact with the components on SpatialOS entities. They can interact with three things in a component: properties, events, and commands. See Designing components for details of what these are.

The code used to do this is generated from schema when you run spatial worker codegen. When you change your schema, you must run this command to get up-to-date code and blueprint nodes to use.

For information about creating components themselves, see the schema documentation.

This page uses the example of an entity blueprint called MyCharacter. It has a custom C++ parent class AMyCharacterCpp that inherits from ACharacter, and has a generated UTransformComponent actor component as a member with a public getter TransformComponent().

Prerequisites

To interact with an entity’s SpatialOS component using the UnrealSDK, make sure you have added the corresponding generated UActorComponent to the entity’s blueprint.

Check if the component is ready

Before you invoke any methods on an UnrealSDK generated UActorComponent, make sure the component is ready.

To check a component is ready in code:

if (!TransformComponent->IsComponentReady())
{
    return;
}
// can use the component

To check a component is ready in blueprints:

Check if component is ready

To listen for when a component becomes ready in code:

  1. Create a method to be invoked when the component becomes ready:

    void AMyCharacterCpp::OnTransformComponentReady()
    {
        // start using the component
    }
    
  2. Bind this method to the OnComponentReady event on the TransformComponent:

    TransformComponent->OnComponentReady.AddDynamic(this, &AMyCharacterCpp::OnTransformComponentReady);
    

To listen for when a component becomes ready in blueprints:

Listen for component ready

Get the value of a property

Prerequisites:

To get the value of a property in code:

auto currentPosition = TransformComponent->Position;

To get the value of a property in blueprints:

Get value of a property

Set the value of a property

Prerequisites:

To set the value of a property, you can:

  • Use automatic state replication, changing the property and letting the component replicate its state automatically (our suggested approach)
  • Use manual state replication, manually constructing and sending updates to the component

Automatic state replication

Each component has a configurable field MaxUpdatesPerSecond which allows you to specify how frequently you’d like the component to replicate its state. MaxUpdatesPerSecond defaults to 30 updates per second, and any value above 0 keeps automatic updates enabled. Updates will be triggered at the specified rate when changes to the component data are detected.

The update rate impacts the bandwidth usage and the local execution time, as it determines how frequently components are checked for local changes.

To set the value of a property in code:

TransformComponent->Position = FVector{0.f, 10.f, 0.f};
// or
TransformComponent->Position.Y = 10.f;

To change the update rate in code:

TransformComponent->MaxUpdatesPerSecond = 10;

To set the value of a property in blueprints:

Set value of a property

To change the update rate in blueprints:

Set desired update rate

Remember to call USpatialOSComponentUpdater->UpdateComponents() after you call USpatialOS->ProcessOps(). For more information, see Processing operations:

USpatialOSComponentUpdater->UpdateComponents(EntityRegistry, DeltaTime);

MaxUpdatesPerSecond defines the frequency of the component change replication while USpatialOSComponentUpdater->UpdateComponents() actually flushes out the changes in components and propagates them over network.

The argument DeltaTime is used to track the update rate according to the setting MaxUpdatesPerSecond in the components.

Manual state replication

Automatic updates should be sufficient for most use cases, but you can send manual updates to ensure state is replicated immediately when it changes, rather than waiting for the next fixed update window. You can disable automatic updates on a per-component basis, by setting MaxUpdatesPerSecond to 0. Then, to send a manual component update, you’ll need to build and send it through the component API.

To send an update manually in code:

auto newPosition = improbable::math::Coordinates(10, 20, 10);
auto rawUpdate = improbable::common::Transform::Update().set_position(newPosition);
auto update = NewObject<UTransformComponentUpdate>()->Init(rawUpdate);
TransformComponent->SendComponentUpdate(update);

Make sure you include headers for the types used to construct the update.

To send an update manually in blueprints:

Set value of a property

Responding to a change of a property

Prerequisites:

To respond to a change in a specific property in code:

  1. Create a method to be invoked when a position update is received:

    void AMyCharacterCpp::OnTransformPositionUpdate(FVector newPosition)
    {
        SetActorLocation(newPosition);
    }
    
  2. Include the method declaration in your header file.

  3. Bind this method to the OnPositionUpdate event on the TransformComponent:

    TransformComponent->OnPositionUpdate.AddDynamic(this, &AMyCharacterCpp::OnTransformPositionUpdate);
    

To respond to a change in a specific property in blueprints:

Respond to change of specific property

To respond to a change in any property of a component in code:

  1. Create a method to be invoked when a component update is received, and check if the property has a new value:

    void AMyCharacterCpp::OnTransformComponentUpdate(UTransformComponentUpdate* update)
    {
        if (update->HasPosition())
        {
            SetActorLocation(update->GetPosition());
        }
    }
    
  2. Include the method declaration in your header file.

  3. Bind this method to the OnComponentUpdate event:

    TransformComponent->OnComponentUpdate.AddDynamic(this, &AMyCharacterCpp::OnTransformComponentUpdate);
    

To respond to a change in any property of a component in blueprints:

Respond to change of any property

Triggering an event

Prerequisites:

Triggering an event is much like sending a manual component update.

To trigger an event in code:

auto textEvent = improbable::common::TextEvent("Hello world");
auto rawUpdate = improbable::common::Transform::Update().add_text_event(textEvent);
auto update = NewObject<UTransformComponentUpdate>()->Init(rawUpdate);
TransformComponent->SendComponentUpdate(update);

To trigger an event in blueprints:

Trigger an event

Responding to an event

Prerequisites:

Responding to an event is much like responding to a change of a property.

To respond to an event in code:

  1. Create a method to be invoked when the event is received:

    void AMyCharacterCpp::OnTransformTextEvent(UTextEvent* textEvent)
    {
        auto text = textEvent->GetText();
        // print text
    }
    
  2. Include the method declaration in your header file.

  3. Bind this method to the OnTextEvent event on the TransformComponent:

    TransformComponent->OnTextEvent.AddDynamic(this, &AMyCharacterCpp::OnTransformTextEvent);
    

To respond to an event in blueprints:

Respond to an event

Sending a command

Entity commands are defined in the schema, and the definition includes an input type and a return type.

When you send a command, register a callback to receive a response, which includes:

  • information about the command’s success or failure
  • a response object of the command return type

It is guaranteed that SpatialOS will only report a command’s success if it actually succeeded, but no other guarantees are made.

Prerequisites:

To send a command in code:

  1. In Visual Studio, type the name of the component from which you want to send the command (e.g. HearbeatSender):

    HearbeatSender->SendCommand()->
    

    and the autocomplete suggestion box should display methods for all the commands defined in your schema.

  2. To send a command, create a method to be invoked with the command result when a response is received:

    void AMyCharacterCpp::OnHeartbeatResponse(const FSpatialOSCommandResult& result, UHeartbeatResponse* response)
    {
        if (!result.Success())
        {
            auto errorMessage = result->GetErrorMessage();
            // handle error, retry logic
            return;
        }
        auto someProperty = response->GetSomeProperty();
    }
    
  3. Include the method declaration in your header file.

  4. Include an instance of the command result delegate in your header file. The type of this delegate will be F[command_name]CommandResultDelegate.

    FHeartbeatCommandResultDelegate OnHeartbeatResponseDelegate;
    
  5. Initialise the delegate in the component constructor.

    OnHeartbeatResponseDelegate.BindUFuntion(this, "OnHeartbeatResponse");
    
  6. Send the command request:

    auto rawRequest = improbable::player::HeartbeatRequest();
    auto request = NewObject<UHeartbeatRequest>()->Init(rawRequest);
    HearbeatSender->SendCommand()->Heartbeat(entityId, request, OnHeartbeatResponseDelegate, 0);
    

    When sending a command you have the option of enabling short-circuiting using the component delivery parameter and the ECommandDelivery enumeration. The default setting for command delivery is to not short-circuit the command. To enable short-circuiting, use the following additional argument:

    HearbeatSender->SendCommand()->Heartbeat(entityId, request, OnHeartbeatResponseDelegate, 0, ECommandDelivery::SHORT_CIRCUIT);
    

    For more details on command short circuiting, refer to the commands documentation.

The example above invokes the Heartbeat command on the entity with entity ID entityId. This entity must have the component in which the Heartbeat command is defined, and some worker must have write access to this component, and be listening for incoming command requests and responding to them.

To send a command in blueprint:

Send a command

To check the status code for the command, use the blueprint node GetCommandStatusCode which is part of the SpatialOSBlueprintLibrary class.

Get command status code

Responding to a command request

A command should be responded to by the worker that has write access on the component that the command is defined in.

Responding to a command request is similar to responding to a change in a property, except that your callback is passed a special Responder object, containing the request, the caller worker ID, and a method to send the response, which you must call.

To respond to an incoming command request in code:

  1. Create a method to be invoked with a Responder when a command request is received:

    void AMyCharacterCpp::OnHeartbeatCommandRequest(UHeartbeatCommandResponder* responder)
    {
        auto callerWorkerId = responder->GetCallerWorkerId();
        auto request = responder->GetRequest();
        // logic based on callerWorkerId and request ...
    
        auto rawResponse = improbable::player::HeartbeatResponse();
        auto response = NewObject<UHeartbeatResponse>()->Init(rawResponse);
        responder->SendResponse(response);
    }
    
  2. Include the method declaration in your header file.

  3. Bind this method to the OnHeartbeatCommandRequest event, on the HeartbeatReceiverComponent. For example:

    HeartbeatReceiverComponent->OnHeartbeatCommandRequest.AddDynamic(this, &AMyCharacterCpp::OnHeartbeatCommandRequest);
    

In blueprint:

Respond to a command

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums