• 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
victorsouza

C++ Engine Layout

10 posts in this topic

Hello citizens!

I'm finally writing an simple Engine in C++ to get practice in Computer Graphics, but anyway, my concern now is the Design of the Engine itself.
At first, I thought a simple design: an Engine baseclass that the Game class would derive. The Engine class would have an bunch of pointer to other modules, such as Configs, Logs, Graphics, Files, Managers and all sort of things. Then, the Game class would derive access to it.
The problems comes when Modules have to interact with other Modules. The solution seems to give each module a reference to the Engine in the constructor, but then, I have cyclic dependency.

[source lang="cpp"]// Engine.hpp

#include "Module.hpp"
class Module;
#include "OtherModule.hpp"
class OtherModule;

class Engine
{
public:
Module *module;
OtherModule *othermodule;
...
};

// Module.hpp

#include "Engine.hpp"
class Engine;

class Module
{
private:
Engine *engine;
public:
Module(Engine *eng)
{
engine = eng;
// Now can play with engine->othermodule
}
};[/source]

To get the desired effect, I have to do forward declarations, witch don't seems good (elegant), with all the Modules.

To make it simple, I want some layout ideas... how do you design your engines?
How do you make many subsystems interact? without please making tons of Singletons.
Ideas please!
0

Share this post


Link to post
Share on other sites
[quote name='victorsouza' timestamp='1355807089' post='5011913']
The problems comes when Modules have to interact with other Modules. The solution seems to give each module a reference to the Engine in the constructor, but then, I have cyclic dependency.
[/quote]
The problem is not that modules need to interact with each other, it is how you have chosen to handle it.
Anything that should know about something else should know only about that something else. Handling off a bucket-load of miscellaneous pointers is just reshaping the same problem behind singletons and globals—you just want access to that global but you gain unwanted access to ten other systems.

Give out pointers when and where needed, and only give out the needed pointers.


L. Spiro
1

Share this post


Link to post
Share on other sites
the most important thing is you must not depend on C++ books!

theres is not priciple like Object oriented..

It's just for convience that's all..

and it's come from MSI. not extremely depend on hardware situation...

I sujest conisder more coponent type hierachy..

every object stand basic concept of data...

in my own engine .. I use mesh . mesh is just mesh he doesn't have any parents. D3D device is also just device.

It's very depend on hardware and it's concept 3D engine designer cant abstract it's nature..

so ... we must admit we treat hardware and don't make any parent or abstract heirachy.

Object orient side is just by game programmer

3D engine programmer can use Object oriented method..Cause we dealing with hardware not human mind and processing

any help plz lets me know [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]


I think good 3D engine is ... simple. easy to use. enough performance. that's all..

so you must divide concept as simple as.. make it as singlton and keep them as independent. don't save data already DirectX or OpenGL contain. minimize wrapping API(It's usless if don't you making real big comercial 3D engine)

and save your effort for making 3D engine as possible as

I hope it help you. Edited by GeniusPooh
0

Share this post


Link to post
Share on other sites
[quote name='GeniusPooh' timestamp='1355811507' post='5011926']so you must divide concept as simple as.. make it as singlton[/quote]Before anyone jumps on that to turn this into yet another singleton thread, [b][url="http://www.google.com/search?q=site%3Agamedev.net+Singleton"]read these first[/url][/b], and then start a new discussion about it [b]if[/b] there's something not already covered by the other 100 discussions on that pattern. Edited by Hodgman
1

Share this post


Link to post
Share on other sites
Assuming I'm fully understanding your issue, the "extern" keyword should help you out.

After the Engine class declaration I just do
[source lang="cpp"]extern Engine engine;[/source]
Now you can reference the engine, and all of the modules it contains from anywhere. I had pretty much the same issue you had when making my first engine in C++. extern made my life way easier.
1

Share this post


Link to post
Share on other sites
Quite interested in this aswell. For example a scenegraph that contains nodes that needs to use the engine renderer? Where/How would you pass the render instance to the nodes?
(Any change for some skeleton code example showing the relationship too?) Edited by KaiserJohan
0

Share this post


Link to post
Share on other sites
The problem with [font=courier new,courier,monospace]class Engine[/font] or [font=courier new,courier,monospace]class Module[/font] (e.g. [font=courier new,courier,monospace]Renderer[/font] or [font=courier new,courier,monospace]Physics[/font] or [font=courier new,courier,monospace]Audio[/font]) is that those classes have [url="http://en.wikipedia.org/wiki/Single_responsibility_principle"]waayyy too many responsibilities[/url], so much so that you're not really using "OO" design any more.

When you've got such big classes, it does become very tempting to just make a single global instance that every other part of the code can connect to, because you've never broken it up enough in the first place in order to create sensible, small connections.
[quote name='KaiserJohan' timestamp='1355823823' post='5011971']
For example a scenegraph that contains nodes that needs to use the engine renderer? Where/How would you pass the render instance to the nodes?
[/quote]"Scene graph" is a very vague term that refers to a lot of different designs, so I'll pretend we've got:
[code]struct Node { std::vector<Node*> children; virtual void DoRendering(); void Traverse(); };
void Node::Traverse()
{
for( int i=0, end=children.size(); i!=end; ++i )
children[i]->Traverse();
DoRendering();
}[/code]In this case, all you need to do is change [font=courier new,courier,monospace]DoRendering[/font] and [font=courier new,courier,monospace]Traverse[/font] so they take a single argument -- [font=courier new,courier,monospace]Renderer& renderer[/font].

However, as above, having a single massive class called [font=courier new,courier,monospace]Renderer[/font] might not be a good idea. I personally like to keep the "renderer" ([i]whatever that is[/i]) and the "scene" seperate, so they don't need to communicate. Instead you ask the scene to give you a list of objects that need to be draw ([i]and after that you don't need the scene at all[/i]). Then you can do whatever you need to with that list and a graphics device, in order to draw them.
[code]struct Renderable { Mesh* mesh; std::vector<RenderState*> states; DrawCall draw; }
std::vector<Renderable*> renderables;
scene.Traverse( renderables ); // find all visible objects, put them into the above vector
std::sort( renderables.begin(), renderables.end(), someOptimisationCondition );//sort by depth/material/etc if required
for( int i=0, end=renderables.size(); i!=end; ++i )
{
Renderable& r = renderables[i];
device.Submit( r.draw, r.mesh, r.states );//issue the D3D/GL commands to draw this object
}[/code]
2

Share this post


Link to post
Share on other sites
Well, interesting insights!

[quote name='Hodgman' timestamp='1355837622' post='5012034']
The problem with class Engine or class Module (e.g. Renderer or Physics or Audio) is that those classes have waayyy too many responsibilities, so much so that you're not really using "OO" design any more.
[/quote]

Hmm.. but as far as my interests go, I don't really want to make an 100% pure theoretically correct OO design so my teacher would give me a trophy. For me OO is a Tool that makes it easier to write and maintain complex systems.

Maybe it's time for some experiments, I'll try to break the dependencies to a bare minimum as suggested by L. Spiro.

But there are some "Modules" that virtually every class/module should have access, for instance, I have a class called Config that reads configurations from a LUA file. Almost all other module uses it, so now my solution is to pass it a reference in the constructor, witch seems right now. A friend suggested a Singleton but I think that this is completely misplaced. Another matter of global access is a Log file that I have. I managed it to get fully static, but I don't know if that`s a good idea either, but I definitely don't want to pass it around always...
0

Share this post


Link to post
Share on other sites
[quote name='victorsouza' timestamp='1355843573' post='5012073']
Maybe it's time for some experiments, I'll try to break the dependencies to a bare minimum as suggested by L. Spiro.
[/quote]
This is the most important part. Now that you have a few different advices from people, there is really no way for you to know which is the best advice until you try them. As you pick one and implement it, you will inevitably encounter issues and see problems with your solution. At this point you may realize how to tweak it, in some cases, or you may realize that a totally different solution was much better. More importantly, you will better understand why some things are bad and why some things are good. A few years later, that understanding may change too [img]http://public.gamedev.net//public/style_emoticons/default/rolleyes.gif[/img]

Have fun man! [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img] Edited by achild
1

Share this post


Link to post
Share on other sites
[quote name='victorsouza' timestamp='1355843573' post='5012073']OO is a Tool that makes it easier to write and maintain complex systems[/quote]That's why it and ideas like [url="http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)"]SOLID[/url] and [url="http://en.wikipedia.org/wiki/Separation_of_concerns"]separation of concerns[/url] were invented [img]http://public.gamedev.net//public/style_emoticons/default/wink.png[/img]
These guidelines weren't invented to impress teachers ([i]who, in my experience, aren't often good programmers[/i]); they were invented so that large projects could be easily managed and reasoned about.
[quote]I have a class called Config that reads configurations from a LUA file. Almost all other module uses it, so now my solution is to pass it a reference in the constructor, witch seems right now.[/quote]That's a decent solution, but if you wanted to break it up more, so that there isn't this single "Config" entity that forges links between your entire code-base, you can break it up too, e.g.
[code]/* foo.h */ class FooConfig { /* just foo stuff */ };
/* bar.h */ class BarConfig { /* just bar stuff */ };
/* config.h */ class FooConfig; class BarConfig;
class GlobalConfig { public: const FooConfig& GetFooConfig(); const BarConfig& GetBarConfig(); };
//cpp
#include all 3
int main()
{
GlobalConfig& cfg = ...;
Foo foo( cfg.GetFooConfig() );
Bar bar( cfg.GetBarConfig() );
}[/code]Now foo/bar don't have to have any connection between them at all, and instead at the next [i]layer[/i] up in the code-base, the global-config composes them together into a useful whole.
[quote]Another matter of global access is a Log file that I have.[/quote]The "log file" example is always one that sparks a lot of discussion when it comes to globals/singletons/param-passing, and how best to deal with it...
Personally, I like to treat it specifically as an exception to the rule, because I don't see it so much as part of your application, but instead it's a part of [i]the environment in which you're building your application[/i]. Things that form a part of your coding environment are ok to break the rules, because they're you're foundations and walls that need to be [i]everywhere[/i], holding up your code.
e.g. the connection to your IDE's debugger, or [font=courier new,courier,monospace]malloc[/font]/[font=courier new,courier,monospace]new[/font] or the call-stack, or the currently executing thread, are all 'environmental' features, like a debugging log. Every single bit of your code needs to run [i]inside the environment created by these features[/i]. Edited by Hodgman
2

Share this post


Link to post
Share on other sites
Logging is the classic example of the thing you make that everything needs access to, because it's the only example (unless you're re-implementing other environmental features, like the memory manager, etc.)

[quote name='Hodgman' timestamp='1355893950' post='5012334']Personally, I like to treat it specifically as an exception to the rule, because I don't see it so much as part of your application, but instead it's a part of [i]the environment in which you're building your application[/i].[/quote]

I don't know how Hodgman manages to be right every single time, but once again, Hodgman's right. Logging is the exception because it's diagnostic scaffolding which is unrelated to our code's actual functionality.

But even so, it's possible to come up with a reasonable design for it.

Our design goals might be:
- Support multiple logging channels, locally configurable to be sent to various destinations.
- No logger object needed at point of logging (i.e. no passing loggers to every single function).
- No globally accessible state.

[CODE]
class Logger
{
public: // "Object part"
void SendChannelToFile (string const & Channel, string const & FileName);
void SendChannelToDebugConsole (string const & Channel);
void SendChannelToOnScreenConsole (string const & Channel);
...

public: // "Global part"
static void Log(string const & Channel, string const & Message);
...
};
[/CODE]

The basic idea is that Logger::Log() has some sort of safe default functionality (like ignoring all log messages), but if a Logger object exists, then the function uses that object's configuration information to route any configured channels.

Note that the globally accessible Logger::Log() function neither sets nor gets state.

The appropriate part of the codebase can create a Logger object, and load a local configuration file to configure logging channels according to the local user's current preference. Setup, access, lifetime, and cleanup of the object and its configuration data are all managed as with any normal object. The Logger class can prevent more than one instance from being instantiated, to avoid mishaps.

The Logger object's internal state [i][b]does [/b][/i]remotely effect the behavior of Logger::Log(), but then Logger::Log() is generally designed to effect the environment [i][b]outside [/b][/i]of the application (like printf() and std::clog() ).

And yes, the connection between the global and object parts would be accomplished through some static pointer, and Logger::Log() may need to be thread-proofed. But again, this system is the exception, and all of the above functions can be conditionally compiled to no-ops in published versions.
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