Seems like a good idea for a prequel, but there's a lot more that I could see you doing with that concept.
This isn't the kind of game that I'd expect to need the speed that C++ has, so Game Maker would work as a substitute. There's nothing wrong with using a tool that is simpler, so long as it gets the job done in a way that works for you. But if you want to stay away from Game Maker for whatever reasons (my current reason is that GML is too simple and uninteresting to be used in complex games), I understand.
You need several things:
- Map Editor
- Collision System
- Input System
- Rendering Engine
- If there are multiple enemies, then Processing Stacks/Stages
- Resource Handler
If you make the base correctly, you can use that base to program both your map editor and your actual game. I'm assuming you'll be using something like SFML, so input shouldn't be too difficult. I'm not entirely sure how much you know about programming something like this, so I'm going to explain pretty much everything in fair detail.
Keyboard input should be stored two ways - By event and by key state. SFML gives you the event objects, so that's already done for you. You just need to also keep a key state version (assuming that SFML doesn't give this, which, if it's anything like the SDL, it doesn't), which would allow you to hold down a key and move left or right without the key-repetition that the OS does for you. I do this by creating a
bool KeyStates[0x80];
, each position in the array representing a character. Of course, all key states are initialized to 0 to represent that the key isn't currently being pressed. When you get an event that a key has been pressed, simply set the corresponding state in the array to true.
KeyStates[KEY] = true;
When the key is released, set its state to false. You can then use this KeyStates array for moving around. Keeping the events also allow you to check for key presses rather than just checking the state of the key, which can be used for something like jumping and creation of UIs.
Collision systems tend to get a bit messy no matter what way you set it up, you just have to try to keep it as clean as possible. What I'm currently doing in my engine is having two lists of hgeRect objects (hgeRect is a class that contains the bounding box representing the general area of a sprite), one is just for collision masks and the other is for collision handlers. Collision masks are just hgeRect objects with an ID. Collision handlers are objects containing both the hgeRect and ID as well as a reference to either a function or a method that would be called in the case of a collision. When we check for collisions (which is done all at once during this process) we loop through the collision handlers and check for intersecting bounding boxes if the ID of the handler and the ID of the mask match. Obviously you're going to have a different reaction to colliding with the floor from colliding with a spike, so this ID system works. To further optimize it, you can use a map with the ID as a key and a list as a value to prevent looping through objects without the same ID.
That collision system has a problem however, the easiest way to do gravity is (semi-sudo-code):
1 2 3 4 5 6 7 8
|
y += VerticalSpeed;
VerticalSpeed += Gravity;
Tile *CollidingTile;
if ( CollidingTile = CollidingWithSomething ( &ThisBoundingBox ) )
{
y = CollidingTile->y - CollidingTile->UpperSpriteDifference - this->LowerSpriteDifference;
VerticalSpeed = 0;
}
|
In this case, collision checking is done outside of the collision event. For this particular issue, functions will be needed that simulate the collision checking process, but it only checks against the bounding box passed to it. If you want, you can also add the ID to check for as a parameter.
The rendering engine should be fairly simple, SFML makes that part a bit easy for you in this case, but you may run into some problems later on with this method. You can simply make a list for each rendering type and then render them in order. The problem with this is that, along with complete lack of drawing order, if you want to do a bunch of things while drawing, or if you want do get specific variables to do a certain thing while rendering, you don't have that option. I did something similar to the collision handler for my rendering system, I have a list of objects that contain, for the most part, just a pointer to a function or a method. You can guess how I'd use them.
A lot of the time, order in which things are processed is VERY important. For example, say you have an object that follows the player. If the follower updates its position first, and then the player updates its position, the following object won't be up to date. (I suppose using pointers would fix that problem, but that's not the point.) To fix this I have a map with an integer as a key, and a list as a value. The key represents the processing stage, and the list gives all of the callbacks to be used during that stage (this, for my system, also works similarly to the rendering system). This way we can keep our objects ordered during processing.
The resource handler is mostly a method for obtaining and releasing resources en masse. I'm still working on this part myself, as I'm trying to get some edits for the rendering library (HGE, it's like SFML, but Direct3D-reliant and is designed for game creation) finished so I know exactly what rendering API we'll be using, but those two ideas that I just stated are key. If you want to avoid global variables you need some place to store them all. Virtual members aren't a good idea because then when you begin to write the cleanup functions for the game, there are a lot of resources you have to release manually. A universal resource handler would be able to free them all at once, allowing you to easily add and remove resource-dependent classes from the game.
That's somewhat similar to what I have for a base API. Only, I'm setting mine up to be used for online, so I have some networking protocol classes in place along with some quad-tree optimization in my collision handler.
This whole thing is, of course, assuming that you're planning on using 2D, since modeling people isn't exactly easy. Even then, most of the described systems would still work just fine.