Thursday, July 19, 2012

AI Experimentation

Collapse is a game set in a post-apocalyptic world in which a human-created AI faction known as the Mechs have taken over. Therefore, AI must be an essential part of the programming.
After extensive research in path-finding algorithms, I decided upon the ubiquitous A*. Then, after days of trying to implement it, using many different techniques from just simple std::vector<Node*> lists to a much more complex and efficient std::priority_queue<Node> binary heap, I gave up. None of them worked properly, obviously due to faults in my programming, but I simple couldn't figure it out.
So I settled on a different approach: pre-defined movement paths. Obviously, this would significantly dumb-down AI decision-making, but I had to at least get something working. The map worked fine, but, again, after days of tweaking and planning and modifying, I couldn't even get the enemy tank to follow the path. I used a rather strange approach with lots of vector math and collision detection, which probably wasn't necessary. The algorithm (in pseudo-code) looked something like this:

Spawn:
    Target = FindNearestPath
    End

Update:
    If reached Target
        Target = FindNextTile
        If no Target
            Turn 3 degrees
    Else
        Adjust angle and drive.
    End

FindNearestPath:
    Find tile with minimum distance to entity.
    Return tile

FindNextTile:
    For each tile in AI map
        If tile collides with line-of-sight
          and tile not collides with entity
            Return tile
    Return no tile

Friday, July 13, 2012

Levels in Collapse

Collapse is the first game I've ever made that actually required levels, so it was quite interesting figuring out how to approach level design, loading, and storage. I definitely had to create some sort of level editor in order to easily design levels. Eventually, after much consideration, I decided that the easiest way to approach level handling would be to split every level into 4 basic components: Terrain, Collision, AI, and Game-Logic. 


Terrain


This map is pretty self-explanatory. It's stored with a .ctm extension (Collapse Terrain Map) and contains information about all the tiles on the map. Currently, since my artwork is fairly limited in the tile department, there are only two tiles available for placement: a floor and a wall. I knew that I'd be expanding later, and I couldn't bear to have a massive switch statement for every single type of file. So, I wrote a quick Python script to gather information about which tile images were available to use in the level, and then stored it in a file call ValidNames.dat. The script is really simple, since I have a special folder dedicated to files, it's pretty easy to find them all:


Collision


Having a separate map for collision makes it so I don't need to store which map tiles are passable and which aren't, and it also allows for more flexibility in terms of destroying tiles for any given reason. For example, when the player fires the tank weapons, if they make contact with the collision map, the collision tile contacted is removed and the terrain tile is changed to a "broken" version. The map is stored as a .ccm file (Collapse Collision Map) and just contains x, y coordinates of 32x32 tiles representing impassable areas.

Saturday, July 7, 2012

Design

It's a crisis of sorts. There is so much going on in my head that I can't seem to make sense of it all. Collapse is exponentially more complex than any game I've ever attempted, and it's beginning to overwhelm me. I don't want another rewrite, I want it right this last time. I'm not even back to the point where I was previously, because some serious design decisions must be made before I can progress.


Thoughts

There are many things that must be accomplished every frame, and so many different ways it can happen. I have already committed to a state-driven engine system, with states such as e_MAINMENU, and e_INTRO. Each primary subsystem has access to a Game_State& reference, so it can modify it as it wishes. Though, this may be changed to reflect another state, a sub-state if you will, that switches between actions within a primary engine state. Such as a e_HUD state within the e_GAME engine state. This is still in consideration though, and the more I think about it, the better of an idea it seems. In addition to this minor dilemma, many other things still hinder me. In any given frame, there could be event handling, level updating, collision detection, object rendering, HUD updating, and dozens of other operations. None of these are mutually exclusive, nearly everything depends on or interacts with something else. My idea is to split everything into 3 primary subsystems: World, HUD, and Menus. The game engine will take care of interaction between them, such as passing them between each other when necessary. Hopefully I won't create circular dependencies... That'd be bad. 


World

This will be a class, most likely named Game_World, since it is indeed a game-specific element. It will handle most of the game mechanics, such as rendering of objects on-screen, map loading/updating/saving/rendering, enemy AI tactics and operations, player event response, and all collision detection between those elements. It will need to share an inventory system, and possibly more, with the HUD subsystem.

The Importance of Commenting Code

When you think about it, it seems fairly obvious. Comments can clarify and explain the purpose of any of your code, from variables to functions to entire namespaces.
Throughout Collapse, I decided to implement a Doxygen commenting system. Doxygen is a command-line program that generates documentation from code comments. It's really handy and clean, and though I do not see much use for it in terms of Collapse because I'm not actually releasing some sort of documentation, it provides a uniform commenting style that encourages me to comment everything well.

The reason I decided to do this is because I realized how unclean and confusing my code was. Here's an example line:
float angle = DEG(atan(abs(y - this->GetY()) / abs(x - this->GetX()) * 1.0f));
I had to look at this for a good five minutes before realizing what this actually did; it determines the angle between the player position and the mouse location. It's after this moment that I realized some sort of commenting system was essentially, and thus began re-write #3.