Module 9: Publishing Samples

How to publish from a game object

Now that we can subscribe to topics in an Unreal Engine project let’s look at how to publish from a game object. Go to the C++ classes/Unreal Shapes folder in the content browser. Right click and create a new Pawn object. Give it a name like ShapePub for Shape Publisher.

This will bring up Visual Studio and creates ShapePub.h and ShapePub.cpp. 

The code will be similar to the subscriber but now we have to create a publisher and writer instead of a subscriber or reader. In ShapePub.h we add the header files as in the subscriber.

#pragma warning(disable:4668)
#pragma warning(disable:4530)

#include <dds/dds.hpp>

Add the needed variables to ShapePub.h which is similar to the subscriber. 

/* Mesh component */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "MeshComponent")
UStaticMeshComponent* StaticMesh;

/* Topic Name */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Connext")
FString TopicName = FString("Square");

/* Color */
UPROPERTY(EditAnywhere, BlueprintReadWrite, category = "Connext")
FString Color = FString("BLUE");

/* Domain ID */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Connext")
int32 DomainID = 0;

 FString QOS_URL = FString("Connext/Unreal_Shapes.xml");
 FString TYPE_NAME = FString("ShapeType");
 dds::pub::DataWriter<dds::core::xtypes::DynamicData> writer =

Now let’s implement the Shape publisher class (ShapePub.cpp) in a similar way to the subscriber. first add the additional include directories.

#include <rti/core/rticore.hpp>
#include <rti/core/QosProviderParams.hpp>

In the constructor AShapePub() we can set the mesh component and set it as root component:

StaticMesh = CreateDefaultSubobject<UStaticMeshComponent> 
RootComponent = StaticMesh;

In BeginPlay create the Connext instances:

/* Construct the fully qualified name for the configuration file (XML) location */
FString xmlFile = FPaths::Combine(FPaths::ProjectContentDir(), QOS_URL);
/* Read the configuration file and set the defaults*/
rti::core::QosProviderParams provider_name;
provider_name.url_profile({ TCHAR_TO_UTF8(*xmlFile) });

/* Initialize the dynamic data type */
const dds::core::xtypes::DynamicType& myType = dds::core::QosProvider::Default().extensions().type(TCHAR_TO_UTF8(*TYPE_NAME));

/* Create a domain participant */
/* Let’s see if a domain participant already exists */
dds::domain::DomainParticipant participant = dds::domain::find(DomainID);
/* If not create one */
if (participant == dds::core::null)
   participant = dds::domain::DomainParticipant(DomainID);

/* Get a reference to the implicit publisher*/
dds::pub::Publisher publisher = rti::pub::implicit_publisher(participant);

/* Create the topic with the configured name for the participant and dynamic type */
/* Find the topic */
auto topic = dds::topic::find<dds::topic::Topic<dds::core::xtypes::DynamicData>>(participant, TCHAR_TO_UTF8(*TopicName));
/* If the topic doesn’t exist create it */
if (topic == dds::core::null)
   topic = dds::topic::Topic<dds::core::xtypes::DynamicData>(participant,  
             TCHAR_TO_UTF8(*TopicName), myType);

/* Create the data writer*/
std::vector<dds::pub::DataWriter<dds::core::xtypes::DynamicData> > writers;

/* Get the list of writers*/
int writer_count = dds::pub::find<dds::pub::DataWriter<dds::core::xtypes::DynamicData> >(

     /* All we need is at least one writer. If there are multiple let’s use the first
       one returned. If no readers are found we create one
   if (writer_count)
       writer = writers[0];
       writer = dds::pub::DataWriter<dds::core::xtypes::DynamicData>(publisher, topic);

Next we need to go ahead and publish the shapes data periodically. The easiest is to do it every time the AShapePub::Tick function is called. Depending on the use case you may not want to publish data every frame. It could be done every Nth frame or if the data changes enough to warrant an update. For simplicity in this example we publish every frame. 

We need to get our shape moving around. There are many ways to move it around by the Unreal Engine. For this example we keep it simple and don’t use the physics engine or handle collisions. We just use AddActorLocalOffset to move our shape and if we reach the box limit, the direction will change. 

As mentioned earlier the Shape application uses an area of about 0 to 250. We have to define the size in which the shape can move. In addition we also need the direction in which the Shape is moving and the dynamic data sample which will be published to Connext. To do this we add the following to the private section in the header file (ShapePub.h):

FVector MinBox = FVector(0.0f);
FVector MaxBox = FVector(250.0f, 260.0f, 270.0f);
FVector Direction = FVector(0.0f);
dds::core::xtypes::DynamicData *sample = dds::core::null;

The box has been defined with different maximum values for each side just so that we don’t have it bounce diagonally. Having different bounds for each axis makes for more interesting movement. Next we need to initialize the direction and place our shape somewhere within the boundaries. In addition we can set the values of the published sample which do not change: The color and shape size. The initialization can be added at the end of BeginPlay.

/* Initialize direction and set initial location */
Direction = FVector(FMath::RandRange(-100.0f, 100.0f), FMath::RandRange(-100.0f, 100.0f), FMath::RandRange(-100.0f, 100.0f));
SetActorLocation(FVector(FMath::RandRange(MinBox.X, MaxBox.X), FMath::RandRange(MinBox.Y, MaxBox.Y), FMath::RandRange(MinBox.Z, MaxBox.Z)));

/* Initialize the sample and set the size and color */
sample = new dds::core::xtypes::DynamicData(myType);
sample->value<int32_t>("shapesize", 30);
sample->value<std::string>("color", TCHAR_TO_UTF8(*Color));

Now we can move the shape and publish the location in the Tick function. As with the subscriber we need to adjust to the different point of origin used in the Shape application. AddActorLocalOffset returns information if the move resulted in a hit. For now this is ignored. However, feel free to change the code to take action on a hit and have it bounce off of each other or some other behavior. 


/* AddActorLocalOffset returns information if the move resulted 
  in a hit. For now this is ignored. 
FHitResult Hit;

/* Move the shape */
AddActorLocalOffset(Direction * DeltaTime, false, &Hit);

/* Get the current location and publish it */
FVector Location = GetActorLocation();
/* If we hit any of the bounds change direction */
if (Location.X > MaxBox.X) { Location.X = MaxBox.X; Direction.X *= -1; }
if (Location.Y > MaxBox.Y) { Location.Y = MaxBox.Y; Direction.Y *= -1; }
if (Location.Z > MaxBox.Z) { Location.Z = MaxBox.Z; Direction.Z *= -1; }
if (Location.X < MinBox.X) { Location.X = MinBox.X; Direction.X *= -1; }
if (Location.Y < MinBox.Y) { Location.Y = MinBox.Y; Direction.Y *= -1; }
if (Location.Z < MinBox.Z) { Location.Z = MinBox.Z; Direction.Z *= -1; }

/* Only publish if we have a valid writer */
if (writer != dds::core::null)
   /* Set the values and adjust for the different origin */
   sample->value<int32_t>("x", MaxBox.Y - Location.Y);
   sample->value<int32_t>("y", MaxBox.Z - Location.Z);

With the shape publisher class completed we can create the blueprint class and add it to the viewport. Right click on the SquarePub C++ class and select “Create Blueprint based on ShapePub” and give it the name SquarePub for square publisher. Now we can drag the ShapePub Blueprint class into the viewport and configure the mesh, material, and Connext configurations.

If you run the example and create a Square subscriber in the Shapes application, you will see it moving in sync with the Unreal project.

This video shows the steps for creating a shapes publisher.

Next: Module 10: Subscribing to Instances at Runtime