In a recent update (potentially the last one?) focus_interface.MaintainFocus no longer “Will never complete unless interrupted.”, this means that it won’t maintain focus at all, but more importantly it breaks race conditions early that it wouldn’t have before. This has broken a lot of NPC logic in one of behaviours as it now exits the race early causing my NPCs to not attack players at all.
Also, this isn’t just for new versions inside UEFN, this is live in public/private versions that haven’t been updated at all, I’ve checked my oldest from release from 21st December and that is now broken as well.
Please select what you are reporting on:
Verse
What Type of Bug are you experiencing?
AI
Steps to Reproduce
Use .MaintainFocus inside a race along with another suspends function that should take time. E.g.
race
{
block
{
FocusInterface.MaintainFocus(Target)
Print("Should never reach here") # printed every tick in console
}
Sleep(1000.0)
}
Expected Result
The function .MaintainFocus should never complete.
Confirming that I also see this behavior. I imagine MaintainFocus is a pretty common call to rely on in race conditions, so this could be breaking a lot of maps. As a workaround I’ve wrapped MaintainFocus in an infinite loop. NPC’s appear to still be focusing.
I was seeing this using an NPC Spawner Device. The maintain focus call was inside of a custom NPC verse behavior script. There is an NPC character definition blueprint set to use the verse behavior, and the spawner set to use the blueprint.
Interesting… I extended my NPC behavior a bit, so maybe there’s an edge case somewhere in there. I pasted some semi-cleaned up code below, but the gist is:
I’ve added an interface (team_targetable_v2) to leverage a method, TargetTeam.
Instead of telling the NPC what to do inside of OnBegin when it’s created, I first let it create itself, then use TargetTeam in some other code to tell the NPC what to do
Other NPC behaviors will extend creep_behavior_default to override “TargetReached”
The NPC will go through some logic of prioritizing who to walk towards, which results in “ValidTarget”
In the code below you can see my workaround for the MaintainFocus finishing by adding a loop around it. Previously I just had MaintainFocus outside of the block on its own. I verified this was breaking by putting a print before and after MaintainFocus.
creep_behavior_default := class(npc_behavior, team_targetable_v2):
@editable
MyReachDistance : float = 500.0
@editable
MyMovementType : NPCMovementTypes = NPCMovementTypes.Run
@editable
MyMovementSpeedMultiplier : float = 1.0
var NPCUtilities : npc_utilities = npc_utilities{}
TargetTeam<override>(AttackLane : Lane, ?ForceAttackPlayers : logic = false)<suspends> : void =
if:
# Get the Agent (this NPC).
Agent := GetAgent[]
Character := Agent.GetFortCharacter[]
Navigatable := Character.GetNavigatable[]
Focus := Character.GetFocusInterface[]
then:
loop:
# If the NPC died then break the loop
if (not Character.IsActive[]):
break
# If we are overriding to target a player only, then choose the player. Otherwise follow
# standard logic.
MaybeClosestTarget :=
if (ForceAttackPlayers?, PlayerInLane := AttackLane.GetPlayersInLane()[0]):
option{PlayerInLane}
else:
NPCUtilities.GetClosestCreepTarget(AttackLane, Self)
if:
ValidTarget := MaybeClosestTarget?
TargetDistance := NPCUtilities.CheckAgentDistance(ValidTarget, Self)
TargetDistance > MyReachDistance
then:
var CreepReachedTarget : logic = false
race:
# Keep looking at the target
block:
loop:
Focus.MaintainFocus(ValidTarget)
Sleep(0.1)
block:
# If the target we're navigating to dies, then stop the race
loop:
Sleep(0.2)
if (not ValidTarget.GetFortCharacter[].IsActive[]):
break
block:
# If we were targeting a player but there's now a guard, re-evaluate
if (not ForceAttackPlayers?, IsPlayer := player[ValidTarget]):
loop:
Sleep(2.0)
MaybeNewTarget := NPCUtilities.GetClosestCreepTarget(AttackLane, Self)
if (NewTarget := MaybeNewTarget?, NewTarget <> ValidTarget):
break
else:
# If we are already chasing a guard then let this thread wait
# This should never win the race.
Sleep(Inf)
block:
#Create a navigation target from ValidTarget
NavTarget := MakeNavigationTarget(ValidTarget)
ThisMovementType := if (MyMovementType = NPCMovementTypes.Run) then movement_types.Running else movement_types.Walking
# Tell the NPC to go to this spot within MyReachDistance. Method will wait here until we get a result.
NavResultGoTo := Navigatable.NavigateTo(NavTarget, ?MovementType:=ThisMovementType, ?ReachRadius := MyReachDistance)
if (NavResultGoTo <> navigation_result.Reached):
# [Some logic here to try a backup for navigating]
else:
set CreepReachedTarget = true
# If our race ended with reaching the target, make sure the creep has time to finish the TargetReached logic before restarting.
if (CreepReachedTarget?):
TargetReached(ValidTarget)
# Sleep for the overall loop
Sleep(1.0)
# Override to perform custom behavior in subclass when we reached InTarget based in MyReachDistance.
TargetReached(InTarget : agent)<suspends> : void=
block:
Unfortunately I still reproduce the issue with the MaintainFocus block at the end of the race. I tried both putting it in its own block, as well as just putting it as a single line inside of the race.
I think that seems to have stopped it from finishing early, but my animations aren’t running, so unsure if that’s still the same thing affecting it. I’ll investigate further on my side.
*animations that are meant to run while it’s maintaining focus
Yep, I mentioned the island code in the ticket, it happens in my current public version and past private versions (that used to work fine).
I’ve just double checked and it definitely fixes it by moving the MaintainFocus to the last block in the race, however as I said before animations seem to not be running while maintaining focus, which wasn’t the case before.
You cannot play an animation and focus at the same time. The animation is considered as a full body action so it will interrupt the play animation
It seems that this was not the case before 33.20 and I can’t see why…
Just to clarify, are you saying that before 33.20 it was possible to play an animation at the sametime, but it shouldn’t have been? (makes sense anyway)
The PlayAnimation wasn’t included yeah, it was part of the logic I had in place of the Sleep(1000.0).
I need to get an old build to validate this…
But yes, on our side, we abort the focus when an animation is played.
If it was possible before 33.20 it was a bug (but I understand that it sounds as a regression)
Oh I might’ve miss lead you there, I mean as in the animation would play while the MaintainFocus was still running, as you say it would abort the focus and play the animation. Previously I can’t remember whether it maintained focus while animating (from what you say that’s not possible anyway). But yeah with the fix you mentioned, it maintains focus, but any animations played won’t play and the focus keeps running.
Thanks so much for helping, I really appreciate the quick responses!
No problem, but you should be able to play an anim, and it should abort the focus.
We have a few layers: navigation, stance, upper body, focus, and actions are using one or may layers, and you can only have one action per layer. Focus is focus, play animation is all of them, so they are exclusive (but not focus and navigate)