radicalcoroutine_code

This week I had to rewrite a class I had wrote a couple months ago. I didn’t really change the interface to it except to add another method to it. But I completely gutted its implementation and rewrote it from the ground up. See the problem was that I wrote it at a point where I wasn’t fully introduced to the Unity Coroutine system, and only had a basic knowledge of it. This week I had to start writing more complex behaviours for AI and needed a powerful state machine. This is what brought me back to my RadicalCoroutine class and made me decide to rewrite it.

AngryAnt Behave

Here’s the thing, I played with some 3rd party AI systems out there, and I couldn’t really jive with any of them. Before Andy and I started this project I played with the AngryAnt Behave Project on my own time and it was my first introduction to behaviour trees. It took some time to wrap my head around the idea of what they are, which led me to my first wall. I was used to state machines up to that point, which caused for some side stepping when learning trees. It’s just such a different way to look at the problem, but this kept me interested to follow through in learning it. I like learning new ways of solving problems. Problem though is that if the learning curve was that for me, what would I do when it came time to have anyone else work with it. Such as when Andy and I started this project months later. Andy draws up his idea of how something should behave as a state machine, and would end up having to convert it to a behaviour tree. Not so nice.

I have to say though. The GUI was beautifully simple. When I find a tool that does up the GUI for me, oh man, I love it.

So I liked the idea in principal… but in practice it just didn’t work out for me. When we went to do this project I went to grab up Behave despite that, but they’ve since released a new version and charge money for it. Working on a budget I went looking for another option.

Rival{Theory} RAIN

I then checked out the Rival{Theory} RAIN Project which also used behaviour trees. It also had other interesting features like sensors and purported to integrate nicely with animations and what not. And the nicest part… it was free! So we downloaded it and I got to work playing with it. First thing I noticed was that all of its parts gave me the ability to extend them in code, this I liked, at first.

Next thing I noticed was that the way the behaviour trees were implemented was very different. The GUI wasn’t as nice as AngryAnt Behave, which used a drag and drop node on canvas design. RAIN on the other hand used a hierarchy tree, moving nodes around felt more cumbersome. Maybe I just liked the node on canvas design of AngryAnt Behave more because it reminded me of how many state machine engines are set up. Old habits die hard and all. Thing is the nodes were all predefined, unlike AngryAnt Behave. In AngryAnt Behave you created a node, named it, and then you had a corresponding MonoBehaviour class that had a method of the same name. The node when active would call the corresponding method, and you wrote code in there. Down side to this was that there were no predefined nodes, but adding new nodes was cheap and easy. In RAIN a custom node required creating a custom class that inherited from their Action class. This meant every node was a complete object in memory, and creating new custom actions was time consuming.

And this was the first time I ran into a recurring problem with RAIN. I didn’t just have the option to extend their framework, I HAD to extend their framework to deal with design flaws. OK, this wasn’t a design flaw perse, this was more a design style I didn’t agree with. Like I said though, this was just the first time I ran into the problem. I ended up writing a handful of custom actions that basically wired my behaviour trees to a corresponding MonoBehaviour and called methods on it by name; sort of like AngryAnt Behave. It just wasn’t smooth, and I also just didn’t like the idea of all those nodes popping in and out of existence and getting garbage collected.

The problems didn’t stop there. See in RAIN the AI is broken into a few parts. Memory, Mind, Motor, Animator, Navigator, Senses. Memory handles saving values that can be accessed between all parts of the brain. Mind handles the traversing the behaviour tree. The Motor moves the entity. Navigator resolves pathfinding. And the Senses is where you attach sensors that can observe the game world.

I ended up having to rewrite the Motor, Animator, and Navigator.

The Navigator was the first issue. This was because the pathfinding engine that comes with RAIN expects a 3D world, or at least a 2D surface that you walk on with the 3rd dimension sticking up off it. This would be great for games like Shadowrun, which actually uses RAIN, but not so much for a side scrolling, platforming, brawling, crazy fest. So I ended up writing my own Navigator to integrate with other pathfinding systems. Again, not really a problem with RAIN, but still a problem I had.

The Motor was next. RAIN just moves your GameObject. Quite literally just updates the position. No collision, no nothing. I guess it presumes the pathfinding dealt with making sure you didn’t go somewhere that didn’t have collision. If you integrated with Mecanim you got some collision, but don’t get me started on Mecanim. Unity really needs to step up their game with that deal. Furthermore the motor too expected a 3D world just like Navigator. So another rewrite.

Then came the Animator. This one bummed me. The Animator expects the Animation component to be placed on the root of your entity hierarchy. The same GameObject that is moved by the Motor. Thing is an Animation component expects to be attached to the parent of the skeleton it is animating. Thing is we have our entities designed so that this skeleton root with the Animation component is a child of the entity root. This way animations that animate position are relative to its root, and not to the world! So yeah, another rewrite.

Spending time on their forums, I came to learn they hadn’t released a new version of their framework in around a year which fixed a myriad of bugs. I noticed that they gave a couple of regulars early access month prior, but no peep about when the general public would get it. I had gotten fed up with it at the point, and decided to just move on, when not but a week later they released their update. So hey, if y’all like RAIN, the new version is out, go get it!

And the kicker, it still used a design that has a learning curve that I only recently got over. Either Andy was going to have to move to behaviour trees and the learning curve involved, or I was going to have to convert his state machine designs to behaviour trees. So bye bye RAIN.

PlayMaker

So PlayMaker by Hutong Games isn’t really an AI system. I’ve heard of people using it for AI wiring and the sort. I hadn’t used it and knew at the minimum it was a state machine that allowed for visual coding to a certain extent. Andy was hounding me for what he was calling an “event system” that would allow him to quickly just wire up some events and wire scenarios in the game together. Looking to save time, and seeing that PlayMaker was on sale that day, I dropped 65 bucks on the thing and checked it out.

I like it.

It’s not an AI system at all!

But I liked it.

We’re using PlayMaker now for scenario stuff, I’ve written up a few custom Actions for it to make it integrate well with out entity pattern and what not. There’s a few minor bugs here in there, mainly in the inspector extension model, but nothing game breaking. I’m still left without a system to write AI normally. Code it is I guess.

Radical Coroutines

So I needed a simple state machine. Really a foundation on which I can build a powerful state machine. I have basic component based state machines where I have a manager that takes in objects that implement some basic interface, transitions between those states, and updates them accordingly. I use this in the MovementMotor for our entities so that I can change between different movement styles. This is very useful for the Player which changes between platforming, wall walking, background wall running, latched to enemies, etc. All of these have different ways they player moves, so breaking them into states is useful.

This model just doesn’t mesh well with me in an AI sense. I don’t perceive AI in an iterative way, which makes coding iterative AI a bit complicated for me. AI is more fluid, and event driven to me. Wait for N seconds, run towards target, keep running until reach target, attack target, retreat from target, keep retreating until certain distance, repeat. This is all… this is coroutines.

I’ve heard of people using coroutines for AI, but I hadn’t seen it done. So I thought about what I would want for them, and there was two primary things I needed.

1) Maintain references to coroutines that are cancellable, so that when I need to exit a state I can stop that state in its track.

2) Making it where I can manually tick the coroutine every update so that I can handle the timing exactly. This is useful in case I have other update or coroutine methods that run in tandem that may effect a state. I need to be 100% certain when and where they happen in the tick process, and also be easily able to wait for the AI coroutine to tick first.

The first I had actually accomplished with an earlier version of my RadicalCoroutines. The second I had to add to them. I originally written my RadicalCoroutine so that I can cancel coroutines easily as well as write custom YieldInstructions which I do by implementing my IRadicalYieldInstruction interface. I also had started some stuff to ease the Async load process, but that project got stalled. Maybe in the future…

It was a little expensive though when you had coroutines in coroutines. I didn’t approach the internal design of it quite properly. You can actually see the massive mistake I did by looking at the old version of it which you can find here, note, it’s also got some bugs.

So I started rewriting it from the ground up and added the ability to manual tick the thing. I hammered out those bugs and really made the little guy shine. This is how she stands now:
Spacepuppy Unity Framework/RadicalCoroutine.cs

The way I deal with the built in YieldInstructions for a minute was hooking into a global GameObject, but that felt really hacky. I since forced requiring passing in a MonoBehaviour as a handle to attach to, it still feels a little wonky to me, but it gets the job done in as smooth of an issue. The main issue using this MonoBehaviour handle is that the RadicalCoroutine properly handles if the MonoBehaviour is disabled or destroyed.

This all results in a coroutine that is more manageable.

And the best part is that because I had designed this prior to the need for AI. Really it’s useful for anywhere I use Coroutines. Coroutines which are super powerful on their own just became more powerful. This change to my pre-exisiting design really just cleaned up error and memory issues as well as adding the manual ticking.

Leave a Reply

Your email address will not be published. Required fields are marked *