c++ class design questions

Started by
5 comments, last by kingos 22 years, 1 month ago
hi everyone. i am writing a "magic the gathering game", and as i am an object oriented convert am having a bit of trouble deciding what should go where in terms of classes. More specifically, this game has a Game class to initialise the game and hold important info and stuff, Player class for each player (eg. Player has a Hand and a Deck, each of which are lists of Cards). I am stuck with regard to how I should do each players Turns. I started off with a Turnstate class, which I would initialise everytime I called Player p.newTurn(), which would remember which part of the turn the player was in. (oh yeah, every turn in magic the gathering has a bunch of different phases). I worked out this sucked, since the other player couldn''t find out what part of the turn it was up to, when he needed to do things. So i decided just to have an enumerated type TurnState, and put it in game, so typedef enum {UntapPhase,...,HealCreaturesPhase } TurnState; and have an instance of it in the game class. but then where do the functions that affect that go? like setCurrentPhase() or something? hmmmm. should I go back to having a Turnstate class with a variable called state inside that or something, and have an instance of that class in game? or just add the functions to the game class? or what? any good articles on knowing what to put in each class, and how to divide it up?
Advertisement
did I put this question in the wrong forum? how come nobody has replied??
It''s only been an hour, and it''s the middle of the night (well, close enough) in the US. Be patient.

I couldn''t be bothered to read the question right now, but I''ll definitely answer tomorrow.

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! ]
Thanks to Kylotan for the idea!
From what I''m reading, I understand that you''re creating a 2 player game.

The most object-oriented way to do this is to have each state as an object.

You''ll need to have a abstract State class:
  class State{public:virtual void  Process(Player* player) = 0;virtual void  EndPhase() = 0;int StateID; //for run-time identification};  


Then for each phase you''ll need to have a concrete State class:
  class Upkeep : public State{public:virtual void  Process(Player* player) ;virtual void  EndPhase();};class DrawPhase : public State{public:virtual void  Process(Player* player);virtual void  EndPhase();};//and so on...  


Your player class will look like this:
  class Player{public:void  Process();State* CurrentState;};  


Process():
  void Player::Process(){CurrentState->Process(this);}  




With this technique, you do not have a single Process Function but from a client programmer''s point of view, it will seem so.

Instead all your processing will be different; there will be a Draw Phase process function as well a Upkeep one.

For example:
  //Same clas from the above source:void DrawPhase::Process(Player* player){player->DrawCard(Library->GetCard());this->EndPhase();}  



Of course, many details are missing but if you get the architecture correct, everything will fall into place nicely (this doesn''t happen in real-life though).
okay Darkor thanks, that looks really good.

a few more questions then:
so to change the state to a new one, I would do something like:
     void Untap::EndPhase(Player *p) { p->CurrentState = new Upkeep; }  

that kind of seems ugly, since you might do a lot of stuff inside the constructor of Upkeep that
you would normally want to do at the beginning of the next phase?

the other thing is, in phases like Main, both players get to do stuff, such as
the other player is allowed to play cards as well during the other players turn.
In the current situation, I guess I could have a function

int Player::GetCurrentState() {
return StateID;
}


in my game class at present i have

Player *Front, *Rear; // where Front is current player

when I initially tried this approach, I ended up having to store a pointer to the game class inside each player, and then doing something like

current_player_pointer->game_pointer->other_player_pointer->GetCurrentState();
[/source]
which looks shocking, and made my code separation or whatever it is called (keeping classes separate)
absolutely disastrous.
Have I misread that somewhere? or how can I get around this problem?


Edited by - kingos on February 25, 2002 4:01:52 AM

Edited by - kingos on February 25, 2002 4:04:32 AM
One way that works for me is to have a state return the next state when it''s finished, so you can assign it. I just return an enum which tells me which state object to instantiate.

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost ]
You got the idea right but instead of handling the change of States within the state itself, you set a flag to the next state. So you give each state a pointer named NextState. You set it to NULL initially but when you call Untap::EndPhase(); it assigns the next state based on the conttext given during that phase.

During each time you process the player, you test if the NextState is NULL. If it isn't you cleanup the current state and create the new state. Hopefully, all your data is stored within the player class because the states are there just to manipulate data based on decisions made in different contexts.

What do you want to do in constructor of Upkeep that you might want to do in the next phase?

quote:
    int Player::GetCurrentState() {return StateID; }  



Right but instead you'll want it like that:

  int Player::GetCurrentState() {return CurrentState->GetStateID(); }  



So you don't have to set the state manually each time. Since the state itself stores its own ID, you just return this value.


About the second player, there are a couple of issues you might want to address first. Firstly, why limit yourself to two players? Chaos magic was fun, though some matches didn't last long. =P Secondly, why do you have to store a pointer to the second player.

I propose having a list of players. And processing only goes to one player. I don't know how you want this game to be played: over a network or on one computer. I will assume that it is over a network (or internet).

On the current player's computer, you'll process the player normally. However, on the other players' computers, you process a NotMyTurnState. All this does is update what's on screen but he can cast instants. Also he will get a chance to react to other spells. He might want to cast a Counterspell or a Force Of Will.


Creating a game like this is far more difficult than I thought. You'll need far more than just player classes. But since you've understood what I've been saying thus far, I think that you'll have a fair chance at finishing it. =P

Okay, so maybe your server computer will have a game server instance class:

       class GameServerInstance{public:void Process();private:vector<Player> Players;int CurrentPlayer;};GameServerInstance::Process{for (int i=0; i<Players.size(); i++){Players[i]->Process();}}  


This is made more complex since opponents get to cast instants during their turns. Okay, I think a way to solve this is to create an action dialog. Whenever a player wants to cast something, the other players will have to wait for him. The current player will get the priority though.

You won't need to check the state of other players because well, they'll be in the NotMyTurn State.

Oh yeah, remember to define virtual destructors for your abstract classes.

Edited by - Darkor on February 25, 2002 9:25:31 PM

This topic is closed to new replies.

Advertisement