Get SpatialOS

Sites

Menu
You are viewing the docs for 11.0, an old version of SpatialOS. 12.0 is the newest →

The SpatialOS Unreal integration is currently experimental. It's usable, but definitely has some sharp edges! 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->GetPosition();

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, construct an update object and send it.

To set the value of a property 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 set the value of a property 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(UConversionsFunctionLibrary::SpatialOsCoordinatesToUnrealCoordinates(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(UConversionsFunctionLibrary::SpatialOsCoordinatesToUnrealCoordinates(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 setting the value of a property.

To trigger an event in blueprints:

Trigger an event

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);

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:

    TransformComponent->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(UHeartbeatCommandResult* result)
    {
        if (!result->Success())
        {
            auto errorMessage = result->GetErrorMessage();
            // handle error, retry logic
            return;
        }
        UHeartbeatResponse* response = result->GetResponse();
        auto someProperty = response.GetSomeProperty();
    }
    
  3. Include the method declaration in your header file.

  4. Send the command request:

    auto rawRequest = improbable::player::HeartbeatRequest();
    auto request = NewObject<UHeartbeatRequest>()->Init(rawRequest);
    TransformComponent->SendCommand()->Heartbeat(entityId, request, &AMyCharacterCpp::OnHeartbeatResponse, 0);
    

Note: 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 blueprints:

Send a command

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 blueprints:

Respond to a command

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums