• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Tispe

The inevitable God object

22 posts in this topic

Hello

 

Anyone else experiencing that most of your code end up in a God Object?

 

Over some time I have toyed with several different programming ways. From pure procedural to strict OOP. As I program my game I encounter problems such as how to move messages between objects. Even messages across a network. These messages has to be properly identified, dispatched and call the correct methods. All messages are derived from a base object such that they can all be stored in the same STL container.

 

We can take advantage of double dispatch to avoid down casting these message objects. However now a message has to be accompanied by all the possible objects it may interact with. So in order for this message to have that acces, we need an interface that holds everything, the God object, or GameManager or whatever.

 

C++ has so many quirks that most of my code are based on solving these quirks rather then focusing on the game itself.

 

Am I alone, is this a noob thing, or what?

0

Share this post


Link to post
Share on other sites

I've noticed that some classes can become large and full of utility functions, but as ApochPiQ mentioned, that is mostly due to design choices and habits.

 

 

Large games are not so much about hacking some code together until it is a game, instead they are rather intense software architecture and design systems.

 

In complex games simply adding a few bytes to an object can completely destabilize everything. 

 

Example:

 

The ubiquitous GameObject base class tends to become a megalithic object. Basically everything derives from it, so when a new feature is added many programmers find it quite tempting for addition. ... After all, the junior programmer may reason, adding a few virtual functions and some data members to GameObject won't hurt anything.

 

Unbeknownst to the junior programmers, the senior programmers have set email triggers on systems like the massive game object, and also on texture objects, mesh objects, particle systems, and all the other high-instance, high-memory, and high-performance source files. They do it for exactly this reason -- these systems are tempting targets for less disciplined programmers. They are sometimes tempting for the senior developers as well, but they know they'll need to defend their decision so they are more careful about it.

 

To the junior programmer's surprise, within seconds of submitting the code they receive some emails from two different senior programmers, they are told the change was reverted and a few minutes later there is a meeting scheduled in a available conference room. Why? Assuming your game engine supports several million game objects in it, some junior-level programmer comes along adding 3 floats to it (also in the wrong place so they add 16 bytes instead of 12) they just increased the memory requirements by a whopping 64 megabytes. The senior developers all have a watch flag in source control so ANY change to GameObject.h triggers a batch of emails.

 

End example.

 

 

There are many good solutions even though the solutions come with a cost. It is a tradeoff you must make.

 

A common tradeoff is to isolate features by abstract base classes. Yes, you need to pay a few cycles to check if the object is the correct cast -- usually by simply casting to the desired type and checking for null if it fails -- but that small cost is typically less than the far greater cost of adding those bytes to the GameObject class and requiring every object everywhere pay the penalty.

 

Only thing I don't like about your example, is that code should be code reviewed before check-in. =)

2

Share this post


Link to post
Share on other sites

 

Hello

 

Anyone else experiencing that most of your code end up in a God Object?

 

Over some time I have toyed with several different programming ways. From pure procedural to strict OOP. As I program my game I encounter problems such as how to move messages between objects. Even messages across a network. These messages has to be properly identified, dispatched and call the correct methods. All messages are derived from a base object such that they can all be stored in the same STL container.

 

We can take advantage of double dispatch to avoid down casting these message objects. However now a message has to be accompanied by all the possible objects it may interact with. So in order for this message to have that acces, we need an interface that holds everything, the God object, or GameManager or whatever.

 

C++ has so many quirks that most of my code are based on solving these quirks rather then focusing on the game itself.

 

Am I alone, is this a noob thing, or what?

 

Why does a message need to be derived from anything ? , a message should preferably just be data so you should be able to use the same class/struct for all messages (no need for inheritance), if your messages have methods then they're either improperly named or are doing far too much.

 

Personally i would have a message dispatcher object, senders can then send any message they want to the dispatcher and interested objects can implement a listener interface (or inherit from a pure virtual listener class in languages such as C++) and then just subscribe to messages based on type, source, etc.

Then the dispatcher just have to call a callback method in the listener interface/class when a matching message arrives and leave the exact details of how to deal with the message to the recieving object. (messages can then be a simple struct with a type identifier, basic information about the sender and some data)

 

 

I agree with you and something that the Google ProcotolBuffers overdo in their design for example.

 

I too am all for the simple struct or list of arguments messages rather than the overly designed way of deriving them. Seems more like a futile exercise in OOP.

Edited by jbadams
Restored post contents from history.
0

Share this post


Link to post
Share on other sites

I also have problems with this - though usually I find it's my inexperience with code architecture engineering that's the problem.

 

By favoring use of composition over inheritance for many things, and keeping the God object as small as possible, it helps me reduce the problem.

 

Messaging is still a difficult problem for me - but less so when I realized that games are complex software, and that I shouldn't try to cram my entire game project into a single paradigm - different parts of the game code are better suited to different paradigms of architecture and different messaging systems suit different architectures better than others.

 

For example, I was writing messy code when I was trying to force my entire game project to use signals-and-slots. Then I was writing messy code when I switched over to forcing the entire project to use an event-based system. Now I realize that one paradigm does not fit all, architecturally-speaking, and that for parent-child hierarchies signals and slots work very well, but for other parts of the game architecture, it doesn't work very well and event systems or callbacks or plain ol' function calls work better (where better here means 'cleaner design').

 

I still write messy code, but just slightly less messy once I understood that.

Good architecture is something I'm still struggling to learn, so don't take my statement as an experienced view. smile.png

1

Share this post


Link to post
Share on other sites


For example, I was writing messy code when I was trying to force my entire game project to use signals-and-slots. Then I was writing messy code when I switched over to forcing the entire project to use an event-based system. Now I realize that one paradigm does not fit all, architecturally-speaking, and that for parent-child hierarchies signals and slots work very well, but for other parts of the game architecture, it doesn't work very well and event systems or callbacks or plain ol' function calls work better (where better here means 'cleaner design').

 

What's an example of a situation where callbacks work better than signals-and-slots for you? My simple-minded understanding of signals is that they are collections of callbacks...

0

Share this post


Link to post
Share on other sites

Why does a message need to be derived from anything ? , a message should preferably just be data so you should be able to use the same class/struct for all messages (no need for inheritance), if your messages have methods then they're either improperly named or are doing far too much.

 

Deriving messages can offer some advantages. In my Entity/Component-Implementation, this allows me to do neat stuff like:

struct MyMessage : public BaseMessage<MyMessage> // implementation generates an unique ID for this message class
{
    int data;
    float data2;
}

// a system using the message:

void System::Register(MessageManager& messages)
{
    messages.Subscribe<MyMessage>(*this);
}

void System::Receive(BasMessage& message)
{
    // eigther by using a convenience static-casting method
    if(auto pMyMessage = message.Convert<MyMessage>();
    {
        int i = pMyMessage->data;
    }
    // or potentially faster, though currently not working completely typesafe in MSVC due to lack of constexpr:
    switch(message.GetClassID())
    {
    case MessageType::MY_MESSAGE: // should really be MyMessage::ID();
        pMyMessage->data;
    }
}
    
// in game code:

m_pMessages->Send<MyMessage>(1, 2.0f);

I can't think of a way how this would work that easily without deriving (and template magic, too).

 

Oh, since we are on the topic, entity/component is a great architecture preventing god objects in gameplay/overall game design code.

Edited by Juliean
0

Share this post


Link to post
Share on other sites

What's an example of a situation where callbacks work better than signals-and-slots for you? My simple-minded understanding of signals is that they are collections of callbacks...

 

I kinda view signals as a more generalized and powerful form of callbacks, but sometimes overkill.

 

If I just want to run an function on every element of a container, passing in a callback (or functor) works perfectly. With signals and slots (in C++ specifically), both the container and the caller would have to support signals and slots, I'd have to attach a slot "DoSomethingToElement()" to the container's signal - which would be a weird signal, not really even an event - maybe called "OnElementAccessed" or something oddly forced sounding like that - and then call the container's "Crawl()" function so the signals get sent. It'd make much more sense to just pass a callback into the Crawl() function directly (and to rename the Crawl() function to something better suited like ForEvery()).

 

Callbacks are useful because they can be temporary or permanent (like signals and slots) but if they are temporary they don't require as much setup, and they don't require the caller to support slots.

 

Callback:

object.doSomething(onCallback);

Signals and slots: (when used for a temporary purpose)

object.attach(this, onCallback);
object.doSomethingThatSignals();
object.dettach(this, onCallback);

Signals and slots are more powerful and more flexible, but aren't always the best choice for me.

One of their pros is having multiple slots from multiple objects attached to the same signal, and another is the ease of use when an object has many signals that it emits.

 

It depends on the needs of the code. My logging system just takes a callback pointer that it permanently holds onto, to process log messages when they arrive. However, it might be good to expand it to take any number of functor objects, so I can have the logs outputted to multiple locations. That's a step inbetween callbacks and signals and slots.

 

I think my game objects will end up using alot of signals and slots (but that's still unknown to me), because they have alot of different types of interactions between each other and are in a parent-child hierarchy.

 

And my GUI stuff (since I'm using Qt) uses loads of signals and slots to great benefit - with a variety of different types of interactions and data being communicated. The signals going up to the parents and siblings, and regular function calls going down to the children.

 

Both the GUI (currently) and my game objects (in the future) also use regular event message queues - these are messages from the overall system rather than communication between two specific objects.

 

</inexperienced> </still-discovering-what-works>

Edited by Servant of the Lord
1

Share this post


Link to post
Share on other sites


Oh, since we are on the topic, entity/component is a great architecture preventing god objects in gameplay/overall game design code.

 

I don't see how you prevent the god object? You still need a way for a message to reach every object it may interact with. Either you need a god object that holds all other objects, like a phone book. Or you need coupling between objects, like a network. In the latter case, every object needs to store a pointer to every object it may want to interact with, which I think is a big mess.

0

Share this post


Link to post
Share on other sites

 


Oh, since we are on the topic, entity/component is a great architecture preventing god objects in gameplay/overall game design code.

 

I don't see how you prevent the god object? You still need a way for a message to reach every object it may interact with. Either you need a god object that holds all other objects, like a phone book. Or you need coupling between objects, like a network. In the latter case, every object needs to store a pointer to every object it may want to interact with, which I think is a big mess.

 

 

Disclaimer: I've never done what you're trying to do.

 

Instead of your Uber-Message-Object holding every object it needs to communicate with, wouldn't you just have the object hold a reference to the Uber-Object?

 

  1. Z = Uber-Message-Object
  2.  
  3. Object A subscribes to Z (ie. A has a reference to Z)
  4. Object B subscribes to Z
  5. Object C subscribes to Z.
  6. A sends a collision message to B.
  7. A sends: FromID (A), ToID (B), Data (id: 206, collide: true) to Z.
  8. Z passes messages to everyone subscribed (ie. puts message in queue)
  9. B checks message.
  10. B find ToID matches its ID.
  11. B reads Data.
  12. B processed Data.
  13. C checks message.
  14. C does not find match to ID.
  15. C ignores message.

 

Isn't this how it's works?

0

Share this post


Link to post
Share on other sites


I don't see how you prevent the god object? You still need a way for a message to reach every object it may interact with. Either you need a god object that holds all other objects, like a phone book. Or you need coupling between objects, like a network. In the latter case, every object needs to store a pointer to every object it may want to interact with, which I think is a big mess.

 

Not at all, I quess it depends on the implementation, but basically my implementation works as follows. You have one "manager" that holds a list of all the entities:

class EntityManager
{

// methods for accessing entities based on their components, for adding and removing entities, ...

private:

std::vector<Entitiy*> m_vEntities;
}

This is not a god class. All it is, is basically a better std::vector with some convenience methods for entities creation, removal and access. The entity class itself stores a list of components:

class Entity
{
// methods for querying, adding and removing components:
private:

std::vector<Component*> m_vComponents;
}

Again, this is merely like a more intelligent vector, and not a god class at all. A component is derived from a base class, and represents plain, stupid data:

class Position : BaseComponent<Position>
{
math::Vector3 vPosition;
}

And then there is systems, implementing actual behaviour:

class TransformSystem final : System<TransformSystem>
{
// implements the logic for creating & updating the transform matrices of a bunch of entities
void Update(double dt) override;

void Register(MessageManager& messages) override;
void Receive(const BaseMessage& message) override;
}

And for each new feature, you just create two of the above, adding the system to a SystemManager (which is once again merely a more intelligent vector). Messages are passed via those systems, too, like in my original post. Each system has all the tools/classes available for dealing with the correct messages and manipulate its components based on it.

 

So there you go. Instead of a god-game-class that has all kind of methods (update-transform, update-physics, render), and a game-object god class with an equivalent to those, you now have single systems that work on their data and their data only. Do you see how this can break up the god-object now?

0

Share this post


Link to post
Share on other sites

This will probably not be well recieved; but I think just packing everything into a God Object which is globally accessible is just much simpler then all this hassle.

 

From anywhere in the code just pass down the God Object or have it be a Global variable with all its members/managers public, then from anywhere add/remove/update players/state.

0

Share this post


Link to post
Share on other sites

This will probably not be well recieved; but I think just packing everything into a God Object which is globally accessible is just much simpler then all this hassle.

 

From anywhere in the code just pass down the God Object or have it be a Global variable with all its members/managers public, then from anywhere add/remove/update players/state.

 

The problem with such an approach is that you'll very easily end up adding/removing/updating players/state from anywhere which makes your code difficult to maintain(and it tends to get worse as your codebase grows), difficult to test(very little will be testable in isolation) and a pain in the arse to version control (the more spread out your changes are the more likely you are to run into conflicts that need to be resolved manually when merging commits).

1

Share this post


Link to post
Share on other sites

This will probably not be well recieved; but I think just packing everything into a God Object which is globally accessible is just much simpler then all this hassle.

 

From anywhere in the code just pass down the God Object or have it be a Global variable with all its members/managers public, then from anywhere add/remove/update players/state.

 

Stop kidding yourself you are using OO and just use globals then. Even easier. You are suffering from *almost* all of the same problems with this approach so saves you typing out parameter lists.

0

Share this post


Link to post
Share on other sites


This will probably not be well recieved; but I think just packing everything into a God Object which is globally accessible is just much simpler then all this hassle.

 

Well there you have your answer why all your code ends of in a single god object. If you go by what looks simplest at the very moment and avoid everything that looks like a little bit of more work, why else would you end up with this. In the long run, a god object gets much more complicated than anything an ECS can put you up to, since ECS scales lineary, while a god object let along increases the time required for lookup of code, let alone the coupling & low cohesion that comes with this.

 

Out of personal interest, what do you even consider a "hassle" with such an entity/component code I posted? Declaring a component & system in lone isolation vs adding a member variable & method to some already huge god class?

0

Share this post


Link to post
Share on other sites

Ohh but I like when a semi-god class appears (you don't have to allow it to become a god class).

 

That's the moment when I sit to really think what is happening, how the interactions work, what is done, what it needs to be done.

 

"Does this class really needs to track this thing? Can't I make a Tracker object that does it?"

"Does this class really needs to do this? Can't that responsibility fall into a new class?

"Does all these classes really need these distinct methods? Can't that behavior be dealt with a single generic class for all of them?

"Does this method really needs to have this object as parameter if its only using a little portion of it?"

 

When refactoring those kind of cases, you start to ask the right questions all over it, question everything! Give it some time to think through it. At least to me its fun to come up with solutions for those type of issues.

2

Share this post


Link to post
Share on other sites

This will probably not be well recieved; but I think just packing everything into a God Object which is globally accessible is just much simpler then all this hassle.

 

From anywhere in the code just pass down the God Object or have it be a Global variable with all its members/managers public, then from anywhere add/remove/update players/state.

 

For small projects, this is workable. The larger the project the better the program architecture needs to be. This is why programmers come up with all these patterns - not to be unnecessarily clever, but to simplify increasingly complex code projects.

0

Share this post


Link to post
Share on other sites

Well in my current simple game engine I split out the rendering from the rest of the game engine.  Its sort of like a MVC framework (Model-View-Controller)., with the Renderer being the "view" and the Engine being the "controller".  I also have a Scene object which fits into the Model.   The GameEngine class is probably the closest it comes to the God class, but at least with the rendering and scene stuff split out, this should be a fair balance between responsibilities.

0

Share this post


Link to post
Share on other sites

Its not so much about where to pack the data, you can go OOP and have a player object handle its own data. You can have a PlayerManager store a map of players. But when you want something outside, like a collision system to do operations on the players you need access to the private player map inside the PlayerManger. Now you gotta develop a whole system of messages and methods between the collision system and the PlayerManager, a total pain. And these are maybe tens of systems to interact with eachother.

0

Share this post


Link to post
Share on other sites

In C++ I just created four template classes for having the event functionality from C#. A class can have event A and then interested objects can register themselves (becoming listeners) by providing a callback. Once the event is fired all the listeners get their callback called.

 

These are the classes (without guards):

 

EventListener.h

#include <functional>

//==============================
// Event Listener With Event Args
template<typename ST, typename T>
class EventListener
{
	std::function<void(ST, T)> _callback;

public:
	void operator ()(ST sender, T e)
	{
		_callback(sender, e);
	}

	EventListener(std::function<void(ST, T)> callback)
		: _callback(callback)
	{
	}
};

//==============================
// Event Listener Without Event Args
template<typename ST>
class EventListener<ST, void>
{
	std::function<void(ST)> _callback;

public:
	void operator ()(ST sender)
	{
		_callback(sender);
	}

	EventListener(std::function<void(ST)> callback)
		: _callback(callback)
	{
	}
};

EventSource.h

#include "EventListener.h"
#include <vector>

//==============================
// Event Source With Event Args
template<typename ST, typename T>
class EventSource
{
	std::vector<EventListener<ST, T>> _listeners;

	EventSource(const EventSource& x);
	EventSource& operator=(const EventSource& x);
public:
	inline bool isEmpty() { return (_listeners.size() == 0); }

	void operator +=(EventListener<ST, T> listener)
	{
		_listeners.push_back(listener);
	}
	void operator -=(EventListener<ST, T> listener)
	{
		for (auto it = _listeners.begin(); it != _listeners.end(); it++) {
			if ((*it) == listener) {
				_listeners.erase(it);
				return;
			}
		}
	}
	void operator ()(ST sender, T e)
	{
		for (auto it = _listeners.begin(); it != _listeners.end(); it++) {
			(*it)(sender, e);
		}
	}

	EventSource()
	{
	}
	~EventSource()
	{
	}
};

//==============================
// Event Source Without Event Args
template<typename ST>
class EventSource<ST, void>
{
	std::vector<EventListener<ST, void>> _listeners;

	EventSource(const EventSource& x);
	EventSource& operator=(const EventSource& x);
public:
	inline bool isEmpty() { return (_listeners.size() == 0); }

	void operator +=(EventListener<ST, void> listener)
	{
		_listeners.push_back(listener);
	}
	void operator +=(std::function<void(ST)> func)
	{
		this->operator+=(EventListener<ST, void>(func));
	}
	void operator -=(EventListener<ST, void> listener)
	{
		for (auto it = _listeners.begin(); it != _listeners.end(); it++) {
			if ((*it) == listener) {
				_listeners.erase(it);
				return;
			}
		}
	}
	void operator -=(std::function<void(ST)> func)
	{
		this->operator-=(EventListener<ST, void>(func));
	}
	void operator ()(ST sender)
	{
		for (auto it = _listeners.begin(); it != _listeners.end(); it++) {
			(*it)(sender);
		}
	}

	EventSource()
	{
	}
	~EventSource()
	{
	}
};

Here is a usage example:

 

Window.h

class Window
{
public:
	EventSource<Window *, void> KeyboardGotFocus;
	EventSource<Window *, void> KeyboardLostFocus;
	EventSource<Window *, void> MouseEnter;
	EventSource<Window *, void> MouseLeave;
	EventSource<Window *, EKey> KeyDown;
	EventSource<Window *, EKey> KeyUp;
	EventSource<Window *, EMouseButton> MouseDown;
	EventSource<Window *, EMouseButton> MouseUp;
        
        // bla, bla, function declarations
}

Window.cpp

void CWindow::OnKeyDown(EKey key)
{
	KeyDown(this, key);
}
void CWindow::OnKeyUp(EKey key)
{
	KeyUp(this, key);
}
void CWindow::OnKeyboardLostFocus()
{
	KeyboardLostFocus(this);
}
void CWindow::OnKeyboardGotFocus()
{
	KeyboardGotFocus(this);
}
1

Share this post


Link to post
Share on other sites

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  
Followers 0