Hello all! I’m well-versed in gameplay logic thanks to scripting/modding experience but NOT well-versed in some C++ and general good coding principles, so I wanted to see if some of you who actually learn things in effective ways could enlighten me a bit…I have two really important questions I need answered before moving forward and I’m having trouble finding anything in my research. I’ll summarize it at the end of my post as a TL;DR example. I’m also well-aware I just need a better foundation for my coding knowledge, but I have limited time and have to learn things on the fly for now. I try to keep my head down and work on stuff instead of trolling through the forums for answers to everything, so please don’t be bothered by the length of this post – it’s repressed things I’ve been trying to wrap my head around for months haha. So here we go:
1. Syntax or Sin-tax?
I’m quite fond of getting references to specific objects in this manner:
I’m of course referring specifically to the right side of that first line – the ‘((AActor*)TheActorI’mPointingTo’ bit. Is this casting? I am under the impression that this is just getting a direct reference to something I already have a hold of, or is this just nonsense I’m telling myself and it’s just another way of casting? **I am performing this operation VERY frequently, too – will that catch up to me in performance or memory usage? **And if it’s not casting, am I headed for disaster if the object I’m trying to assign a reference to ends up not being the class I put on the left side?
2. The Casting Connudrum
Okay, here’s number two – how do I go about casting efficiently? I’ve heard that ANYTHING you cast to at ANY point in your code will be held in memory if the casting actor is present in the world – is this true? If so, when is casting ever viable at all and not just a train-wreck eventually? For instance, I heard a story of someone referencing a boss enemy class with a cast deep in their player animation BP code, and because the player has access to the BP from the get-go, the memory for the boss was being allocated in the menu. I know I should be adhering to event-based scripting, but how do I do event-based scripting without casting in the case of, say, needing to cast to a vehicle class when a player overlaps it and I want them to possess it to provide input, or not even possess it and just call input functions on it? I think if someone were able to give me a concrete example of the best way to handle this situation, it would clear up a lot of my other misunderstandings****.
In this example, what should I do? Have a UInterface for all interactables in my game (it’s VR)? Or could I just do this with a universal ‘Use’ function in a parent ‘interactables’ class that they all derive from, and have that Use function triggered in each one with their correct response through a virtual void, like this?
The casting syntax you’ve listed is a C-style pointer cast, which functions as a “reinterpret cast”. In modern c++ you’d usually use reinterpret_cast<AMyCustomClass*>(Actor) instead, but this is unsafe unless you’re completely sure the types are compatible (if Actor is an AMyCustomClass or inherits from it). You can reinterpret-cast literally any pointer type to anything else, but only certain kinds of casts are meaningful - to take a simple case, doing a reinterpret cast from an int pointer to a float pointer is literally just saying “interpret the bits making up this int as bits in a float”, which is almost always nonsensical. Performance is a non-issue, this type of cast is free (it generates no instructions).
I don’t follow point 2. Casting doesn’t copy or allocate memory, it’s just saying “treat X as Y”.
I mentioned casting is unsafe - Unreal has a special casting function for UObjects (including AActors) which is very safe, but has a small performance cost. If you Cast<AMyCustomClass>(Actor), that cast will return nullptr if Actor isn’t an AMyCustomClass and doesn’t inherit it. In your second code block, the cast will always “succeed” in the sense it’ll give you a non-null pointer if OtherActor is non-null in the first place, but this is regardless of whether OtherActor is actually an AInteractableBase. If you’re unsure of what kind of thing an object is, this is a quick and easy way to find out at runtime. It’s equivalent to casting in blueprints.
Thanks so much. I had seen several posts indicating that casting irresponsibly as a habit could lead to memory leaks, but you’re confirming what I thought, that it made little to no sense…So using the syntax I listed is fine, but just not typical? Cool. I’ll probably start using the safe Unreal cast method, and I think I’m good to go then, this is much appreciated.
Yeah I don’t see how casting could directly lead to memory leaks. To be 100% clear, I wouldn’t recommend doing C-style pointer casting in general, especially not with UObjects/Actors. Unreal’s Cast<> is basically foolproof because it checks the types for you using Unreal’s reflection system - if you’re in any doubt at all about whether the object you’re looking at is the type you’re expecting (e.g. if it’s an actor passed in by some function param or event), use Unreal’s Cast function.
Casting is affecting memory management. If you have a character with 300 different types of weapons and you cast to each type of weapon from within the character then all 300 weapons has to be loaded when the character is loaded even if the character only possess 1 weapon at the time.
Casting to a common type like the character that the player is using 90% of the time is not so bad though so each weapon could cast to the character without a big loading issue issue since the character is most likely already in memory.
There are ways to avoid loading every weapon and still have a reference to them though which could be decoupling through interfaces, base classes (inheritance), composition or manage the loading yourself with weak pointers.
I don’t see Unreal’s C++ UObject-to-UObject casting function doing any loading - it does GetClass(), which expects the UClass to already be in memory, and then checks that Object->GetClass()->IsChildOf(TargetClass). IsChildOf just walks up the supers until it finds the target UClass (or not). Then it just returns the C-style reinterpret cast, or nullptr if there was no match. I’m not sure it’s even meaningful to have a UObject* pointer to an object that’s not loaded?
Makes sense. And to do this with interfaces would just involve casting to the interface directly with (OtherActor) following it?
Interesting. Seems like there’s not a consensus on this since I’ve heard a lot of different things regarding this from a lot of people. Can’t imagine people aren’t casting at all though so I’m sure the best approach will be to just do it within reason when necessary.