PlayMaker News > General Discussion

FSMBool.Value slow due to unboxing

(1/1)

toddprofanegames:
Hello,

In addition to my other post about heap allocations when PlayMakerFSM.Awake is called, I'm also seeing some pretty significant latency due to a frequently run condition which compares FsmBool.Value to a boolean flag, namely the IsTrue method in this custom class:


--- Code: --- 
[System.Serializable]
  public class BoolTestValue
  {
      [Tooltip("The test will return True if the testVariable has a True value.")]
      public bool TestTrue = true;

      [RequiredField]
      [UIHint(UIHint.Variable)]
      [Tooltip("The Bool variable to check for a True or False value.")]
      public FsmBool testVariable;

      public bool IsTrue()
      {
          return this.testVariable.Value == this.TestTrue;
      }
  }

--- End code ---

I've attached a screen shot of the profiler.  This is a worst-case scenario as it doesn't always run this slowly.  That said, if the FSMBool.Value was strongly typed as a boolean this wouldn't be happening.  Is there any alternative to calling FSMBool.Value and therefore NamesVariable.CastVariable which unboxes and runs slowly in a tight loop with a lot of pressure?

Please see the attached profiler screenshot for an example of what I'm dealing with here.

jeanfabre:
Hi,

 1 seconds is a lot, how many prefabs instances do you have running the same fsm?

instead of checking for this bool, can you not send an event about a change in this boolean state? that's usually better thant checking everyframe, especially in the game loop itself.

also, could it be that it's not the check that causes a lag but what you do after you've detected the change? maybe you play a sound that is not uncompressed already, or something like that?


Bye,

Jean

toddprofanegames:
Hi Jean,

Thanks for the reply.

There are 46 separate prefabs which represent enemies who are sitting in various places in the world.  The action that is producing this latency is iterating through an array of about 5 FSMBool's checking their combined states to determine if the enemy need to start seeking the player.  This action is the enemies idle state when they don't see the player and are just standing there, which is why so many of them run so frequently in the game loop.

These FSMBool's represent:
1. whether or not the enemy's line of sight of the player is occluded
2. whether or not the player is currently within a reachable part of the navigation mesh
3. whether or not the enemy is currently located in it's original spawn position or if it is standing somewhere else (to determine if it needs to find a path home to it's spawn position)
etc...

The logic which sets these flags can be very slow, so in some cases I'm using jobs to get calculate these values then storing them in the FSMBool's when they are finished calculating.

Typically iterating a small array using a for loop and checking boolean flags isn't a big deal in the game loop when the array elements are strongly typed, but in this case the unboxing seems to be causing an issue. 

Since my original post, I've already reworked this so that I'm still checking these FSMBool's , but I'm short-circuiting the checks to minimize the calls to FSMBool.Value now that I know it's problematic.

The reason I'm passing at this data in FSMBool's is because my AI logic uses combinations of these flags in various actions to decide how to proceed to the next state.  While I could change these flag to instead reside as class members of the enemy's C# script, it mean a large rewrite of a lot of complex logic and would be more difficult to debug as I couldn't see the value of the bools without attacking visual studio (which is very slow and cumbersome in my large project).

At any rate, my short-circuiting changes have mostly eliminated that bottleneck, so I'm on to the next issue anyway.

jeanfabre:
Hi,

uhm.. yeah I see, not ideal indeed. Using jobs for this is a stretch though...

Does all 46 enemies are close to the player? should you not use some monitor and only active these expensive calls,when the player and the enemy is close enough for it to be meaningful. that's how network games work. You can set it up very easily with checking the distance or simply use a trigger sphere, when triggered the ennemy is active with all these bools checks etc..., when not, it's inactive, just doing its thing from far.

that's the first thing to do here. then if the perfs are still problematic, look to improve the ennemy fsm themselves.

Bye,

Jean

toddprofanegames:
Actually one of the FSMBool flags is a value which represents whether the enemy is within range of the player based on distance to the player and angular distance from the enemies forward, so the enemies essentially have a view frustum.  This is computed every frame and assigned to the FSMBool, one of which is processed in this particular action.  As part of my rewrite I mentioned earlier, I am short-circuiting these FMSBool.Value tests so that the most likely false flags are checked first and then the following flags are skipped if a False is encountered. 

The unity Jobs logic actually processed navigation mesh pathfinding and just assigns a boolean result in the UI thread after the job is finished which indicates if the player or enemy is currently standing on a navigation mesh and on which node of the mesh they reside (which is needed for other logic).  So the boolean result is assigned to an FSMBool when it becomes available so that various FMS states can use the result to make decisions.

It's a complicated system but I've got it running pretty smoothely now.

Navigation

[0] Message Index

Go to full version