May 28th, 2012 by Christian Knudsen
For the past week, I’ve been doing a lot of behind the scenes work on Hostile Takeover. Some of this work has gone into refactoring the code to make it easier to add environmental sprites and data in the future. And a bunch of work has gone into making the map editor as usable and powerful as possible.
Until know, the information for each object making up the game’s environment has been spread out across multiple files, procedures and units. To draw a chair, for example, depending on whether the code was running in-game or in the map editor, information about which specific sprite to use (based on the map and/or object’s rotation), which texture atlas this sprite could be found on, where it should be drawn on the screen relative to the tile it’s on, whether or not the sprite should be flipped vertically or horizontally, and how the object will block line-of-sight – all this information was scattered around in the code. Not only was this highly inefficient from a coding point of view, as information would be duplicated across procedures and files, but adding new stuff to the game would’ve been a nightmare. I’ve now split everything into three highly structured files, ensuring that this process will be a lot simpler and quicker.
Adding the individual sprites and objects to the game is only the first part, though. I then have to build a map with them. Adding ground, walls, interactive objects, light sources and characters into a coherent whole can quickly become a messy process, so having a powerful and easy to use map editor is essential. I believe it takes a lot of iterations to make a good and challenging map – and the easier it is to build and modify a map, the more willing I’ll probably be to scrap something that simply isn’t working, or refine something that isn’t quite there yet.
So while my map editor may not look very flashy – and is probably very fiddly to work with if you don’t happen to be the guy that programmed it – it does everything I need it to do to build a map as comfortably and easily as possible.
When a map has been built, I’ll move to the scripting language to program the effect of the player’s interaction with the map, as well as dialogues (between the player and NPCs) and conversations (between NPCs).
May 21st, 2012 by Christian Knudsen
I was considering just writing an “I’m still alive” blog post to let everyone know that I’m, well, still alive and still very much working on Hostile Takeover. But that’s a pretty short and boring topic, so here’s a completely different blog post instead…
I’ve been working on the AI for Hostile Takeover. I like having a clear goal with the stuff I’m doing, so right now I’m putting the final touches on having a guard patrolling an area (by passing through designated waypoints) and being able to spot and attack you. To determine whether or not the guard can see the player, three factors have to be taken into account: field of view, distance and line-of-sight.
Field of view is simple to implement. You just calculate the angle from the guard to the player and check if it’s within the guard’s field of view (95° to either side). And distance is applied by having the chance of the guard spotting you decrease as the distance increases. But line-of-sight can be a bit more difficult to implement – especially for a 2D game. With a 3D game, you’ve already got the geometry set up in the game world, so you can “just” send of a ray from the guard to the player and check if it intersects with any world geometry. But the geometry of my 2D isometric world is a bit more abstracted.
Walls don’t automatically have volume, for example. Everything plays out on a flat map with just two dimensions, X and Y. There’s no height. No Z-axis. So when a character stops in front of a wall, it’s not because that character’s hitbox was intersecting with the wall’s geometry, as would probably be the case in a 3D game. Instead, the 2D map tells the character that he can’t move from tile A to tile B because there’s a wall in between.
At first, I tried doing line-of-sight by drawing a Bresenham line from the guard to the player with each point on the line being a whole tile. If this line didn’t intersect any walls, there was line-of-sight. The problem with this solution was that it wasn’t very precise. Characters are often moving between tiles and are sometimes only partially obscured by a wall, and using an entire tile as a point on the Bresenham line was too low resolution:
Guard's (green) line-of-sight to the player (blue) is blocked by a wall (red)
So the solution was quite simply to increase the resolution by dividing each tile into subtiles and occupying these subtiles with whatever could break line-of-sight. I decided that a 10x resolution would be sufficient (primarily because characters have 10 frames of animation when moving from one tile to another, so they can occupy 10 different spots in between tiles, which can be represented by dividing each tile into 10×10 tiles). I’m now just drawing the Bresenham line by using these subtiles as points on the line. Whenever a subtile is plotted, the game checks whether or not this subtile is empty or not. If not, line-of-sight has been broken:
Using subtiles, the guard's line-of-sight isn't blocked by the wall
I don’t have to create subtiles for the entire map, just the single tile that the Bresenham line is currently passing through, so it’s neither memory nor processor intensive. To occupy the subtiles, I have a procedure that starts off by setting all 10×10 subtiles as empty, then checking for character, walls and other world objects on that tile. If any are there, the relevant subtiles are filled in.
I believe good ol’ X-COM did something similar, but also had a Z-axis for individual tiles, so it in essence had subcubes that the ray was passing through. This, for example, allowed a bullet to pass through an open window. I’m considering expanding my own system to that as well, since I’ll most likely be adding functionality for shooting at characters on different levels of the map. But I’ll wait and see if it actually ends up being necessary.