Separating graphics, input, game logic etc.

Started by
18 comments, last by guywithknife 16 years, 3 months ago
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]
Advertisement
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?
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
Sincerely,Arto RuotsalainenDawn Bringer 3D - Tips & Tricks
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]
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.
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!
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>// prototypesclass 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]
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.

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 ^^.
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 .



This topic is closed to new replies.

Advertisement