AI getting stuck, part 3

OK, continuing from yesterday’s short one and this morning’s ScreenShotSaturday tangent, let’s get on with it!

Ahha!  Got it.  Reproduction when I realized it only happened when the Behavior Tree Reference had to not only supply variable values but also get those values from bound variables!  (yeah, that’s kind’a complicated but it makes sense if you’re a Behavior Designer user.  Comment if you’d like an explanation.)  Right, time to package up and send my minimal reproduction case to Justin who, knowing him, has probably fixed it already in his usual paranormally prescient mien! (sorry, I should have mentioned that I’m an incorrigibly loquacious inkhorn 😛 )

Right, done.  Time to confirm his ‘fixed’ version doesn’t.  (Not expecting to since it didn’t in actual project.)

Nope, got myself a drink, a shave, a quick play with my kids and he’d fixed it in 30 minutes.  Seriously, are there any better asset developers than Justin at Opsive?

So no errors but no movement either.  Debugger!  Oh, not getting calls to my OnFixedUpdate() call again!  Did I lose my change to allow super-classes to be recognized as holding Behavior Designer callbacks again?  Think I need to feedback to Justin that his fix for this just doesn’t.  Hmm, restored my change but still doesn’t recognize as having OnFixedUpdate()?  There’s some change directly above.  Hm.  Ah, yep, he’s added System.Reflection.BindingFlags.DeclaredOnly which precludes inherited members!  Doh.  Let’s remove that and check.  Yup.  Feedback time (I supplied a patch for this previously but he went with a different solution which doesn’t seem to do the job I’d expect — not sure why).

OK, with that fixed, my PIDController works perfectly in the AIDev project — time to try it on an actual Snowman?  Oh, nope, forgot I was working through a list from part 1 of this endeavour.  So, repathing rate seems like the next part.  Then over to actual SnwScf project.

So, repathing.  Should it check whether the target position has changed or simply repath regardless?  It’d be more efficient not to repath if it’s not changed but perhaps a new path is required just for the seeker being in a new position?  Could make it an option but think will go with always repathing for now.   (YAGNI)

Done that, of course now need something to test it’s updating its path with!  Re-enabling my old waypoint following TargetBot for it to chase 🙂  Done.

Next to create a new BT to do chasing!  After dinner.  Roast chicken!  Yum!

After dinner, it didn’t take long to make this:


Works! Left branch is all merely finding the closest GameObject tagged “Target”. The right branch gets target position moves towards it and periodically repaths.

My only concern with it is one of efficiency: that Parallel doesn’t really need to do the left branch every frame — out really only needs to do that when when repathing is needed. Think I should probably leave as is for now since (a) it’s not a costly other branch and/or (b) optimized later if profiling says needed. Part of me wonders how behaviour trees most efficiently do a “when consumer needs it” operation? I find myself wondering whether one ends-up bastardising a Composite or a Decorator Task as the top level such that it can do part of its logic then indicate whether it’s child/children should run (then the rest of its logic).

Lost er, ‘invested’ the rest of the evening (after putting the boys to bed) playing Duck Game and Move or Die with a friend — online!  (think they both started as local multiplayer, Duck Game definitely — I originally played this on my Ouya!).  Both excellent!  Both on Steam Sale this weekend (and DG is free-weekend)!

More development tomorrow morning (before swimming with 1.5 year old in afternoon) and we should see the AI not getting stuck any more … well, unless it’s against another player since I’ve not integrated the A* Project’s RVO facility.  But the way it’s used means it should be OK so another YAGNI.


#ScreenShotSaturday: PID Controller rolling a Rigidbody along AStar under BT!?

Continuing from yesterday’s short one, today is Saturday so hope for a biggie with lots of awesomeness … and definitely not getting tangented creating a #ScreenShotSaturday since I did that earlier in the week 😛


The picture is the PID Controller successfully controlling the rolling Rigidbody along an A* path including a section ramped sideways to the direction of travel (to show it compensating for the disturbance caused).  Of course, the whole thing is under the control of a Behavior Designer behaviour tree.  The cyan line is the current move vector and red line that hovers, growing and twisting above it, is the force vector it’s trying to apply (along with a fading history).  The purple cross on the floor is its current waypoint along the path.  The yellow or green wire sphere is its range it will accept to that waypoint.  Sadly GifCam got some oddity on the third section of the path where the screen went all green but I think it’s still quite cool to watch 🙂

For context, this is the development view of my Snowman AI needing more health.  In Snowman Scuffle, the snowmen fuel the snowballs they throw with their own health = snow!  Obviously injury also results in their losing snow health.  To regain snow, Snowmen need to roll in some snow that’s appropriately smooth, deep, etc.  I have developed an A* path generator that determines where is ‘good snow’ (perhaps I’ll go into detail on that some time if I need to work on it more or by request).  Once the AI gets the path from that system, this aspect then is responsible for getting the Snowman rolling along it at a speed appropriate to gain the snow health it needs.

In fact, this is a blog post not a tweet, why don’t we push the boat out and have… another animation!


Here you can see (again in development view), the snowman shooting at a target dummy (which turns red when hit — I’ve since fixed the missing seen here).

And, when in-game, we get


(Note, most of my animated GIF lacks image effects that usually run — GIFs just don’t like all the extra colours, etc!)

Y’know what?  Per my “post more often” resolution, I’m gonna post this to link from my #ScreenShotSaturday tweet then continue my devlog post separately.

P.s. Hmm… so that whole “definitely not getting tangented creating a #ScreenShotSaturday” thing kind’a didn’t work, huh!  A morning of finding old screenshots, tweaking WordPress and creating a new header image.  Still, at least the devblog is semi public now!  Gulp.  Right, tunes on, fresh coffee in-hand — definitely back to applying the PIDController path-following logic to non-rolling … by first diagnosing that weird Behavior Designer bug!

AI getting stuck, part 2

A short session tonight continuing from the end of yesterday’s post, the main man Justin at Opsive replied in his usual, crazily responsive and helpful fashion with a test build that might fix the problem!

In my usual “I forked this code so am careful before bringing in changes” way, I extracted the unitypackage using my shell script then diff to the unforked version that my changes fork from, i.e. Behavior Designer 1.5.6.  Curiously, there was an extra directory “Scripts” with some bits in but otherwise, the Editor DLL has changed and a few files have changed so it’s probably easiest to patch those changes atop my forked version in-repo and Git Revert if there are problems (or go full backup if that fails).  This is only mildly complicated by my use of the excellent MAD Compile Time Optimizer asset which reduces compile times by moving source code between the different Unity special directories so they are not compiled as often due to being in different .Net Assemblies (mostly moving things from base to the “Standard Assets” / Plugins assemblies).  The usual hitch is that one must “Revert” its changes before updating assets… buuuuttt…. if you do things manually, this doesn’t apply so mad UNIX CLI skills to the rescue again B-)  (all those nights hacking away in shell were worth it in so many ways.)

Sadly, once I got his version in, it didn’t work.  Exception!  Fed back.
Tried for minimal reproduction but a few slight hiccups (also in thread).
Short version: no reproduction from minimal parts I thought were necessary … (ain’t it always the way).  Will continue to track down the crux tomorrow.

Is getting this working still worth it?  From point of view of AI dev for SnwScf, yes — this pattern (external behaviour tree with parameters included in multiple places) is a common one for me so it needs fixing.  From point of view of submitting to Develop Indie Showcase?  Less sure.  Will reassess tomorrow after some sleep … (and maybe determining the crux of the problem in my sleep 😉 ).

AI getting stuck

The Snowman AI gets stuck sometimes when moving tactically.  Perhaps switch to both A* path and PIDCtrl for all movement?  (background: they’re using direct movement in vector of desired movement at the moment).

Atm, roll has 2 big differences:

  1. It does 1 pathing then
  2. the move action can work the path until completion (by returning TaskStatus.Running).

In contrast, move & shoot continually:

  1. determines destination
  2. moves towards it.

In contrast, the AIPath supplied with A* project does its own periodic repathing by taking a destination point (that might be updated).
To use pathing, M&S would need to periodically:

  1. determine new destination (perhaps using pathing to determine ‘good’)
  2. path to destination

Probably a good time to investigate the Tactical Pack! (and updating it to A*.)

(a week passes where I work on updating Behavior Designer A* Integration pack from a start the author Justin began — see notes elsewhere.  Here’s the thread where we mooted the idea.)

So, Tactical Pack uses a single Task per tactic to do all of the work bar actual pathing which it delegates to the pathfinding solution’s default approach — for A*, the AIPath or RichAIPath.
As noted above, those have an updatable destination point and a repathing rate.

Perhaps we could do similarly by:

  • using MoveSnowmanWithPID
  • set inDoPathfinding
  • unset ‘useRoll’
  • add repathing rate — which tactical movements would have as very small.
    Background: Snow gathering roll would have this as a very large value but it unsets inDoPathfinding since it uses a different pathfinding approach — PathToMaximizeSurface which analyses data embedded in the A* grid from some preprocessing while generating to convey how likely rolling over this part of the A* grid is to get a snowman some more health.

What experiment could I do to determine whether this would be a good course of action?

Gonna try switching my “Move To Point” subtree (an external behavior tree) to use MoveSnowmanWithPID as above.  I’d expect it to work but with no path updating yet.

Yep, works surprisingly well since, when the snowman runs low on health, his ‘reactive planner’ / ‘goal oriented’ behaviour tree design to interrupt his current action (subtree) to path and roll for snow health which causes the attack action (subtree) to be restarted.  Of course, the ‘circle the player’ aspect of this is lost due to the lack of repathing so let’s fix that.

Again I’m struck that I’d like a “Find Usages” facility in Behavior Designer to find where a Task is used in all of my behavior trees!  (and I use lots of external trees so it’s not a crazy ask!)  Luckily, since (a) I keep them all in one hierarchy of directories, (b) I’ve opted for JSON persistence and (c) I’m master of UNIX command line, I’ll just do some quick grep/awk magic to tell 😀

$ find . -iname '*.asset' | xargs grep -aE 'MoveSnowmanWithPID|MoveToWithForceAndPID'
./Snowman/RollForSnowHealth.asset:                                      "Type":"MoveSnowmanWithPID",

Yup, just the one usage.

We’ll also need to add a State for TravellingAndPathfinding so it doesn’t stop to wait for the next path (currently the states are { Unknown, Uninitialized, Pathfinding, Travelling, Arrived, Finished, Error } ).

Decided to move over to my AIDev project since iteration time is much lower.
Sadly, since I’ve updated to Unity 5.3.4, I need to update all the same things in that project as in SnwScf — namely the excellent Unity Test Tools (which is now more integrated into Unity Editor anyway — such that it has DLL collisions!) and Behavior Designer including  redoing this bug fix!  Along the way, fixed a bug in my break-on-change for SharedVariable (when there are no global variables, the GlobalVariables.Instance is null!).

Hmm… with the updated versions, my AIDev project creatures don’t move at all!?  Great.  Guess something happened with the upgrade that’s messed-up some target.  Perhaps the inOptPidController or PathfindingHelperAStar is getting a value from serialization rather than GetComponent()?

End of section for now — afternoon with the kids.  I might continue this later this evening after they’re abed.


[ Decided it’d be OK to continue a post — as long as it’s already published. ]

So, it looks like a change to Behavior Designer I’d suggested, which the author agreed with but made a slightly different way isn’t working properly.  Ahha, it’s not in 1.5.6 — guess I missed it from my patch work?  Yep, that was it.  Hm.  That’ll teach me not to use my usual patching process and just copy over files after a quick diff.  Better go do the rest!

Rest looked fine.  Odd.  Investigated why didn’t see originally.  Worryingly undetermined.  Have to leave for now with note here to remind if see again.

Modified MovementController to be an abstract super-class with MovementRigidbodyController and MovementCharacterController concrete implementations.  Updated MoveWithControllerAndPID to use move information from its MovementController — specifically moved position and velocity into MovementController too.  First run was *veeeery* odd — stuttery jerky movement … which eventually started moving towards its goal — ahha, PID Controller values need re-tuning for CharacterController (rather than rolling Rigidbody).  Set to P:1, I:0, D:0 and it looks pretty good — followed the path perfectly.

Next oddity: it’s not finding its second goal?!  It’s stuttering re-pathing to the shelter.

Background: The test-scene uses the old “find food and shelter” scenario.  The food is tagged “Food” and the shelter is tagged… wait for it… “Shelter”!  Right?  I know!  Amazing.

Each are sought by an included instance of an external behaviour tree “DetermineAndPathToClosestTaggedItem” using Behavior Designer’s overriding variables facility.  Worryingly, when inspected before running, they show the correct string but at runtime, the food-seeking one is showing “Shelter” and the shelter-seeking one is showing a blank!?.  Uh oh, I’m 90% sure this used to work — I /miiight/ have an extra layer in here but don’t believe so.  Could Behavior Designer 1.5.6 has broken something here?  Let’s ask.

Well, I’m due to be at school with my 1.5 year old tomorrow morning who’ll wake at 6-6:30 so think that’s all for tonight.  The mystery will have to last till tomorrow.

Intro: And now for something completely different…


I’m Rupert.  I write a lot — a lot of notes.  I have pages and pages of notes from my development work.  I write them for my own use but realize 2 things:

  1. I need more public content for people to take an interest in my work (especially my on-going project — Snowman Scuffle) and
  2. People other than myself might find value in some of my notes since they ramble over all sorts of development topics (being an indie game developer, there probably isn’t an area of game development we don’t try our hands at!)

So I’ve long pondered how to publish this content.  Being the perfectionist I usually am, I’ve procrastinated publishing it for quite a while thinking I’d take the time to polish it up before posting.  However, given I spend all my available time focusing on making the game, that means there’s not much for non-core-game content making — like polishing blog posts, etc.  So, the non-grand experiment…

Publishing my notes — flaws and all!

I’ll write on the assumption that context will eventually be made by previous posts.  If anyone reads these and wants more explanation or context, please post comments and I’ll try to either edit or add extra entries to explain!  (wooo, alliteration is one of my pet pleasures 😉 )

So.  Intro over.  First post ready to fly.  Time to copy-and-paste some notes in as minimal context and get back to work!

P.s. While I’m tempted to keep one post per area, I suspect that’s pandering to my perfectionism again.  As such, I *will* post more often — per development session *and* per topic (in those odd cases where I do multiple sufficiently large topics in a single sitting).  (Look at me writing “positive actionable” goals … but not present-tense ‘cuz that still feels wrong 😉 )