You can now add this to your wishlist on Steam!
Very bitty day, virtually none of which had anything to do with my game… But I did manage to fix a couple of state related bugs in the dungeon, and finish setting up my laptop for work.
I’ve started giving feedback to my students, so most of the day was spent playing builds and chatting on conf calls. In-between I started experimenting with the new AI bits.
I’ve decided to add a Blackboard to the AI Controller, which can be passed to each AIAction as needed. Blackboards hold key/value pairs, and you can update the values at runtime. It makes more sense for the Blackboard to hold references to things like the PlayerCharacter, or points of interest, than it does for AIActions to get / calculate them repeatedly.
That said, having to reference blackboard keys through string literals (FNames) makes my skin crawl. I honestly don’t understand why there’s not another mechanism for that.
Derp of the day: Turns out that I also forgot to push Thursday’s code changes, so I had to go back in and re-write it all. I’ve done this twice in as many weeks, which is unlike me, so I’m not sure what’s happening there…
Anyway, with that done, I starting writing some AIActions for a Wasp. I have it picking locations in a donut around an object in the world, but the locations need some filtering to be nice.
I need to try a few things to work out the best pattern for actions. Atm, this first one is re-entrant. Ie: when the wasp gets to the new location and the action has ended, I’m allowing it to call “enter” on itself, and send the wasp to the next location. That’s convenient, but now I’ve had time to look at it I think it’s wrong, so tomorrow I’m going to play with some different ideas.
I’ve been all over the place today but I did play around with the AI some more. I’ve still not decided how to handle actions ending, but I have cleaned up entry and tick. And more importantly, I’ve worked out what parameters need to be passed from the controller, to the action, so I was able to clean up the interface a tad.
I couldn’t get the wasp to look nice using AI pathing, so I pulled everything apart and started using forces instead. This gives the wasp a nice floaty kinda feel, and if I turn down the linear damping I can get it skidding about quite nicely. For the life of me though, I couldn’t get “Orient To Movement” to actually do anything, whether the forces were applied through the character controller, or directly on the physics capsule. Burned some significant time on that…
This might actually be for the best, though. If I do it myself I’ll have worked out all the values I can send into the Animation system, meaning I can get the wasp to lean nicely, pull back when braking, and dip-forward when accelerating. Should also give me some numbers to attenuate audio effects with, as well.
Unfortunately you'll have to imagine all this. I thought I'd exported a video from Preimer Rush, but, well, the less said about that complete PoS, the better.
Had a good block of time to experiment, today, and I’m feeling really confident that I’ve answered all my questions from the start of the week.
I was over thinking how repeating actions should loop. They shouldn’t do anything special at all.
When an action has ended it should, when asked, say that it can no longer run (and return a score of zero). The UtilityAIComponent – the thing that looks for the highest scoring action – will see that it can no longer run, ask it to clean itself up, and then reset it. The reset action will automatically (assuming it’s still the highest scoring) repeat. I don’t need to do anything explicit in the action at all.
In fact, building everything to be as one-time and stateless as possible should probably be the goal…
I made a few changes to the UtilityAIComponent to make this super-duper obvious. Actions now get Ticked before any scores are computed, and I added a function to swap actions, so every code-path does the same thing.
It’s still possible for there to be a frame with no active action, but I realised this was always a function of this system; all the available actions could be returning Zero, anyway! Nothing to stress about there.
The other thing that was in the back of my mind was how to handle time. Seems pretty obvious that any non-trivial action will want to know something about time, if only to know when to “kill” itself, but I’m trying to remove as many passed parameters as possible. Now, the first thing I do when ticking is put the current time in the Blackboard…
I’m at the stage where I’ve got a few actions, all intermingling, and the system’s working exactly as I hoped. Things bubble up nicely. But… I’ve got a couple of actions that involve flying to locations and they could share the same code. This is exactly what Blueprint Function Libraries are for (even though I rarely use them) so I bounced a function up to the library and… hit a wall:
Yeah, you’re not supposed to call BPL functions from classes/blueprints that are derived from UObject (the base type in UE4). This is by design, apparently, although I don’t understand the reasons why. Ugh.
I know what the hack is… but… well…
I asked about regarding the limitation of UObject derived classes and function libraries. It looks like “that’s the way it is”, which is, er, not ideal.
I can fix it by changing AIActions to derive from something further along the class hierarchy, but I’m holding off for now. It feels like overkill but, if I do end up repeating code then it’s probably better to pay the price. As it turned out, the things I thought I could share between the actions on the wasp were actually fairly different, so it didn’t matter in the end…
I did some more work on the Wasp and it can now chase you around, abort when it’s too far from home and Idle occasionally. You can watch the actions bubble up the stack, debugging is easy, and it’s really nice to have everything in one place when developing each action. I feel like I’m in charge of it atm.
I’m discovering there’s a little bit of subtlety in the numbers. I’m doing a few things like scale scores based on distance or time, which need a bit of tweaking, but these feel like simple leavers with clear responses.
Admittedly, this isn’t the most strenuous of tests, but this jasper’s already a little bastard. (And I've stopped messing about now. DaVinci Resolve is on the case...)
I’ve been promising Phil, my music collaborator, a build for a few weeks and it’s been an internal milestone for the end of this month, so, today’s the day! I setup the depot on Steam and kicked the tyres on Steve Streeting’s Release Script. It does exactly what it says on the tin, and I have a build on Steam!
That feels like a hurdle jumped, so I’m calling it; wine-o-clock!