Sign in to follow this  

Separating graphics, input, game logic etc.

This topic is 3627 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm kind of confused about how I should separate handling graphics, input, game logic etc. How separated should they be? Is it better to give objects that need to be drawn a reference to the graphics class as a member and a member function like Render()?
class Paddle {
  public:
    void Render() { m_refGraphics.Draw(m_sprPaddle); }
  private:
    Graphics m_refGraphics;
    Sprite   m_sprPaddle;
};



Or should I give the graphics class a member function to draw the object?
class Drawable {
  protected:
    m_sprite;
};

class Paddle : public Drawable {
};

class Graphics {
  public:
    void Draw(const Drawable&);
};



And how should I separate input from game logic? Should I have a HandleInput() function which reads input and stores it in an array that can be used by a GameLogic() function? These were some of the questions I had in mind but the whole way of keeping things separated is kind of vague to me. Maybe somebody could point me to some nice guidelines to follow or a link to a good article about this? Thank you in advance ^^.

Share this post


Link to post
Share on other sites
I would choose the second version. Because it does not make sence, that the paddle even knows that it can be rendered. Look at the MVC (model view controller) principle. The paddle is in the model. The graphics is in the view. The view draws what is in the model. So the graphics draw the paddle.

Share this post


Link to post
Share on other sites
Neither. Paddle is a game concept which should be independent of its representation. A game should be able to be run without drawing anything to the screen (though it might be hard to play if you can't see where the ball is).

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Neither. Paddle is a game concept which should be independent of its representation. A game should be able to be run without drawing anything to the screen (though it might be hard to play if you can't see where the ball is).
Agreed, however, this is often easier said than done (cleanly, at least). I am working on a similar problem currently.
Definitely worth it it the end though, since you now have the flexibility of generating different "views" of the game: screen, network, AI, replay file, whatever.

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Neither. Paddle is a game concept which should be independent of its representation. A game should be able to be run without drawing anything to the screen (though it might be hard to play if you can't see where the ball is).


But if I plan to make a game where you can see the paddle, how should I draw it?

Share this post


Link to post
Share on other sites
Something like this would work:

Game Objects -> Event/Messaging System -> Event/Message Listeners (eg: renderer)

Your renderer could be listening to update events for certain game objects and then render them. Your AI could also be listening for such events and reach to them. Network code could use the events to send data accross the network. And so on.

The difficult part now is designing your event system .

Share this post


Link to post
Share on other sites
The game object would generate an event, the event would contain some data/parameters with details about the event (to describe the event, the sprite id could be part of this) and then the listeners would use the description of the event to handle it in some way.

There are, of course, many ways your original problem could be solved, this is only one way.

Share this post


Link to post
Share on other sites
My current approach is having a rendering system that's responsible for loading, drawing and managing images. Any game object that needs a visual representation receives a reference to this renderer object, so it can request the rendering system to create a sprite object (one, or multiple, or whatever) at a specified location, with a specified image. The rendering system then returns a handle to that sprite object, so the game object can manipulate their sprite: move it around, toggle it's visibility, select a different animation, remove it, whatever actions make sense. The renderer is responsible for drawing it, though. Since drawing happens in a single place, it's much easier to add optimizations there.

In other words, game objects tell the rendering system what to draw and where to do it, and they'll update that information from time to time. The rendering system is unaware of what and why it draws things, and game objects aren't cluttered with drawing functionality.


It may not be a perfect solution, but it works well for me so far. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Arjan B
But which part should know about which sprite to draw? The game object, the event system or the event listener?


The listener, sort of.

The game object is the paddle. It exists at X/Y on the playfield, is XxY dimensions, has X properties, is controlled by X player; it will contain a series of events (size changed, location changed).

When created, a sprite is also created that looks like a paddle and added to the scene. The events are then tied between the paddle and the sprite by the Game (which does the creation and knows about the game rules and the presentation). Paddle change location causes sprite change location (proportional based on the differences between the playfield game object and the visual area that is the playfield).

[note that a similar tying mechanism is used to make (arrow-up) move the game object paddle and other input]

At that point you're free to swap out the paddle or the sprite implementation without the other knowing about either.

Share this post


Link to post
Share on other sites
Telastyn is correct, I kinda did not think of that earlier...
The game object shouldn't know anything about the graphics system. That is, the paddle object does not know (or care) if its represented by a sprite, a polygon, a 3D mesh or even an entire particle system.
Also, the events don't care how the paddle object, which generated the event in question, is being represented graphically, because it doesn't know what/who the listeners are (does the AI care which sprite is being rendered? probably not. Does the networking code care? Maybe, maybe not. The renderer is the only thing that definitely cares).

So, the listener (in this case, the renderer) stores this.
Maybe each game object has a unique ID and you can assign this ID to renderable resources (managed and owned by the renderer, no other part of the game needs to know about these).

So, the paddle (game object 0) generates a "moved" event. The renderer, being a listener listening for "moved" events gets the event, which contains the id of whatever object generated the event (0), looks up its table of game objects which were assigned resources and sees "AHA! Game object 0 corresponds to this sprite here" and draws it at whatever position the event says it has moved to.

This is similar to Captain P's method, except that the game objects do not hold the references, the renderer does.

Hope that helps.

[Edited by - issch on January 7, 2008 4:02:26 PM]

Share this post


Link to post
Share on other sites
I think I understand now :D Thank you very much all!

One more question though, is it a good idea to make an event handler of some sort with a stack of events? Where things like the graphics and input handler can query for the events they're interested in?

Share this post


Link to post
Share on other sites
There is no need for tricks, try to keep it simple. I'd suggest doing something like this :


class CObject
{
void Update(); // update position, etc.
void Move(int x, int y);
void Set_Position(int x, int y);
// ...
};

class CGeometryObject : public CObject // or renderable object...
{
virtual void render(); // draw the object
};

class CPaddle : public CGeometryObject
{
void render()
{
m_sprite->render();
}
}


Now you can :
- keep a list of all CObjects (update all at once)
- keep a list of all CGeometryObjects in scene, or whatever structure you have to be able to render everything at once.
- customize rendering

Share this post


Link to post
Share on other sites
Quote:
Original post by Arjan B
I think I understand now :D Thank you very much all!
No problem .

Quote:

One more question though, is it a good idea to make an event handler of some sort with a stack of events? Where things like the graphics and input handler can query for the events they're interested in?
I don't know, does it make sense to you to do this?
Personally, I am using a queue (because with a stack the latest events are always processed first, while I'd rather process them in the order that they were generated).

I have my listeners subscribe to the events they want to receive (each of my events has an event type by which they can be identified) and then the event system will call the listeners callback with the event that was generated as a parameter.

Basically, my event system is more or less an Observer pattern. (Though I do some other things, for example, I only process the events once all events have been generated for that frame and if an event handler generates an event, it will not get processed until the next frame).

Quote:
Original post by Arex
There is no need for tricks, try to keep it simple.
I prefer to keep things separated, which was what the OP was asking about.
I like game objects being treated separate from the graphics objects and audio objects which represent them, because it allows me to do some neat things, like create a number of differing views of the game.
For example, the player gets the graphical view, AI's get a different view - the same information is given to the AI's as to the player, but it a format that they can understand. The networking code (if the game is multiplayer) can get a view tuned to work with your networking code. You can also generate a view which is to be logged (for later playback? or analysis? whatever) and so on.
Is this overkill? Maybe, it depends on your game. For me, this works well. Obviously, your way works for you (I saw your screenshots, very nice, btw ).


To the OP: it's important to keep things simple, but it's also important to lay things out logically. Besides the extra code for managing events, personally, I find my way simple and definitely more logical. For my purposes at least. My advice is to pick whichever method works best for you and your game! You know my way, perhaps think about whether Arex's method may suit you better and then go from there . If you have any more questions on my method, feel free to ask.

[Edited by - issch on January 9, 2008 6:34:38 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Arjan B
I think I understand now :D Thank you very much all!

One more question though, is it a good idea to make an event handler of some sort with a stack of events? Where things like the graphics and input handler can query for the events they're interested in?


Sure. Events and messages are just a means to an end. The implementation details will vary upon requirements.

Share this post


Link to post
Share on other sites
I really like the idea of the Observer pattern. I'm trying to figure out how to implement it and am running into some problems. But I'll create a new thread for that since it's not really on-topic ^^.

Thanks again all!

Share this post


Link to post
Share on other sites
Okay I've given a try at a simple MVC implementation but run into some errors of which I know how to solve, but if I did it that way it would kind of screw up the whole MVC design (I think..).



SOURCE CODE

mvc.h

#ifndef MVC_H
#define MVC_H

#include <list>

// prototypes
class Model;
class View;
class Controller;

class Model {
public:
virtual void Attach(View*);
virtual void Update(void);
private:
std::list<View*> viewList;
};

class View {
public:
View(void): model(NULL) {}

virtual void Attach(Model*);
virtual void Update(void) = 0;
virtual void Render(void) = 0;
private:
Model* model;
};

class Controller {
public:
Controller(void): model(NULL) {}

virtual void Attach(Model*);
virtual void HandleInput(void) = 0;
private:
Model* model;
};

#endif




mvc.cpp

#include "mvc.h"

void Model::Attach(View* v) {
viewList.push_back(v);
v->Attach(this);
}

void Model::Update(void) {
for (std::list<View*>::const_iterator i = viewList.begin();
i != viewList.end(); i++)
{
(*i)->Update();
}
}

void View::Attach(Model* m) {
model = m;
}

void Controller::Attach(Model* m) {
model = m;
}




game.h

#ifndef GAME_H
#define GAME_H

#include "mvc.h"

class Game : public Model {
public:
void SetData(char);
char GetData(void);
private:
char data;
};

class Display : public View {
public:
void Update(void);
void Render(void);
private:
char data;
};

class Keyboard : public Controller {
public:
void HandleInput(void);
private:
char input;
};

#endif




game.cpp

#include "game.h"
#include <iostream>

void Game::SetData(char c) {
data = c;
}

char Game::GetData(void) {
return data;
}

void Display::Update(void) {
data = model->GetData();
}

void Display::Render(void) {
std::cout << data << std::endl;
}

void Keyboard::HandleInput(void) {
std::cout << "Input a character: ";
std::cin >> input;
model->SetData(input);
}




main.cpp

#include <iostream>

#include "game.h"

int main()
{
Game pong;
Display screen;
Keyboard keyb;

keyb.Attach(&pong);
pong.Attach(&screen);

while (1) {
keyb.HandleInput();
pong.Update();
screen.Render();
}

system("pause");

return 0;
}






ERRORS

Quote:

In member function 'virtual void Display::Update()':
'Model* View::model' is private

I could solve this by making View::model (edit:)protected, but I'm not sure if that's a right thing to do. (Same thing at Keyboard::HandleInput())

Quote:

In member function 'virtual void Keyboard::HandleInput()':
'class Model' had no member named 'SetData'

I could solve this by moving the Set/GetData() functions to the base Controller class, but not all controllers should have the same Set/GetData() functions. (I have to add different kinds of those later)



QUESTION

I kind of understand the idea of the MVC pattern, but not enough to implement it. Could someone give a better solution to these errors than the ones I thought of myself? And maybe point me in the right direction if I'm in the wrong one ^^?



Thank you in advance ^^.

[Edited by - Arjan B on January 10, 2008 10:16:33 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Arjan B
Quote:

In member function 'virtual void Display::Update()':
'Model* View::model' is private

I could solve this by making View::model private, but I'm not sure if that's a right thing to do. (Same thing at Keyboard::HandleInput())
Emphasis is mine. You mean protected, right? If all the derived classes should be able to directly access those variables, then yes, make them protected.

Quote:

Quote:

In member function 'virtual void Keyboard::HandleInput()':
'class Model' had no member named 'SetData'

I could solve this by moving the Set/GetData() functions to the base Controller class, but not all controllers should have the same Set/GetData() functions. (I have to add different kinds of those later)
Your Model class does not have a SetData() method, Game does (which is derived from model). A simple way of solving this is to type cast model to Game* before calling the method. Not sure if its the best solution, but its a solution.


Quote:

Could someone give a better solution to these errors than the ones I thought of myself? And maybe point me in the right direction if I'm in the wrong one ^^?
There are some modifications I, personally, would make, but it looks like a decent start to me. IMHO, the most important things are that the code is logical and easy to understand, for you and that it is easy for you to modify or add functionality later. If you have that, then whichever solution you choose is good.



Quote:
Thank you in advance ^^.
You're welcome.

Share this post


Link to post
Share on other sites
Ahh yes I meant protected sorry ^^. Thank you for your help (in the other thread too), I rated you highest by the way :P.

I think this way it will work out pretty well except that I've always had a feeling that using Get/Set()s wasn't very good so as soon as next week's exams are over I'll start thinking of a way to get events in there.

Once again, thank you all ^^.

Share this post


Link to post
Share on other sites
Quote:
Original post by Arjan B
Thank you for your help (in the other thread too)
Just glad I can help someone.

Quote:

I've always had a feeling that using Get/Set()s wasn't very good
Agreed. Theres a discussion about them starting with this post.

Quote:

next week's exams
I, too, have exams next week .



Share this post


Link to post
Share on other sites

This topic is 3627 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this