How to Spawn Scene Capture at Runtime

I am trying to create a simulation where the user can drive a car around a map. The car will be equipped with various sensors (example: camera, radar, lidar, …). I have been able to create the sensor by placing using a 2D scene capture. This works great and all but I want to create some way of defining the number and position of the scene captures through a configuration file of some sort. I am not sure how to go about creating such a file or what code I would need to process the file and place the scene captures on the vehicle blue print at run time.

Edit:

I’ve gotten everything to work thanks to the discussion bellow, however I get an error when trying to spawn the SceneCapture during runtime. The code is

USceneCaptureComponent2D *sceneCaptureActor = (class USceneCaptureComponent2D *)GetWorld()->SpawnActor<USceneCaptureComponent2D>(USceneCaptureComponent2D::StaticClass());

which ends up returning a nullptr and printing error

LogSpawn: Warning: SpawnActor failed because SceneCaptureComponent2D is not an actor class

Not sure what to do about it.

Also, I updated the sensor config to

UENUM()
enum SensorType { RADAR, LIDAR };

UCLASS(config = Game)
class USensorPlacementConfigData : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float rot_x;
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float rot_y;
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float rot_z;
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float rot_w;

	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float scale_x;
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float scale_y;
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float scale_z;

	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float trans_x;
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float trans_y;
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		float trans_z;

	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		TEnumAsByte<SensorType> type;
};

and tried using a config called DefaultSensorPlacementConfigData.ini

[Script/<WATOSIM_API>.USensorPlacementConfigData]
rot_x=0,
rot_y=0,
rot_z=0,
rot_w=0,
scale_x=0,
scale_y=0,
scale_z=0,
trans_x=0,
trans_y=0,
trans_z=100, 
type="USensorPlacementConfigData::SensorType::RADAR"

When looking in at the blueprint in ue4 I see the default value for the array of sensors and added a sensor

232955-defaultsensors.png

But when printing the values in c++ they are all 0. Not sure why that is or how to get my values showing up.

To recap please help with SceneCapture spawn problem and config values not being set properly.

Suggestions Tried:

Tried using a struct instead of class for config object. Here is my final attempt, I fiddled a bunch with it and couldn’t get it to build

USTRUCT()
struct FSensorPlacementConfigData
{
	GENERATED_BODY()
public:
	UPROPERTY( )
		float rot_x;
	UPROPERTY()
		float rot_y;
	UPROPERTY( )
		float rot_z;
	UPROPERTY( )
		float rot_w;

	UPROPERTY( )
		float scale_x;
	UPROPERTY( )
		float scale_y;
	UPROPERTY( )
		float scale_z;

	UPROPERTY( )
		float trans_x;
	UPROPERTY( )
		float trans_y;
	UPROPERTY( )
		float trans_z;

	UPROPERTY( )
		TEnumAsByte<SensorType> type;
};

class API ASensorMountedVehicle : public AWheeledVehicle
{
	GENERATED_BODY()
	

public:
	UPROPERTY(Config, BlueprintReadWrite, EditAnywhere)
		TArray<struct FSensorPlacementConfigData > sensors;
}

The error produced at the “sensors” line is

Error: Type ‘TArray’ is not supported by blueprint. SensorMountedVehicle.sensors

What are you asking?

Seems like you got 3 questions.
How do I …

  1. spawn a SceneCapture2d actor into the world and set a TexterRenderTarget2d to use?
  2. specify information in a config file
  3. bind a node in c++?

Again, What are you asking… What it is you are doing that is not functioning?

I updated my question, should make more sense

Do You write code?

Here is how I would do it. Derive your blueprint from a C++ class that you have written. Use a different C++ class for each car type. The config file will set the same captures on every instance the same.

in your UCLASS() Decorator add “Config=MyCPPClassName” but without the qoutes,

Then create an array of the configuration data for the items you want to spawn.

TArray<UMyCppClassConfigData>Captures;

Decorate this with UPROPERTY(Config).

Then Above your McCPPClass add the definition for the instanced capture configuration data.

UCLASS()
class UMyCppClassConfigData : public UObject
{
    GENERATED_BODY()

   UPROPERTY(Config)
   float MyXPosition;

   UPROPERTY(Config)
   float MyYPosition;

...
}

then in your config folder create a file named : “DefaultMyCPPClassName.ini”

Edit the file and add the following lines:

[Script/<MyModule>.MyCPPClassName]
+Captures=(MyXPosition=, MyYPosition=, ...)

Each time you add a +Captures= You add another item to the array.

Then inside your class code, BeginPlay() loop over the array and spawn your items, with the already populated configuration data.

Keep in mind that The config data can change without a recompile. So you could open your self up to some serious mod-ing… (if you wanted to).

Cheers.

Thanks allot for the instructions above, was able to follow them and I think I’ve almost got it to work. I’m having an odd error

Error: Missing ‘*’ in Expected a pointer type

when make the sensors array an array of pointers I get this error

Error: Not allowed to use ‘config’ with object variables

#include "CoreMinimal.h"
#include "WheeledVehicle.h"
#include "Components/SceneCaptureComponent2D.h"
#include "SensorMountedVehicle.generated.h"

UENUM()
enum SensorType { RADAR, LIDAR };

UCLASS(Config = SensorConfig)
class USensorPlacementConfigData : public UObject
{
private:

	GENERATED_BODY()

	UPROPERTY(Config)
		FTransform myRelativeTrans;

	UPROPERTY(Config)
		TEnumAsByte<SensorType> type;

public:
	const FTransform &getRelativeTransform() const { return myRelativeTrans;  }
	const SensorType getType() const { return type; }
};

UCLASS(Config = SensorMountedVehicleConfig)
class SIM_API ASensorMountedVehicle : public AWheeledVehicle
{
	GENERATED_BODY()

private:
	UPROPERTY(Config, Category=Any)
		TArray<USensorPlacementConfigData *> sensors;

	UPROPERTY(Config, BlueprintReadWrite)
		TArray<USceneCaptureComponent2D *> mountedSensors;

	UPROPERTY(Config, BlueprintReadWrite)
		TArray<UTextureRenderTarget2D *> sensorRenderTargets;

	// Reference UMG Asset in the Editor
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Widgets")
		TSubclassOf<class UChevyBoltHUD> wMyHUD;

	// Variable to hold the widget After Creating it.
	UChevyBoltHUD* myHUD;
	
public:
	const TArray <UTextureRenderTarget2D *> &getSensorTargets() { return sensorRenderTargets; }

	virtual void BeginPlay();
	
	virtual ~ASensorMountedVehicle();
};

Thanks for the help

Well your config data is not dynamically allocated. It is an aggregate of the array. You dont want an array of pointers you want an array of config data. Get rid of the astrisks.

When I remove the pointer from the declaration I get the first error I mentioned “Missing ‘*’ in an expected pointer type”, Not sure why it wont let me create an array of objects, it seems to want a array of pointers instead?

Could you point me to some documentation on how to dynamically load objects? I cant seem to find anything online.

2 things about unreal. You cant have an aggregate UObject class. Only dynamically composed. You can have an array of * to uobjects, but you have to create them manually in the constructor as default objects.

You can have aggregate Structs… something like …

The Config data for a USensorPlacementConfigData would come from an ini file called DefaultSensorPlacementConfigData.ini, and not inherit from the owning Uobjects config data.

IE: to instance the sensors, you need an array of config data that is not a sensor itself.

Hope this helps.

Hey thanks for the reply, but when remove the extension of the UObject class i get the following error

Error: Class ‘USensorPlacementConfigData’ must inherit UObject or a UObject-derived class

I have created a DefaultSensorMountedVehicle.ini file already. Not sure how to init the Ftransform. Any suggestions?
api
[Script/.ASensorMountedVehicle]
+sensors=(
myRelativeTrans=(,

	), type="USensorPlacementConfigData::SensorType::RADAR")

Do I also have to create a DefaultSensorPlacementConfigData.ini? I will try that now

Thanks again for the help, would be completely stuck without the community support

IM not sure what you mean by ‘extention’? You are using UObjects yes? You can not use UObjects… They do not aggregate. You need to make them USTRUCT()'s with the struct keyword. Not UCLASS()'s with the class keyword.

You can have a config entry for a class. It is the DEFAULT for the class and is stored in the DefaultClassObject or the CDO.

You are creating a single CDO that has all the information you need to SPAWN the other UObjects.

Ok Thanks! I think I got it all working, everything builds and runs, however I cant not figure out how to specify values for the sensor config data. In particular I dont know how to set a value for the transfor (FTransform) or the type which is an enum.

My config looks like this right now

[Script/<WATOSIM_API>.USensorPlacementConfigData]
myRelativeTrans=()
type="USensorPlacementConfigData::SensorType::RADAR"

but i dont think this works

Well you probably cant. If its not a serializable class then it cant deserialize config data. And there is a very good reason why you would not want it to be a serializable class. Becuase you might want it to use SSE registers, and run fast, and stuff.

Did you not wonder why I put XPos and YPos as separate floats?

What you need to construct an FTransform is the Rotation, Scale, and Translation…

If it were a serializable class then you simply assign the members by name. If you look at FTransform, is it decorated with UCLASS or USTRUCT? If so look at the members, do they have Decorations? If so look at the type definitions of those members are they Decorated? so on and so forth. If the answer is ever no, then no ,it is not a serializable class.

The real question you should be asking yourself is, “Why do I need to get this data from an ini file”?

Everyone else just uses the blueprint, and plugs values in there directly. Even using the little position, scale, rotation widgets to do so. Then when they save out the Blueprint, all of that data is saved with it.

Updated my question

Only actors exist in the world. And Components go on actors.

There is already an actor wrapper for USceneCaptureComponent2D, I believe it is called ASceneCapture2d.

Once they are spawned You need to have something referencing them or the garbage collector will just toss them out the first chance it gets.

Your call to SpawnActor should give an owner to be attached to. This adds a reference to the spawned actor. Make sure you fill out the FActorSpawnParameters, and you should be ok.

When you have multiple issue you should create multiple tickets. The noise on this page is a good reason why…

A couple notes about config serialization.

[Script/.USensorPlacementConfigData]

the are customarily used to indicate a user dependent value. they should not appear in the final ini.

Unreal strips the ‘U’ for components and UObjects and strips the ‘A’ from actors.

SO if your project is called Watsom. Then you have a Watsom.build.cs file. And c++ classes in that Project are refered to in the ini as [Script/Watsom.]

or

[Script/Watsom.SensorPlacementConfigData]

then you can access the members of the CDO for USensorPlacementConfigData in the ini by name. Seperate items with a comma, and use () to go down into a member definition.

so your ini should read:

 [Script/Watsom.SensorPlacementConfigData]
+sensors=(rot_x=0,  rot_y=0, rot_z=0, rot_w=0, scale_x=0, scale_y=0, scale_z=0, trans_x=0, trans_y=0,  /
 trans_z=100,  type="USensorPlacementConfigData::SensorType::RADAR")

note the ‘/’ at the end of line 1 this tells unreal that the assignment will continue on the next line.

You can add a line +sensors=… for each sensor you want to add to the array.

Alternatively you can populate the entire array at once with :

sensors=(                              /
(rot_x=0,  rot_y=0, rot_z=0, rot_w=0, scale_x=0, scale_y=0, scale_z=0, trans_x=0, trans_y=0,  /
 trans_z=100,  type="USensorPlacementConfigData::SensorType::RADAR"),                             /
(rot_x=0,  rot_y=0, rot_z=0, rot_w=0, scale_x=0, scale_y=0, scale_z=0, trans_x=0, trans_y=0,  /
 trans_z=100,  type="USensorPlacementConfigData::SensorType::RADAR")                             /
)

With each wrapped in () and separated by a comma.

So I updated the config to be

[Script/<WATOSIM_API>.SensorPlacementConfigData]
rot_x=0, /
rot_y=0, /
rot_z=0, /
rot_w=0, /
scale_x=0, /
scale_y=0, /
scale_z=0, /
trans_x=0, /
trans_y=0, /
trans_z=100, /
type="USensorPlacementConfigData::SensorType::RADAR"

but i do not see the value 100 being printed out

Also I have a config file called DefaultSensorMountedVehicle.ini . I would assumethis would be used in the blueprint, however it seems to not be used. Not sure why that is.

[Script/<WATOSIM_API>.SensorMountedVehicle]
+sensors=(    /
	rot_x=0,  /
	rot_y=0,  /
	rot_z=0,  /
	rot_w=0,  /
	scale_x=0, /
	scale_y=0, /
	scale_z=0, /
	trans_x=0, /
	trans_y=0, /
	trans_z=100, / 
	type="USensorPlacementConfigData::SensorType::RADAR" /
)

You should probably read the whole thing.

lose the "<" and the ">" that could not possibly be a valid file name "<WATSOM_API>.build.cs"

I was using because WATOSIM_API appears in generated class headers

class WATOSIM_API ASensorMountedVehicle : public AWheeledVehicle

So and I saw a tutorial which used <>. I tried without the <>. the build file is called WatoSim.build.cs, I tried WatoSim as well which did not work.

So “”, “WATOSIM_API”, “WatoSim” all haven’t worked