How to create a global actor for simulation management

Background
I have been developing a test simulation that has an arbitrary number of autonomous vehicles moving around an automatically generated environment - with a number of autonomous turrets that fire projectiles. There are NO player controllable entities in the level - but I was hoping to have a Master Controller actor to manage a sequence of simulation runs, and also handle data I/O operations (such as data saving), and possibly world re-building. The simulation works well apart from this Master Controller. For info - the sim_controller just calls a C++ function to save the output_data to a CSV file.

Problem
When I create a sim_controller actor (which stores Output_Data - see below) with a test custom test event (Test_Event), I cannot seem to call/trigger these events from any of the other actors (in the second image this was from a separate actor BP). I have created a local variable (Master is of type sim_controller) have tried triggering the Test_Event both directly and through cast_to_class approach. Neither approach however seems to trigger the sim_controller event. I also have an instance of the sim_controller actor dropped into the level for good measure.
I assume the issue is that creating a variable within another actor BP is at best creating a separate instance, and not a truly global.

EDIT - the examples below are highly simplified to highlight what I tried to implement more broadly.

Question
If I want to communicate with the sim_controller actor (instantiated in the level) - do I need to spawn all the vehicles/turrets from that sim_controller, and pass through reference to the global sim_controller? I don’t want to really try and create a local instance of the sim_controller with in the vehicle/turret BP, because I want to be able to destroy these between runs.

Once again sorry for the noob question - but I can’t find tutorials on developing global controllers that weren’t part of the player controllable actor (which I don’t have). Or do I need to make the sim_controller actor player controllable?

1 Like

Hi OzMomotaro

There are different ways you could approach this. The easiest way would be to store a reference in the Game Mode class as the game mode should be accessible by all objects (unless this is a networked application).

  1. Create a custom Game Mode class
  2. Go to project settings → Maps and Modes → Assign the custom game mode (Or assign it in the world outliner as an override if you prefer).
  3. Add a variable to store the reference to the sim controller
  4. Inside your sim controller class, on BeginPlay call GetGameMode → Cast to custom Game Mode → Set the sim controller reference to self.
  5. Now all other objects can access the sim controller through the Game Mode providing at least one instance of the sim controller exists.

Unless you are using the sim controller to set or manage the positions of the objects in design time, it might be better to use classes designed to handle the rules of the game / level and users such as Game Instance, Game Mode and Player Controller instead of just using a standard actor.

Good luck

Alex

Answered my own question - and for anyone with a similar question I would recommend the following UE4 [UE4 BP Communications Tutorial][1]. I should have watched this a long time ago, as it simplified things enormously (as I had erroneously believed that you could access global class functions without having a specific instance - and had assumed that you could transmit signals to all (both wrong).

One Solution: Create a BP Interface for the different types of comms data… for example when you want to transfer data between actors (in this case one actor is a simulation controller). I will caveat this with the fact that I’m a noob - so my solution may not be the best/neatest/good-practice. There are other ways, but I like this one - you use the interface to effectively create a 1-1 communications channel between objects/actors etc… the trick is that you need to have a unique instantiated object reference (not just a class reference). In this case the Sim_Controller is just a stock standard actor, but you will want to have a reference to it in each actor/element that needs to communicate/transfer to it (in this case autonomous vehicles and turrets).

Step 1: Create Interface BP with functions that relate to event-triggers - the interface should contain ‘functions’ that relate to the unique types of command/data-transfer that you want to undertake (each one will generate an event that can be listened to by the Sim Controller (in this case). The “function” is also defined by the data structures that are being transferred.

Step 2: Create a Send Signal/Event from the object/actor that is transmitting information/data (in this case to a global controller). This is done by calling the function, and attaching both the appropriate data structure from your send object/actor.

Step 3: Obtain a unique/specific reference to the Sim Controller… I know this is NOT the most elegant solution, but as I only have a single Sim Controller actor in the game, I can do a simple get all actors of class call. This reference is used for the actual comms transfer, as you need to know the specific instance (not class) of the object/actor/etc.

Step 4: Create an event listener in the receiving actors (Sim Controller) that reacts to the signal/event that is transmitted to it. As you need to include a specific object reference in the call (in step 2/3) only this actor will need to react to it.

322402-receive-transfer-event.jpg

As I said - this is pretty basic stuff, and I’m sure there are other ways of doing this (like casting directly to the actor - if you are able to extract the reference from a collision for example). Let me know if there are better ways of doing this.

Thanks for that Alex - as I said, I’m sure my answer was likely not the best (or most elegant). The challenge with jumping into something like UE4 is that your solutions tend to reflect more your experience (“everything looks like a nail…”). So thanks for the different option. I also felt that game mode should enable a sim-variant, so your approach looks neater (though I don’t have any experience with game modes). Much appreciated… but I wanted to put down my own (simple) approach just in case anyone like me had no idea - and also as a way of giving a nudge to all the better ideas out there.

Edit - I was going to actually try and use Python to do most design-time control functions through the scripting (though it would be better if there were more in-built methods to also use python at run-time).

Edit-edit :slight_smile: so from what I can interpret from your answer, the advantage is that this pre-configures all objects (etc) to have a link to the game-mode derived sim_controller? So this removes the need to have dedicated calls/assignments to local sim_controller classes (ie my get_all_actors_of_class approach to create local references on all actors that need to communicate with the controller?). Sorry if that’s a nonsense question.

Alex - tried that and it makes sense. This is going to sound very noob, however does this mean that every time you want to access the Sim_Controller (from custom Game Mode) you need to cast (Get Game Mode > Cast to Sim_Mode > pull off Sim_Controller > access any functions/variables (see below).

Would it not be better to still have a local reference to this Controller in the actor BP, to simplify this? For example you create a reference at Begin_Play? If so, then the only advantage of putting it in the custom Game Mode (Sim_Mode) is that you guarantee initialization before any other BP?

322539-question1b.jpg

Am I missing something obvious? Thanks in advance

I know this is old but in case anyone’s runs into this:

To answer your latest question, you could create a helper function to wrap "Get Game Mode > Cast to Sim_Mode > pull off Sim_Controller ", that way it becomes “GetSimController” and you can use it immediately.

That function could potentially be a member or even a global function as described here:

Of course what you mentioned about doing it in the BeginPlay could also work, but it could be nicer to avoid storing it everywhere and potentially having stale data (albeit unlikely).