Hey, so I’m still in the process of learning C++ and recently learned to clean up a lot of my memory allocation by using static in order to create a shared variable for all instances of my objects.
Currently all my NPCs and enemies have their own variable holding the the player pawn. However, I obviously only need one universal reference, since the player is the player pawn and it never changes in my game. (and it’s single player)
So I tried making my variable static, and declaring at the top of the .cpp file. static AWSPlayer *Player; AWSPlayer* AWSNPC::Player;
The first entity to spawn gets and sets the player variable:
void AWSNPC::BeginPlay()
{
Super::BeginPlay();
if (!Player)
Player = Cast<AWSPlayer>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));
This works perfectly fine the first time I run the game. All entities use the variable as expected.
However, it appears that after closing the game in editor or changing maps, my player doesn’t appear to get emptied, and it refers to my previous value, causing a crash as that version of the player no longer exists. How do I make sure it always sets the Player variable, but only once?
Ah, I’ve been learning general C++, I’m passingly aware of garbage collection but not enough to realize the engine is already handling these kinds of things.
Another piece of advice would be start getting used to the Smart Pointer Library, it will get you out of trouble ahead, so get used on how to use it and also it is a very good idea to check some engine source code for plugins, it will be a great benefit for you.
UObjects are managed objects, so by the time the smart pointer tries to destroy the object, the garbage collector has already destroyed it.
If you’re using UObjects you need to use the built-in ways to create/destroy them, that’s NewObject<>() and CreateDefaultSubobject<>().
For non-UObject types, you can use new/delete, smart pointers etc. and that’s fine.
Oh, the object I’m trying to create a shared pointer to is my Player’s pawn. I’m not trying to make a new UObject. Is maybe what I need a weak pointer instead since it already exists? I wasn’t able to figure out how to assign it though.
Basically I’m just trying to figure out how to make universal pointers to unique instances, instead of allocating memory to every single object that needs a reference to them.
I think you need to check through engine source code and realize that several classes are subclasses of UObject, which includes Pawn. When you need a reference to an object (which is alive) you are only having a pointer and that’s it, you are not allocating memory more than the enough to handle a memory address.
What you can do is just hold the refence inside any object that needs it. The info passed by Bolobolo is accurate.
A pointer is 8 bytes of memory. Using 130.000 unique pointers adds one MB of additional memory to your game. My advice: Do youself a favor and abandon that task. The gains don’t justify the amount of work you’re putting in and have already put in.
Why would you use TWeakObjectPtr in an actor class? Sure in a struct or some class which is not reflected. But there is 0 reason to use it in an Actor class. A UPROPERTY() raw pointer to an actor inside a reflected class is more than enough.
Why not, smart pointers are very powerful and are used throughout the engine, take for example FHitResult you get from doing a line trace, it has a bunch of TWeakObjectPtr variables, including the Actor hit by the trace is also a TWeakObjectPtr.
It has additional cost overhead, and it is not needed in this case.
Weakpointers only prevent an object from being reference counted. Actors are different, and they get explicitly destroyed by the world and the object is marked pending kill. Raw pointers will not keep this object alive. If you are holding a pointer to an Actor inside another another actor/actorcomponent, then weak pointer is not needed.
The property must be UPROPERTY so the reflection system and GC can see that property. But the GC will turn that property nullptr when the actor is destroyed. Before the actor is actually fully destroyed, it will be marked pending kill, which is why you should always use the IsValid global for checking object pointers, as this also checks PendingKill.
I just feel like TWeakObjectPtr in this case is not required.
In this case it might not be but still, it’s good to know that other options exist.
TWeakObjectPtr IsValid() function can also return false if the actor is pending kill.