Monday, March 26, 2012

AI fun times

I hate coding AI.  I really do.  It's not necessarily because it's an unpleasant task... it's because I find it annoyingly difficult.  See, it's easy to understand what you want the computer to do, but it's ridiculously hard to tell it to do that.  For instance, it's all well and good to say "this guy should go around walls."  But how exactly do you do that, code-wise?  The answer, it turns out, is quite complicated.  Far more complicated than simply adding a "go around walls" line of code somewhere in the movement routine.

Gamemaker has a very VERY easy function for simple movement planning, which is what I used in previous Gamemaker projects (although if memory serves, enemies in Pit didn't do anything to try and find their way around walls, since it was rarely necessary).  It basically will change an NPC's movement direction under-the-hood to move parallel to a  solid object if it contacts it.  This works pretty well if you're only dealing with single blocks or detached "lines" of wall objects--the NPC sometimes takes the long way around, but it doesn't get stuck.  However, when intersecting walls come into play, this routine can regularly cause NPCs to start infinitely looping in corners.  Not a huge problem when you're making a mob shooter like Nurf Nerfus, but a significantly larger problem when you need your enemies to be able to actively hunt down the player and pursue them through inside areas.

So this is why motion planning exists.  Motion planning functions let an NPC plan out the shortest, most logical path to a given point by looking at the entire layout of the area at once rather than simply moving until they hit something.  The motion planning in Gamemaker is actually pretty good, and allows for some pretty convincing "maze solvers."  The problem with them is that calculating a path takes a fair amount of time, in computing terms.  So it's not practical to just have the AI calculate a path every step.  When my NPCs are just walking around, it's works perfectly well to set a point once and let them find their way to it.  You wouldn't notice any slowdown.  But when the point is constantly changing--such as when an NPC is chasing the player, or going to the source of a noise-- the game can easily slow to a crawl.  In reality, most situations like this can be averted with some smart "cutting corners" (for instance, if the NPC can see you, he doesn't really need to find his way around walls, and most of the situations where an enemy needs to actively chase the player involve direct line of sight).  But then, the game needs to know when to use motion planning and when not to use it.  It's not exactly a Carmack-worthy problem, but it adds more code and more complexity.

Any veteran programmers reading this are probably shaking their heads and going "what... he can't handle some simple motion planning?  How does this clown ever expect to succeed as an indie game designer!  I can do motion planning in my sleep.  Now excuse me while I go back to making a mod that improves the visuals on yet another AAA commercial title."  Well I don't know if I mentioned it previously or not, but I am a thoroughly right-brained programmer.  In other words, anything that requires something resembling mathematical competence will invariably confuse me.  This is why I love the Gamemaker toolset.  Because then I don't have to worry about those niggling little annoyances like collision detection, sprite rotation, etc.  At worst, I may have to use a function or two.  At best, I can merely set a variable and the engine takes care of it for me.  However, even within the admittedly noobish environment of Gamemaker, there are still things that I would much prefer not having to do.  Things that require me to exert effort beyond simply typing in the correct sequence of functions and variable assignments.  Things that will inevitably result in woeful spaghetti code and arcane solutions.  Which is always...ALWAYS...the case with my AI.

So, this is a rather rambling post... basically, I'm working on the AI right now.  And it's predictably difficult.  It will ultimately involve some cutting of corners and implementation of different techniques.  But thus far, it's turning out pretty good.  Enemies won't be able to see you if you're hiding in shadows or are outside their FOV, they will actively hunt for food and run from danger, and they'll do a little to try and find you if they know you're around.  Next post, I'll have some screenshots of the lighting and simple little graphical effects that the game has implemented thus far.

-jefequeso

4 comments:

  1. Heh, right there with you mate. Dunno how does this work in gamemaker, but I found the simplest solution for this issue to be for the NPC to pick a point (like you said, yes), calculate direction using vector, and then give the NPC a rough direction to move in - like, for instance, north-east. So, when it hits the wall, it keeps moving in this general direction (preventing loops in corners,) until it can move freely again (isn't restricted from any direction) - it's hardly CPU consuming at all, and gets the job done.

    Anyway, if you're basing the AI on the 'last sight,' what do you do when AI doesn't see you anywhere from that point? Target the player's location again, whatever that might be, switch AI back to idle mode, or do you want to do some kind of simple search pattern thingy? Did I even understand correctly that the AI searches for the place it saw the player last time until it sees him again? I think I should go to sleep.

    ReplyDelete
  2. In past versions of Swamplands, I used a system whereby when the enemy can't see you, he will keep moving to points that are your location + or - a random distance. So it looks like he's actively searching for you. But although the effect is fairly realistic, from a gameplay standpoint it doesn't work very well, since it means it's nearly impossible to keep from being found again. So I think that this time I'll do some "dumbing down." The enemy will probably move in the direction that he last saw you, but he won't hunt you for as long. Or something like that. Ultimately, I won't know for sure until I actually try it out in-game.

    ReplyDelete
  3. Well that's because you tie his search pattern with actual player's location, which will never work unless the creature has very good tracking ability (in which case it makes sense and would force players to, let's say, cover themselves in shit to be less tracable. It would also mean that your AI would need to keep track of yet another variable, I know.) Point is, there is no way for the creature to know where are you - wouldn't it be more fun and easier to just send the creature in random direction, or code some form of search pattern (i. e. going in circles around the place of last sight?), while keeping the creature in alertet state and triggering chase mode instantly when it sees you? (provided you have some form of timer) I suppose you do have some sort of short spot-timer in place so it doesn't just start chasing you whenever you stumble into the general direction it's currently looking in.

    ReplyDelete
  4. *headslap*

    derp. Of course... having the enemy search around the place where he last saw you would be the best solution. It will require an added state, but the structure of the AI should support that. I'll probably try that out, as soon as I have the "fleeing" portion finalized (NPCs will currently flee from objects they deem dangerous, but I've been occupied with other stuff and haven't dealt with wall collisions and haven't put in a run distance threshold yet).

    ReplyDelete