Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Design flaw in my game ?  (Read 5373 times)

0 Members and 1 Guest are viewing this topic.

unranked86

  • Newbie
  • *
  • Posts: 37
    • View Profile
Design flaw in my game ?
« on: August 15, 2011, 06:11:12 pm »
Hi there

I hope, this is the right place to ask this.

I got really stuck with my project. I think I have a nice flaw in my design, or maybe I simply lack of proper knowledge of C++.

Right, so I have a game engine. It handles different game states. Something like this:

Code: [Select]

// main.cpp

// include things

int main ()
{
  Engine* MyEngine = Engine::GetInstance();
 
  // declare game states
  PtrToGameState Title(new TitleState(TITLE_STATE));
  PtrToGameState Map(new MapState(MAP_STATE));
 
  MyEngine->AddState(Title);
  MyEngine->AddState(Map);
 
  MyEngine->Init(...);
  MyEngine->Run(...);
 
  return 0;
}


The engine stores the game state objects in a vector, and when it's running it iterates through this vector and calls some function:
Code: [Select]

// hopefully this piece of code is easy to understand...
while (running && lState != EXIT_STATE)
{
  for (iter = states.begin(); iter != states.end(); ++iter)
  {
    if ((*iter)->GetState() == lState)
    {
      (*iter)->Init();
      lState = (*iter)->Mainloop();
    }
  }
}


So, with this I have a nice state manager-like thing, which handles various states I create, as long as I inherit from an abstract base class called, GameState.
However, the state objects can't communicate with eachother. I try to explain what I mean, and want to achieve.
I start a new game, and I'm playing it for 2 hours, then decide to call it a day. So I want to save my progress. But with this code I can't do that!
The TitleState don't know where I am in the MapState, so even though TitleState has a Save Game function, it doesn't know what I want to save. (And the engine only knows the GameState base class.)

I hope you understand my problem, and give me some advice on what should I modify in my design. Because I am out of ideas.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Design flaw in my game ?
« Reply #1 on: August 15, 2011, 06:20:57 pm »
Maybe a virtual GameState::Save() function that is implemented in each concrete state class? If you want to save the game, then this function is called for all elements of your states vector...
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
Design flaw in my game ?
« Reply #2 on: August 15, 2011, 07:09:54 pm »
Perhaps some kind of centralised event mapping system?

If you want to save, simply fire off an event state and anything that wants to listen to it handles it. And if not, then that state simply ignores it.
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

unranked86

  • Newbie
  • *
  • Posts: 37
    • View Profile
Design flaw in my game ?
« Reply #3 on: August 16, 2011, 04:45:39 am »
Quote from: "Nexus"
Maybe a virtual GameState::Save() function that is implemented in each concrete state class? If you want to save the game, then this function is called for all elements of your states vector...


Yes, you are right :) It seems I was thinking too much, so I forgot about the easiest solution. But is this the best way ? Or should I not worry about it, just implement it ?

Quote from: "MorleyDev"
Perhaps some kind of centralised event mapping system?

If you want to save, simply fire off an event state and anything that wants to listen to it handles it. And if not, then that state simply ignores it.

I'm really sorry, but I'm not quite sure what you mean. Do you mean some system like the "observer pattern" ?

Disch

  • Full Member
  • ***
  • Posts: 220
    • View Profile
Design flaw in my game ?
« Reply #4 on: August 16, 2011, 04:54:04 am »
I don't think the GameStates should own a Save function.  That doesn't really make sense.

Does the game you save differ from state to state?

If no, then it doesn't make logical sense to put it in every state class, and it would be a bunch of duplicate code anyway.

If yes, then how do you plan to load it?  What if you try to load a game in state A that was saved in state B?  Would you have to switch to state B?


I would have game saving/loading part of the overall Game class.  States should have access to "Game wide" info in one form or another anyway.  Just throw the save function with that.

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
Design flaw in my game ?
« Reply #5 on: August 16, 2011, 02:42:34 pm »
Quote from: "unranked86"

Quote from: "MorleyDev"
Perhaps some kind of centralised event mapping system?

If you want to save, simply fire off an event state and anything that wants to listen to it handles it. And if not, then that state simply ignores it.

I'm really sorry, but I'm not quite sure what you mean. Do you mean some system like the "observer pattern" ?


Exactly. I rolled my own awhile ago that looked like this:

Code: [Select]

bool ASaveFunc(jml::typeinfo type, SaveEvent event)
{
      // do some saving
      return jml::typed_event_map::CONTINUE;
}

jml::typed_event_map events;
jml::typed_event_map::connection c = events.connect(typeid(SaveEvent), ASaveFunc);

events.queue(typeid(SaveEvent), SaveEvent); // Queue an event to be triggered
eventes.flush(); // Triggers all queued events

events.trigger(typeid(SaveEvent), SaveEvent); // Trigger an event, calling it instantly

c.disconnect();


Where typed event map is actually a specialisation of a generic "event_map" templated class that can use any hashable type as a key and any copyable class as a value.

I'd show it but it relies on a few other things I rolled for ease-of-use. A typeinfo wrapper that on windows uses hashes to avoid unneeded string comparisons whilst still crossing library boundaries (jml::typeinfo), a function that binds member functions to a functor without needing to use placeholders (by using variadic templates in gcc or just a lot of overloads in visual studio)...it'll all be opensource eventually xD

But yeah, maybe the state shouldn't be the saver...
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Design flaw in my game ?
« Reply #6 on: August 16, 2011, 02:59:23 pm »
The virtual function I proposed points to the same direction, but maybe GameState isn't the best place for it, as Disch stated.

For existing event systems, you could take a look at the Boost.Signals library. It is of advantage if you are familiar with C++ callables, like provided by Boost.Function and Boost.Bind.

I have also implemented a small EventSystem class in Thor, mainly for SFML events. But as it's a template, you can use any event type.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

unranked86

  • Newbie
  • *
  • Posts: 37
    • View Profile
Design flaw in my game ?
« Reply #7 on: August 16, 2011, 03:56:10 pm »
So, I can't avoid using function objects :) They seem powerful to me, but I've not yet grasped the idea behind them. I recently found a nice article about Boost.Function and Bind on gpwiki. But there is something I don't really get. Can function objects have different parameters with the same name (the return-type don't really matters) ? Like: func (), funct (int a), func (std::string), func (int a, std::string abc)

By the way, I checked Thor::Event yesterday before I opened this thread, and was thinking maybe I can use it for my states, it provides a very similar functionality.

So, polymorphism is not the best way to write such a state manager, is it ?

Disch

  • Full Member
  • ***
  • Posts: 220
    • View Profile
Design flaw in my game ?
« Reply #8 on: August 16, 2011, 04:28:10 pm »
Quote
So, polymorphism is not the best way to write such a state manager, is it ?


I think it's a fine way to do it.  It's how I do it myself.

IMO, The problem is you're trying to build something that has nothing to do with states into your states.

I think you're making this more complicated than it needs to be.  I would just do this:

Code: [Select]

struct GameEnvironment
{
  // .. various "game wide" data that can be used in every state

  // throw the Save function in here
  void Save();
};


Create one instance of that class in main and pass a pointer to it to each game state when they're created.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Design flaw in my game ?
« Reply #9 on: August 16, 2011, 04:29:50 pm »
You can also use polymorphism. In fact, almost all flexible event systems use it internally to dispatch to different functions. But in C++, you should also be familiar with function objects, as you mention they are very powerful and often allow less coupling and less boilerplate code. std::function and std::bind are also part of the coming C++ standard, and they are in my opinion among the things every C++ programmer should know.

By the way, a function object or functor is just an object of a class that overloads operator(). Instances of std::function/boost::function are more sophisticated function objects. It depends on the template parameter of the function class which functions you can assign to it. The signature must be compatible.

You should carefully read the official documentation of Boost.Function and Boost.Bind, everything is explained there.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

unranked86

  • Newbie
  • *
  • Posts: 37
    • View Profile
Design flaw in my game ?
« Reply #10 on: August 16, 2011, 05:22:31 pm »
Quote from: "Disch"

I think you're making this more complicated than it needs to be.


That's what I think, as well :)

Quote from: "Disch"

I would just do this:

Code: [Select]

struct GameEnvironment
{
  // .. various "game wide" data that can be used in every state

  // throw the Save function in here
  void Save();
};


Create one instance of that class in main and pass a pointer to it to each game state when they're created.


Something like this ?
Code: [Select]

// this is from the OP
GameEnvironment* ge = GameEnvironment::GetInstance();

/*...*/

// declare game states
PtrToGameState Title(new TitleState(TITLE_STATE, ge));
PtrToGameState Map(new MapState(MAP_STATE, ge));


Quote from: "Nexus"
You should carefully read the official documentation of Boost.Function and Boost.Bind, everything is explained there.

That's what I'll do, when I got the time :)

After all of these replies, I think something is moving in my head. The GameState and all of its derivatives are good as they are, and iterating through a vector is fine, I just need to pass each state an "all-purpose" object. (and I need to expand my knowledge with the function objects, just in case :) )

keyforge

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
Design flaw in my game ?
« Reply #11 on: September 11, 2011, 04:35:06 am »
I did something similar before and it worked. I had a singleton Game class (I probably shouldn't of used a singleton, and I should of just passed down a pointer to the game class and stored it in each state like suggested above!) which had an instance of my world class, and the world class stored my map. My map class had a public method SaveToFile(filename) which saved the map to a file. I also could call LoadFromFile(filename) when I needed (for say, a gui textbox where you enter a map name and load it from a file in the menu state)

In psuedo-code form...

Pause_GameState.hpp
Code: [Select]

if(guiButtonIsPressed()) { // guiButtonIsPressed() is psuedo code for if the save GUI button is called

Game::GetInstance().GetWorld().GetMap().SaveToFile(name);

}
Need a place to upload your code, files or screenshots? Use SFML Uploads!

coolhome

  • Jr. Member
  • **
  • Posts: 59
    • View Profile
Design flaw in my game ?
« Reply #12 on: September 11, 2011, 07:41:11 am »
Don't know if this will be any help to you but in my engine I have a screen (state) manager that is a singleton class. I also give each screen a unique name. So in one screen I could pull out a code like this

Code: [Select]
XDE::ScreenManager::GetPtr()->GetScreen["MainMenu"]->SomeFunction();

However since its saved as the base class you need to cast it to the original screen to pull out functions not in the XDE::Screen.

Code: [Select]
MainMenu menu* = static_cast<MainMenu*>(XDE::ScreenManager::GetPtr()->GetScreen["MainMenu"]);

A little lengthy I know but it gets the job done ;) I usually overlap screens. For saving tho I would create a screen run some functions and it will deactive itself and do a callback onto some function to tell the main program whats up.

I hope that wasn't too confusing! lol
CoderZilla - Everything Programming