Repeatedly Casting in TickNode?

When working through this lecture one of the things I spotted was that we are doing this;

	PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
	AIController = Cast<AShooterAIController>(OwnerComp.GetAIOwner());

over and over again in the TickNode. Whilst I appreciate Sam mentions that the frequency of this tick was less that the frame tick, it still felt like it was unncessary to repeatedly run those lines of code.

I tried to be clever. I failed. :smiley:

So, in both cases I created a private variable in the header file, and the went about setting them once before they were then used within the TickNode.

I tried the Constructor as a place to put them, although thereā€™s a little warning about the GetWorld() (and other) virtual methods being called at compile time, and obviously I needed the Owner.Comp which wasnā€™t availabe in the constructor - so I figured that wasnā€™t going to work.

I then discovered the OnBecomeRelevant method. The documentation suggests that it is called when the node becomes active, which kinda sounded like what I wanted, e.g just fire this once, or at least only once each time that node become active. That didnā€™t work either.

In each case, both variables = nullptr, so when TickNode runs, the desired code/outcome doesnā€™t occur.

It strikes me there must be a way to this but fumbling around the documentation and Google searches Iā€™ve not found anything yet.

Any ideas? Would be interested so as to extend my learning a bit here.

I donā€™t think there is such a thing, however.

  1. Dynamic casting Unreal isnā€™t the same as in standard C++ which uses Runtime Type Information (RTTI) which Unreal has disabled. Unreal has its own implementation that uses its reflection system which is more efficient; whilst not free it should be insignificant and Iā€™d be surprised if itā€™s ever the cause of any performance problems.

  2. UGameplayStatics::GetPlayerPawn(GetWorld(), 0) is essentially returning the first element of an array with a few extra steps, all of which I doubt would be of any performance consideration.


With that said and the usual caveat of profile before doing anything in the name of performance

You could store the Player in the AI controller and provide a getter. Also since you know the AI controller is always an AShooterAIController you could also use CastChecked<> instead which does the checking for you and will crash with a fatal log if the argument is ever nullptr or the cast fails then in non-development builds is just a static cast. So you would end up with

AShooterAIController* AIController = CastChecked<AShooterAIController>(OwnerComp.GetAIOwner());
AShooterCharacter* Player = AIController->GetPlayer();

(You can also control whether CastChecked checks if the argument is null or not, by default it does)

2 Likes

You could just do a null check on the AIController var and if itā€™s null cast the ai owner and set it; then it will only do it once. I think you have to set in the constructor the bOnBecomeRelevant to true or it wonā€™t call it. I had this same problem. I havenā€™t messed with this code in awhile, but I remember theyā€™re comments stating certain functions wonā€™t be called unless ā€œtoggled onā€.

1 Like

Thanks @DanM and @Phillip_Wood for your replies, appreicated. Some things here for me to check out.

Iā€™m not yet fully clued up on the performance side of things, and certainly havenā€™t yet used any profiling in Unreal Engine. Fully agree that for something like this the repeated casting probably doesnā€™t make much difference, but having been typically avoiding that kind of thing in Blueprint previously (e.g. Iā€™d not cast within Tick) if felt a bit odd to then be ok with doing it here.

I also wondered whether storing what was needed in the actual Blackboard would have been an option too, as from what Iā€™ve read, it seemed to suggest that the services were typically/often used to just update values on the Blackboard.

The CastChecked<> isnā€™t something Iā€™ve seen before, so thanks for that, Iā€™ll have a look at it.

Also, Phillip, thanks for the bOnBecomeRelevant info, I might give that a whirl also just to see if I could get that function to be called and set the pointers as Iā€™d initially tried - just for giggles :slight_smile:

Interesting Dan about the reflection stuff, maybe one of those urban myths kinda things, but when Iā€™ve first starting learning .Net all those years ago, I recall conversations about how reflection should typically be avoided, and ironically again for performance reasons. Didnā€™t know enough then either to know whether that was sound advice or just ā€œstuffā€ being passed on like whispers etc. Inevitably I guess profiling would be the best way to get some concrete info to work with but I donā€™t think Iā€™ve had a project yet thatā€™s really big enough to justify it. Might still be handy to know how to do the profiling in due course though.

Anyhoo - rambling now - thanks again both, lots to take away and work with and learn more about :slight_smile:

Well it would be slower in BP due to the nature of it being in a VM etc. though at the same time I kinda doubt it would be so bad it would be a bottleneck.
The main thing to watch out for is casting to a type with hard references to assets which can unnecessarily increase memory footprint.
Iā€™ve only seen this mentioned regarding casting in blueprint so not sure if the same applies to casting in C++; the issue being that casting to a class which has hard references to assets would cause those assets to load even if the cast fails. However the C++ class doesnā€™t have any references to assets, those were all set in the blueprint derived class.

1 Like

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms