NodeMemory things

i wanted to ask about how we using node memory…
when we use standard cast?? GetNodeMemory?? GetSpecialNodeMemory?? and i don’t know why in some decorator (e.g BTDecorator_Cooldown) we use reinterpret_cast???

All runtime variables should be placed in NodeMemory block. None of base classes use it (task, decorator, service), so you don’t have to worry about allocations from parent class.

Since it’s being stored as continuous block of bytes, it’s best to use regular C cast or reinterpret. Of course this is potentially dangerous, but usually allocations are limited to single class which makes it easily manageable.

If your node requires complex data types (containers with dynamic allocations) please consider changing it to instanced one: set bCreateNodeInstance in constructor. Those nodes don’t use memory block, but can operate on class properties/variables instead.

Special memory block is special, and sole purpose of its existence is hiding internal engine-level mechanics from users. Please don’t use it unless you really have to and keep in mind to use proper struct hierarchy, so memory layout stays the same.

Thanks a lot. And I have another question.
WrappedOnBecomeRelevant() in UBTAuxiliaryNode:
const UBTNode* NodeOb = HasInstance() ? GetNodeInstance(OwnerComp, NodeMemory) : this;
Why need GetNodeInstance()? if node is intanced, why not use " this " pointer?

You need to find node object, because all wrapped handlers are called on tree “skeleton”, not the actual instanced nodes. Their whole purpose is to redirect control flow to correct object and let it pick up from there.

In most cases, nodes are not instanced and all functions are called on “skeleton” nodes - shared between all instances of that behavior tree (e.g. all AI running the same behavior). They have some limited memory capacity for storing simple variables, but that’s it.

That model simply trades flexibility for low memory usage.

1 Like