16
\$\begingroup\$

I've been working on a 2d RPG for awhile now, and I've come to realize I've made some bad design decisions. Theres a few things in particular that are causing me problems, so I was wondering what sort of designs other people used to overcome them, or would use.

For a little background, I started working on it in my free time last summer. I was initially making the game in C#, but about 3 months ago, decided to switch to C++. I wanted to get a good handle on C++ since it's been awhile since I used it heavily, and figured an interesting project like this would be a good motivator. I've been using the boost library extensively and have been using SFML for graphics and FMOD for audio.

I have a fair bit of code written, but am considering scrapping it and starting over.

Here's the major areas of concern I have and wanted to get some opinions on the proper way others have solved them or would solve them.

1. Cyclical Dependencies When I was doing the game in C#, I didn't really have to worry about this since it isn't an issue there. Moving to C++, this has become a fairly major problem and made me think I may have designed things incorrectly. I can't really imagine how to decouple my classes and still have them do what I want. Here's a few examples of a dependency chain:

I have a status effect class. The class has a number of methods (Apply/Unapply, Tick, etc.) to apply it's effects against a character. For instance,

virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);

This functions would be called everytime the character inflicted with the status effect takes a turn. It'd be use to implement effects such as Regen, Poison, etc. However, it also introduces dependencies on the BaseCharacter class and the BattleField class. Naturally, the BaseCharacter class needs to keep track of what status effects are currently active on them so that's a cyclical dependency. Battlefield needs to keep track of the fighting parties, and the party class has a list of BaseCharacters introducing another cyclical dependency.

2 - Events

In C# I made extensive use of delegates to hook into events on characters, battle fields etc. (for an example, there was a delegate for when the character's health change, when a stat changed, when a status effect was added/removed, etc.) and the battlefield/graphical components would hook into those delegates to enforce their effects. In C++, I did something similar. Obviously there's no direct equivalent to C# delegates, so instead I created something like this:

typedef boost::function<void(BaseCharacter*, int oldvalue, int newvalue)> StatChangeFunction;

and in my character class

std::map<std::string, StatChangeFunction> StatChangeEventHandlers;

whenever the character's stat changed, I'd iterate and call every StatChangeFunction on the map. While it works, I'm worried this is a bad approach to doing things.

3 - Graphics

This is the big thing. It isn't related to the graphics library I'm using, but is more of a conceptual thing. In C#, I coupled graphics in with alot of my classes which i know is a terrible idea. Wanting to do it decoupled this time I tried a different approach.

In order to implement my graphics, I was imagining everything graphics related in the game as a series of screens. I.e. there's a title screen, a character status screen, a map screen, an inventory screen, a battle screen, a battle GUI screen, and basically I could stack these screens on top of each other as necessary to create the game graphics. Whatever the active screen is owns the game input.

I designed a screen manager that would push and pop screens based on user input.

For instance, if you were on a map screen (an input handler/visualizer for a Tile Map) and pressed the start button, it'd issue a call to screen manager to push a Main Menu screen over the map screen and mark the map screen to not be drawn/updated. The player would navigate around the menu, which would issue more commands to the screen manager as appropriate to push new screens onto the screen stack, then pop them as the user changes screens/cancels. Finally when the player exits the main menu, I'd pop it off and get back to the map screen, remark it to be drawn/updated and go from there.

Battle screens would be more complex. I'd have a screen to act as the background, a screen to visualize each party in the battle, and a screen to visualize the UI for the battle. The UI would hook into the character events and use those to determine when to update/redraw the UI components. Finally, every attack that has an available animation script would call an additional layer to animate itself before popping off the screen stack. In this case, every layer is consistently marked as drawable and updatable and I get a stack of screens handling my battle graphics.

While I haven't been able to get the screen manager to work perfectly yet, I think I can with some time. My question about it is, is this a worthwhile approach at all? If it's a bad design I want to know now before I invest too much more time making all the screens I'm going to need. How do you build up the graphics for your game?

\$\endgroup\$
0

2 Answers 2

15
\$\begingroup\$

Overall I would not say anything you listed should cause you to scrap the system and start over. This is something every programmer wants to do about 50-75% of the way through any project they are working on, but it leads to a never ending cycle of development and never finishing anything. So, to that end, some feed back on each section.

  1. This can be a problem but is usually more of an annoyance than anything else. Are you using the #pragma once or #ifndef MY_HEADER_FILE_H #define MY_HEADER_FILE_H ... #endif at the top or surrounding your .h files respectively? This way the .h file only exists once within each scope? If you are, my recommendation then becomes removing all #include statements and compiling, adding in the ones as needed to compile the game again.

  2. I am a fan of these types of systems and see nothing wrong with it. What is an Event in C# is commonly replaced with an Event System or Messaging System (can search the questions here for those things to find more information). The key here is to keep these to a minimum when things Need to happen, which it already sounds like you are doing so should be no to minimal worries here.

  3. This also seems on the right track to me and is what I do for my own engines, both personally and professionally. This makes the menu system into a state system that either has the root menu (before the game starts) or the player HUD as the 'root' screen displayed, depending on how you set it up.

So to sum up, I see nothing restart worthy in what you are running into. You may want a more formal Event system replacement down the road but that will come in time. Cyclic includes is a hurdle all C/C++ programmers constantly have to jump through, and working to decouple the graphics all seem like logical 'next steps'.

Hope this helps!

\$\endgroup\$
3
  • \$\begingroup\$ #ifdef doesn't help circular include problems. \$\endgroup\$ Commented May 31, 2011 at 20:52
  • \$\begingroup\$ Was just covering my basis with expecting that to be in there before tracking down the cyclic includes. Can be a whole other kettle of fish when you have multiple symbol defines as opposed to a file needing to include a file that includes itself. (though from what he described if the includes are in the .CPP files and not the .H files he should be ok with two base objects knowing about each other) \$\endgroup\$
    – James
    Commented May 31, 2011 at 21:43
  • \$\begingroup\$ Thanks for the advice :) Glad to know I'm on the right track \$\endgroup\$
    – user127817
    Commented Jun 3, 2011 at 5:45
4
\$\begingroup\$

Your cyclical dependencies shouldn't be a problem as long as you're forward declaring the classes where you can in the header files and actually #including them in the .cpp (or whatever) files.

For the event system, two suggestions:

1) If you want to keep the pattern you're using now, consider switching to a boost::unordered_map instead of std::map. Mapping with strings as keys is slow, especially since .NET does some nice things under the hood to help speed things up. Using unordered_map hashes the strings so comparisons are generally faster.

2) Consider switching to something more powerful like boost::signals. If you do that, you can do nice things like make your game objects trackable by deriving from boost::signals::trackable, and let the destructor take care of cleaning up everything instead of having to manually unregister from the event system. You can also have multiple signals pointing to each slot (or vice versa, I don't remember the exact nomenclature) so it's very similar to doing += on a delegate in C#. The biggest problem with boost::signals is that it has to be compiled, it's not just headers, so depending on your platform it might be a pain to get up and running.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .