Me again, still trying to get a decent grasp of unreal c++ programming.
I’m trying to get different uproperties to work as I intend them to, but I often encounter strange and unexpected behavior.
Fist of all, I was doing some custom UObjects, and I found out through this forum that if I wanted to create objects of this type through the editors property drop down menu, I had to add Instanced to it’s property. That works (sort of, I’m still trying to resolve some problems). On the other hand, without using Instanced parameter I still get a drop down, my c++ classes are not on it, but blueprint stuff and actor components show up on it. Also, with Instanced, drop down list looks different, having items shown with white background and without it, items are shown with black background.
Second, as far as I have figured out by studying some examples, using TSubclassOf<Class> instead of a raw pointer gives me a drop down that does not instance a new object, but just lets me select a type of the class. For example in such a manner I choose a UI widget in the Battery collector example. On the other hand, when working with particle system it seems for some reason I can’t follow the same logic as by using TSubclassOf<UParticleSystem> gives me a list with just UParticleSystem class, while derived blueprints I created for particular effects aren’t listed.
I have a feeling I’m missing some essential unreal concept here, so I’d be very grateful if anyone could help me figure out why things behave as they do. Thanx
Not enough data. You need to tell how are you trying to create them, from which menu, and preferably provide some code. It is really unclear what you’re trying to do at the moment.
Why would you want to do that?
Particle system is created in editor through context menu->create particle system. You double click it and edit it in particle system editor. The resulting particle system is then referenced from UParticleSystemComponent. As UParticleSystem*, without TSubClassOf. I don’t think there’s any reason to try to derive from it?
IIRC It stores “reference” or pointer to object’s UClass. The one that is returned from ::StaticClass() method. You usually pass that into SpawnActor, NewObject, etc.
Also, you can extract object from that. Call GetDefaultObject(). You probably don’t want to do that with actors, components, and such. Those must be spawned or created.
Let’s say I have a class TopClass and class BottomClass, where TopClass references BottomClass and it also references some component in different ways.
#pragma once
#include "TestClassTop.generated.h"
/**
* Class for testing. This will be the upper class
*/
UCLASS(Blueprintable, editinlinenew, BlueprintType, ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class FEATURESTETS_API UTestClassTop : public UObject
{
GENERATED_BODY()
public:
UTestClassTop(const FObjectInitializer& ObjectInitializer);
~UTestClassTop();
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StatPropperties")
class UTestClassBottom* RawPointerNoInstanced;
UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = "StatPropperties")
class UTestClassBottom* RawPointerInstanced;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StatPropperties")
TSubclassOf <class UTestClassBottom> Tsubclass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StatProppertiesComponent")
class UMeshComponent* RawPointerComponentNoInstanced;
UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = "StatProppertiesComponent")
class UMeshComponent* RawPointerComponentInstanced;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StatProppertiesComponent")
TSubclassOf <class UMeshComponent> TsubclassComponent;
};
#pragma once
#include "TestClassBottom.generated.h"
/**
* Class for testing. This will be the upper class
*/
UCLASS(Blueprintable, editinlinenew, BlueprintType, ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class FEATURESTETS_API UTestClassBottom : public UObject
{
GENERATED_BODY()
public:
UTestClassBottom(const FObjectInitializer& ObjectInitializer);
~UTestClassBottom();
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BottomTestProperties")
FName TestName;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "BottomTestProperties")
float TestValue;
};
Now if I open propperties of different types from the editor like so:
I get an empty list. When I use the Instanced argument, the list is populated as expected. I thought it was just that without the instanced argument it searches for the objects that are created by the editor while the one with the instanced property creates new object of type offered through the list. But then I realized that for component referencing properties both ones that contain instanced and those that don’t work seemingly the same, both showing the full list of components.
I have seen the documentation for “Instanced” you have linked, but it isn’t very informative. What do they mean by “object assigned to this variable in defaults”?
Also, when I have a non instanced property that is not null by default but has an object created in the class constructor, I can see the object, but can’t access any of it’s properties. Is it just something you don’t do in unreal engine, or can it be made to work somehow?
Aha, I’m starting to understand. So when creating a particle system in the editor I’m creating an object, not a new class, and therefore non instanced property sees that object, but instanced one does not see any classes it can use as a base to create new items. Am I corect? And the TSubClassOf is a templated err something? that holds references to UClasses that are basically just somethingused by the reflection system to keep data about properties each object type contains. Am I right there also?
First off, it is complicated. I wouldn’t suggest trying to understand in too much detail how it all works, just in order to make use of it.
So, a few things.
Components get special treatment. There is a whole lot of code, in-game and in-editor, that is written for dealing specifically with components. For one thing, all components are Instanced by default, even if you don’t mark them as such. Further, there are special customizations for displaying components within the details panel. That enables you to edit properties of default components, but you can’t do the same for regular objects. Currently that can only be done if they are Instanced, which gives you the rather different looking inline expansion. I think this is something that could be improved, to support for all UObject properties many of the things that currently are supported only for components.
A note about Instanced. It’s a bit of a confusing name, in that even object properties that are not marked as such, will still point to an object instance (when not null). Essentially, the difference is in how they are treated within the editor/details panel, along with how they are treated for object duplication purposes - instanced properties will mean that a new copy of the object gets created whenever its parent is duplicated, whereas with regular properties, a duplicated parent object will just end up with a pointer to the same subobject instance as the object that it was duplicated from. So, Instanced results in deep copying.
The dropdown for an Instanced property gives you a list of classes, but this is because it’s saying ‘I’m about to make a new object instance, tell me which exact class you want me to make it’.
TSubclassOf is just a typesafe wrapper; essentially it’s the same as defining a property of type UClass*, it just limits the hierarchy of classes that can be assigned to it. As you say, it stores a class, as opposed to a regular object instance.
The one other thing that comes into this, and can get quite confusing, is assets. An asset is, for the most part, just an instance of a particular UObject type, that exists (and persists) at design time and is listed in the content browser. So a particle system you make is an object instance of type UParticleSystem. It does not represent a new type of class, hence it won’t show up in the list of a TSubclassOf< UParticleSystem >. The confusion comes with blueprints. A blueprint is actually an asset which is an object of type UBlueprint, but it represents a new class, of the same name as the blueprint. So blueprints (or rather the classes generated from them) will show up in the dropdown lists for TSubclassOf properties.
So finally, going back to regular, non-Instanced UObject properties - this dropdown is asking you to select an existing object instance. At design time, this is essentially (with the exception of actor classes) any assets you have of the given type. If there are no assets, then the list will be empty.
Actually the blueprint asset doesn’t represent its class; it has serialized code which builds up the class when loaded.
If you have MyBlueprintClass blueprint, the class it contains is by default same name and stored inside the asset, named then something like ‘MyBlueprintClass.MyBlueprintClass_C’, the real raw class name.