Wednesday, July 30, 2014

Hero Worship - Village People

What's a village without people?  Unlike Actraiser I want the villagers to actually walk around and do stuff. In Actraiser, you would occasionally see people walking around and tending to things, but it was all window dressing.  There was no simulation beyond the how upgraded your houses were (and even that happened automatically).

So in borrowing a huge chunk from Real-Time strategy games like Age of Empires I present to you, the humble lumberjack and his buddy the stonemason.



These guys emerge from their buildings, find resources, gather them, and return.  It's pretty simple behavior, but presented a couple of interesting puzzles to solve.  Both of these guys work the same way, so for the purposes of examples, lets just talk about the lumberjack.

The obvious thing to do here would be to make the lumberjack find and go to the nearest tree.  That's all well and good, and thanks to "instance_nearest" in Game Maker, it's a piece of cake to implement.  But what if you have several lumberjacks.  They will all go after the same tree.  The game would still function correctly, but it would look silly.  I want them to spread out, with each lumberjack finding his own tree to chop down.


There are several ways to possibly do this.  I had been reading up on a couple of features in Game Maker. Specifically Instance Ids which are unique identifiers for each instance in your game, and the funky way Game Maker handles functions.  Basically what I did was have the lumberjack find the nearest tree and find the instance id of the tree.  When the lumberjack and the tree collide, it checks against the instance id and if it's the right tree, it starts gathering the resource.  Almost all of this information is packaged up and sent to an external script to be implement with the results being returned to the lumberjack object, or stonemason object, or any other villager that needs to collect something. Super re-usable.  When the lumberjack is full, it heads back to the building and conveniently forgets the tree. More on why in a later post.

This still didn't solve the problem of all the lumberjacks going to the same nearby tree.  I could have created a Boolean variable in the tree object and flip it when it was "taken" by a lumberjack.  But then in order to get the NEXT nearest tree, I'd have to loop through all the trees in the room and check each one.  I'm lazy and didn't want to write that much code so I had a tree object (obj_tree) that was tagged by an incoming lumberjack swapped out for an different, but identical looking tree object (obj_treeC).  That way, the next time a lumberjack looks for the nearest tree, it finds a different one.  This also fixed some weird collision issues I was having because the lumberjacks now only check to see if they are colliding with obj_treeC objects.

The final bit was to implement some kind of pathfinding so the villagers wouldn't get stuck on impassable objects like buildings, mountains, and whatever else ends up in the game.  Game Maker has several solutions.  I first attempted to use the "dumb" pathing which just redirects a moving object if it bumps into something (kind of like a Roomba vacuum).  That mostly worked but they would still get stuck on occasion. That's when I discovered that Game Maker has the A* pathfinding algorithm build right in.

I thought it would be difficult to implement, but the documentation is excellent.  The pathing grid is set up at the beginning of the game.  Before any village moves it checks and creates a path to the goal and then goes on its merry way.  All of this is done in a script that's called from any villager objects in the game whenever they need a new path.  When a new building is plopped down, it's location is added as "impassable" to the pathing grid.  Then a message is sent out to all the villages to recalculate their path based on this new information.  The result is villagers dynamically altering their paths when necessary.

I felt pretty pleased with my progress and with myself.

Then I added monsters...

No comments: