LearningAgentManager in pure C++ (setting MaxAgents -or- components can't find Manager)

I have been working with the Learning Agents Drive tutorial. I got it to work well with Blueprint, like it is presented. Then I went back and tried to redo it using C++. Everything has gone smoothly right up to the end. I’m finding I cannot get the LearningAgentManager to work correctly without encapsulating it in a Blueprint. However, doing so causes other problems.

Pure C++ Manager Class

Here’s the problem when I don’t have my manager wrapped in a Blueprint class:

  • The MaxAgents property in ALearningAgentsManager is private and immutable.
  • It is used inside PostInitProperties() to allocate memory for all the agents so I understand why it is private and immutable.
  • It NEEDS to be set. Leaving it as the default 1 is absurd, but there seems to be no way to do this from C++.
  • MaxAgents as a UPROPERTY is marked EditDefaultsOnly so you can set it if you derive a blueprint from your class and change the ‘class defaults’

The fact that this can be set that way means there MUST be a way to set it from C++ but it is buried in the complex and poorly documented world of the UObjct system and FObjectInitializer and its archetype and I can’t find a way to do this (I’m going to ask about that elsewhere).

Wrapping my C++ Manager with a Blueprint

So, I gave up on that and tried to just wrap it in a Blueprint version of my manager. From there, I can set the MaxAgents in the class defaults as expected BUT, I get a different issue:

  • The Interactor, Trainer, and Policy components on the manager (which are all written and attached via C++) fail to properly initialize when my manager is wrapped with a Blueprint.
  • I am creating them all in my manager’s constructor with CreateDefaultSubobject<>().
  • Their ‘outer’ is properly set to my custom Manager class which inherits from ALearningAgentsManager.
  • Despite this, all of these components report “Error: Agent Interactor: Must be attached to a LearningAgentsManager Actor.” when I try to run their respective Setup methods.

This error ONLY happens when the manager is wrapped in a Blueprint; my code works fine when I just add the C++ class to the scene directly. However, the C++ version has MaxAgents set to 1 and I can’t change it. Catch 22!!

My best guess for the problem with the Blueprint version was that the order of execution is somehow different (constructor vs. BeginPlay vs. calling the various component Setup funcs, etc.) but I’ve tried doing a few logs from these various functions and they seem to execute in the order expected and all the C++ versions are running too (they aren’t getting shadowed by the Blueprint versions). Furthermore, all these logs happen in the same order regardless of whether the C++ class is wrapped in a Blueprint or not.

I examined the source code for the LearningAgentsManager and found where that error message about needing to be attached to a LearningAgentsManager actor happens. All it is doing is calling GetOwner with the template set to ALearningAgentsManager so I tried doing that myself and ensuring it doesn’t return null and IT DOESN’T! It seems like it should pass that test, but for some reason when playing-in-editor, it fails it. This makes me think I somehow have multiple versions of these components that are attached to my LearningAgentManager and some of them are getting attached properly while others are not. And that just confuses me even more!

So that’s my situation! Could use help with either or both of these things. I’m going to post about the general “provide default value for a private, immutable parent property” separately as that is not necessarily specific to the Learning Agents plugin. But solving this particular problem with the Learning Agents in C++ is my general goal so …

2 Likes

I’m sure if anyone wants to help their probably going to need to see my code. Here it is! It seems to work other than the problems I note above and I can’t really get much further and fix any remaining issues until I solve the MaxAgents problem described above.

I found my own solution to the Blueprint version of the Manager.

  • The Manager Components only work when marked as ‘transient’
  • This is the second parameter passed to CreateDefaultSubobject

So in the Manager’s constructor, I changed the component creation code to this:

AutonomousInteractor = CreateDefaultSubobject<UAutonomousVehicleInteractor>("Agent Interactor", true);
AutonomousPolicy = CreateDefaultSubobject<UAutonomousVehiclePolicy>("Agent Policy", true);
AutonomousTrainer = CreateDefaultSubobject<UAutonomousVehicleTrainer>("Agent Trainer", true);

Notice the extra ‘true’ parameter. That change made it so the ‘Manager’ variable inside each component did NOT get overwritten with nullptr. Not sure why, I need to learn more about what it means to be ‘transient’ and how to tell when a component needs this. But for now, this allows it to work!

I’ll update the code and push the changes soon. Still curious if there is a solution to the pure C++ approach (a way to set MaxAgents without having to create a Blueprint).

1 Like

Thanks for reporting this issue. I did a fair amount of testing with C++ code but I usually end up creating some kind of blueprint at the bottom. We must have overlooked this issue with the MaxAgentNum being unchangeable from pure C++ code.

Transient is needed when you don’t want that particular property/object to be serialized.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.