Download

NewObject using a Blueprint class?

I made a C++ class derived from ActorComponent (“BaseSkill”) and then some C++ classes derived from BaseSkill (e.g. JumpSkill). Then I made blueprint classes from those (e.g. BP_JumpSkill). Then I loaded them up into an array of CharacterSkills in the Character blueprint. I want to be able to create those skills based on the index of the CharacterSkills array.

.h


UPROPERTY(EditAnywhere, Category = "Player")
TArray<TSubclassOf<UBaseSkill>> CharacterSkills;

.cpp


UBaseSkill* MySkill = NewObject<UBaseSkill>(this, CharacterSkills[Index], TEXT("Skill"));

This compiles but crashes the game when it’s run.

If I chose the skill C++ class manually then it works fine, but obviously this just spawns the same one every time.


JumpSkill = NewObject<USkillJumper>(this, USkillJump::StaticClass(), TEXT("Jump skill"));

I’m assuming it’s something to do with BP class references, the StaticClass() bit, etc. But I just can’t work it out. Any thoughts?

Cheers

Can you post the crash log?

I would do it like so:



UBaseSkill* MySkill = NewObject<UBaseSkill>(this, CharacterSkills[Index]->GetFName(), CharacterSkills[Index]->GetDefaultObject(), true);


Also, where are you calling this from ? Are you calling from a class that derives from UObject directly? Because “this” won’t work as it needs a world context(correct me if I’m wrong) object, so if you are calling this from a class derived directly from UObject, then I would do:



UBaseSkill* MySkill = NewObject<UBaseSkill>(this->GetOuter(), CharacterSkills[Index]->GetFName(), CharacterSkills[Index]->GetDefaultObject(), true);


Sorry if the code is a bit off. I am typing this on my phone.

Thanks Aaron. I’m calling all this from my ABaseCharacter which derives from ACharacter.

I just had to add the RF_NoFlags part to your corrected code, but otherwise it works perfectly. Thank you very much :slight_smile:


UBaseSkill* MySkill = NewObject<UBaseSkill>(this, CharacterSkills[Index]->GetFName(), RF_NoFlags, CharacterSkills[Index]->GetDefaultObject(), true);

Yes, it’s something I forgot to add when writing the post haha Good pickup

I would also use NAME_None instead of GetFName(), to let the engine generate a unique name for each instance.

Correct me if I’m wrong, but by no longer using CharacterSkills[index] in NewObject<>()

I believe this will only create a UBaseSkill object and assign it the name of your BP, not creating the actual BP class.

TSubclassOf is a pointer to a class, which in this instance specifies what subclass of UBaseSkill you would like to create.

An example from my own project:
[SPOILER]



UAITask_Base& UAITasksManager::CreateTask(TSubclassOf<UAITask_Base> inClass, EAITaskPriority priority, bool activate, FName name, ETask inTaskContext)
{
    UAITask_Base* task = NewObject<UAITask_Base>(this, inClass);
    task->SetInstanceName(name);
    task->InitAITask(*owner->GetThisAIController(), *this, (uint8)priority);

    //We need to initialize task here as well since this can be used to create task outside of manager
    //If CreateTask() is called by manager, then this functionality will be overridden
    task->InitializeTask(owner, owner->currentManager, inTaskContext);

//    UE_LOG(LogTemp, Log, TEXT("%s is creating a task of %s, address is %d. AITasksManager::CreateTask()"), GetOwner() ? *GetOwner()->GetName() : TEXT("Owner Unknown"), inClass ? *inClass->GetName() : TEXT("class Unknown"), task);
//    UE_LOG(LogTemp, Log, TEXT("Task class is %s. AITasksManager::CreateTask()"), task ? *task->GetName() : TEXT("task Unknown"));

    if (activate)
    {
        task->VisualLogAndLog(FString::Printf( TEXT("%s is activating a task of %s. AITasksManager::CreateTask()"), GetOwner() ? *GetOwner()->GetName() : TEXT("Owner Unknown"), inClass ? *inClass->GetName() : TEXT("class Unknown")));

        AddTaskReadyForActivation(*task);
    }

    return *task;
}


[/SPOILER]

Has the OP verified the objects created are of the actual BP, and not just using the names?

I replied earlier thinking this was okay, but you are correct. It’s creating a UBaseSkill and not the BP of the subclass entered into the TArray.

What would be the best way to tell it to create the component of the BP?

There is an overloaded version of NewObject<> where it asks for a UClass* together with a template object.

You have to pass to it both the CharacterSkills[Index] class and CharacterSkills[Index]->GetDefaultObject() to spawn from Blueprint.

Thanks a lot for your reply. I’ll try this.

For clarity:



UBaseSkill* MySkill = NewObject<UBaseSkill>(this, CharacterSkills[Index], NAME_None, RF_NoFlags, CharacterSkills[Index]->GetDefaultObject(), true)


I am having issues after upgrading to 4.24 latest. There are checks now that see if the class being created is the same as the class type passed in. They are not. One is the native c++ class, the other is a UBlueprintGeneratedClass… And so it fails. A Blueprint is not a UMyClass.

Old Posts from epic indicate to use NewObject<UObject>(…) this also fails that UObject is abstract and cannot be used.

For whatever reason, the devs have broken this feature.

This does not work:

UObject* bgo = NewObject&lt;UObject&gt;(this, TEXT("SOPScript"), RF_Transient, SopScriptType.Get());

Neither does this:

UObject* bgo = NewObject&lt;UObject&gt;(SopScriptType);

However, this does work:

#include "Kismet/GameplayStatics.h"

UObject* bgo = UGameplayStatics::SpawnObject(SopScriptType, this);

Which simply calls this:

NewObject&lt;UObject&gt;(Outer, ObjectClass, NAME_None, RF_StrongRefOnFrame);

I hope this helps.

And one final note:

This also works fine:

NewObject&lt;UObject&gt;(this, SopScriptType, TEXT("SOPScript"), RF_Transient);

However This with the same parameters, but in the other order, does not:

NewObject&lt;UObject&gt;(this, TEXT("SOPScript"), RF_Transient, SopScriptType);

The second signature is broken.