Trying to create instanced list of blueprints, subclassed from C++ class, no duplicates

Markusb123
Wed, 22 Nov 2023 16:25:18 +0000
C++

Hi everyone

I’m trying to set up a system (a Utility AI system) to allow users to create Blueprint classes from a C++ base class which itself inherits from UObject. These Blueprints are to be stored in a list (either TSet or TArray) in a Component with no duplicate entries and which allow inline editing of a property within the child Blueprint such that each will have it’s own value for this property for different instances of the owning component. I also want to ensure that only child classes of the base class are listed in the editor when populating the list which is why I’m trying to use class types and the abstract class specifier in the UCLASS macro.

The problem is that I am really not sure how to achieve this and I clearly am not understanding how class variables, as in TSubclassOf, and pointers to instances relate to each other. I have done research and I understand the TSubclassOf is a class template, not an instance, and that it can be used to create an instance via NewObject or GetDefaultObject() and returns a pointer to that instance.

I had assumed that I would create a set (TSet) of TSubclassOf class types and use them to populate an array (TArray) of pointers to the base class using NewObject in BeginPlay. This works if I don’t use instancing but if I try to used instanced in the UPROPERTY macro for the class types I get an error, not surprisingly and if I put the pointers to the instances in the set I can put in duplicates of the class type, again not suprisingly as they are pointing to different places in memory. I have created a small test project to experiment and tried a few variations of TSet, Instanced, Blueprintable and EditInlineNew but I can get close to the desired behaviour but never quite there. I’ve attached my test code and examples of the results.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyTestCPPUObject.generated.h"

/**
 *
 */
UCLASS(DefaultToInstanced, Blueprintable, Abstract, EditInlineNew)
class MYUCLASSTESTPROJECT_API UMyTestCPPUObject : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	float AFloatProperty = 0;
};

Base class for Blueprint child objects

#include "MyTestCPPUObject.h"
#include "MyUPropertyTestActor.generated.h"

UCLASS()
class MYUCLASSTESTPROJECT_API AMyUPropertyTestActor : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AMyUPropertyTestActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// class template varaibles?
	UPROPERTY()
	TSet<TSubclassOf<UMyTestCPPUObject>> myTestClasses;
	// the pointers to the actual instances?
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Instanced)
	TArray<UMyTestCPPUObject*> myTestInstancees;
};

Test Actor storing list of blueprint child objects

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyUPropertyTestActor.h"

// Sets default values
AMyUPropertyTestActor::AMyUPropertyTestActor()
{
	PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AMyUPropertyTestActor::BeginPlay()
{
	Super::BeginPlay();

	// Create instances for use in game, though not sure if I need to do this
	// if they are instanced, how else to create the objects though?
	for (auto testClass : myTestClasses)
	{
		UMyTestCPPUObject* newInstance = NewObject<UMyTestCPPUObject>(testClass);

		if (newInstance)
		{
			myTestInstancees.Add(newInstance);
		}

	}

}

// Called every frame
void AMyUPropertyTestActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

Test Actor with BeginPlay creating instances. Edited this too as I established that looping through the myTestClasses set does nothing, as I should have known, so the instances are solely derived from the Blueprint assets

I created two instances of my test Actor and gave them both an instance of the same Blueprint asset and was able to give them different values for thier property which worked, but currently I can also add duplicate entries, that is, of the same child class type as you can see below.

Test Actor 1 with correct editable list of Blueprint instances

Test Actor 2 with correct editable list of Blueprint instances

The child Blueprint, this inherits from UClass and has only on float property which is editable anywhere

Different Actor instances printing out the respective values of thier copies of the child Blueprint, correctly

Test Actor 1 with duplicate entry in list of Blueprint instances, undesired behaviour (edited as I highlighted the name of the base class type, not the child)

Test Actor with duplicated Blueprint output, undesired behaviour.

Any suggestions for how to achieve this would be great and an explanation of how class types and instances work together would also be really helpful.

6 posts - 3 participants