Jump to content
  • Advertisement
Sign in to follow this  
Master Jake

Game Controls and Logic Question

This topic is 2516 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've been messing around with OpenGL/GLUT lately, and there's always been this design problem bugging me since I started creating Windows applications (instead of console) back with SDL. I would think it appropriate to separate the window control stuff from the actual game logic (e.g. separating the code that checks for mouse clicks from the code that actually moves the player to the location that you clicked the mouse). But this has posed a great deal of stress on me. I've yet to figure out a way to do this without using a ton of globals (or by faking globals through using static class singletons). Can someone please post a very small design snippet of an efficient and organized interface or help me out with this problem somehow?

One idea I had was to have all the game data extend a base window class which has all the control information in it like so:

class Window
{
// stuff for window events and callbacks
// updating key states and mouse states, etc.
};

class Game
{
public:
void Update()
{
// loop through all game objects and
// call their update functions
}

private:
GameObject objs[10]; // create 10 game objects
};

class GameObject : public Window
{
// inherits all of the window traits such
// as the current mouse coordinates, window width/height,
// etc.
};


But from a true inheritance viewpoint, it makes absolutely no sense whatever to have a game object inherit window properties. However, what alternative is there?

Share this post


Link to post
Share on other sites
Advertisement
Something like this:

class MouseListener
{
public:
virtual ~MouseListener(){}
virutal void onClick(int x, int y, MouseButton button, /* ... */) = 0;
};

class Window
{
public:
Window(Mouse *mouse, MouseListener *mouseListener)
:
mouse(mouse),
mouseListener(mouseListener)
{
}

void update()
{
if(mouse->clicked())
{
mouseListener->onClick(mouse->x(), mouse->y(), mouse->button(), /* ... */);
}
}
private:
Mouse *mouse;
MouseListener *mouseListener;
};

class Player
{
public:
void shoot();

// ...
};

class PlayerShootMouseListener : public MouseListener
{
public:
PlayerShootListener(Player *player)
:
player(player)
{
}

virtual void onClick(/*... */)
{
player->shoot();
}
private:
Player *player;
};

int main()
{
Player player;
PlayerShootMouseListener mouseListener(&player);
Mouse mouse;
Window window(&mouse, &mouseListener);

while(true)
{
// input checking
mouse.click(42, 13, MouseButton::Left);

// Later ...
window.update();

// Rendering etc...
}
}

As you can see, the GUI knows nothing about game objects and the game object knows nothing about the GUI. If you're using boost then something like boost::function<> can be used to reduce the amount of boilerplate code required.

Share this post


Link to post
Share on other sites

Something like this:

class MouseListener
{
public:
virtual ~MouseListener(){}
virutal void onClick(int x, int y, MouseButton button, /* ... */) = 0;
};

class Window
{
public:
Window(Mouse *mouse, MouseListener *mouseListener)
:
mouse(mouse),
mouseListener(mouseListener)
{
}

void update()
{
if(mouse->clicked())
{
mouseListener->onClick(mouse->x(), mouse->y(), mouse->button(), /* ... */);
}
}
private:
Mouse *mouse;
MouseListener *mouseListener;
};

class Player
{
public:
void shoot();

// ...
};

class PlayerShootMouseListener : public MouseListener
{
public:
PlayerShootListener(Player *player)
:
player(player)
{
}

virtual void onClick(/*... */)
{
player->shoot();
}
private:
Player *player;
};

int main()
{
Player player;
PlayerShootMouseListener mouseListener(&player);
Mouse mouse;
Window window(&mouse, &mouseListener);

while(true)
{
// input checking
mouse.click(42, 13, MouseButton::Left);

// Later ...
window.update();

// Rendering etc...
}
}

As you can see, the GUI knows nothing about game objects and the game object knows nothing about the GUI. If you're using boost then something like boost::function<> can be used to reduce the amount of boilerplate code required.


Lovely example, thank you very much! :)

Edit: Would it be a good idea to make Window class have a dynamic array of mouse listeners and to say loop through all of them and call their individual click functions when the mouse is clicked? This way I can have multiple mouse listeners. I just wonder if that would effect the performance greatly in some negative way.

Share this post


Link to post
Share on other sites
Relative to your application, mouse clicks are rare. It probably won't matter if you have a whole bucket of mouse listeners.

Generally there are two types of game GUI - the kind where clicking anywhere triggers the same limited set of responses (move/attack/shoot) or a button oriented GUI. In button oriented GUIs, you might want each button to have a listener rather than iterating across every listener.

For hybrid games (e.g. RTS with a button oriented control panel and an action-oriented game map) you might want to divide the screen view port into two separate objects. But for such a game you probably shouldn't be rolling your own GUI library.

Share this post


Link to post
Share on other sites

Relative to your application, mouse clicks are rare. It probably won't matter if you have a whole bucket of mouse listeners.

Generally there are two types of game GUI - the kind where clicking anywhere triggers the same limited set of responses (move/attack/shoot) or a button oriented GUI. In button oriented GUIs, you might want each button to have a listener rather than iterating across every listener.

For hybrid games (e.g. RTS with a button oriented control panel and an action-oriented game map) you might want to divide the screen view port into two separate objects. But for such a game you probably shouldn't be rolling your own GUI library.


Thank you so much. You've been very helpful, and that polymorphism example you've showed me is really going to help with a lot of my future applications.

Share this post


Link to post
Share on other sites
Using polymorphism is a simple way to do it, requiring no additional dependencies. Using boost::function (soon to be std::function) you won't even need to write such interfaces. For stateless callbacks a function pointer can be the simplest solution, but it is also the least flexible.

As an aside, we generally prefer not to mark threads as "solved" on these forums. This is a discussion. For example, someone else may want to weigh in with some more advice, or perhaps a concrete example using boost/std function or might have a question about this design.

Share this post


Link to post
Share on other sites

Using polymorphism is a simple way to do it, requiring no additional dependencies. Using boost::function (soon to be std::function) you won't even need to write such interfaces. For stateless callbacks a function pointer can be the simplest solution, but it is also the least flexible.

As an aside, we generally prefer not to mark threads as "solved" on these forums. This is a discussion. For example, someone else may want to weigh in with some more advice, or perhaps a concrete example using boost/std function or might have a question about this design.


Ah, sorry about that. Didn't know.

Share this post


Link to post
Share on other sites
What I'm about to suggest is most-likely overkill for most hobbyist games. While the listener pattern does work great for this, the logic will still be tied to the specific callbacks. If you wanted to provide support for joysticks or other external controllers your logic would need to be re-factored.

You can completely decouple input from logic by having a central location where window input events are translated into logic input events. For example: you can have events like:


class PlayerMoveEvent : public LogicEvent {

public:

PlayerMoveEvent(const Vector3& movement);

};

class PlayerShootEvent : public LogicEvent {

public:

PlayerShootEvent(BulletType bulletType, const Vector3& direction);

};


These events are dispatched to whatever code handles the logic. This could also be implemented with listeners, however I prefer using events which gives me the option of asynchronous event handling (also my logic is implemented in Lua and events are more natural to implement than polymorphism).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!