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

Engine/Framework software design/engineering questions

8 posts in this topic

I hope this is the right place to ask but I'm having a few questions on how to properly build a structure for a framework or small engine.

 

My first question is circular dependencies. Are they bad ? People say they are a flaw in the design. If so how would you construct an engine.

Let me give you an example of how I did this in the past:

 

ApplicationClass - Has an instance of my engine class

      EngineClass - Has an instance of it's application and every other component

 

or a MaterialClass - Has an instance of the renderer component class and uses this to get .e.g the rendering device to create shader resources...

and at the same time the renderer has to work with the material class.

         

Is it wrong to think that every component has to have a two-way connection to it's owner/child ?

 

 

Second question, which is similar to the above question. What I currently do is something like this:

 

Engine creates an instance of the renderer and at the same time passes a this pointer to the constructor of the renderer class which then stores this reference as a local member to have access to the engine and its components. Now even at this point if I were to use shared_ptr I'd be having a big problem as I can't easily pass a this pointer as the engine itself would be a shared_ptr stored inside the application class.

So basically often it is the case that if a certain class needs something from the timer class, it accesses it by using its local pointer to the engine class, which then gets its timer component.

I feel like I'm in a circle of bad design here and need some help on how to properly set up this kind of a structure. Or maybe I'm just paranoid biggrin.png ?

 

Third question. In my application I create my components as pointer to an object. So during runtime these pointers are getting passed around as e.g. Core* core or Renderer* renderer as parameters of the functions. As I understand this only passes a copy of the pointer to this specific object to the other class so it is rather quick, right ? Now when returning e.g. floats would I then return the address to this float member to make it as quick as possible ? Also what is the best way to pass an std::vector?

Edited by lipsryme
0

Share this post


Link to post
Share on other sites

Engine creates an instance of the renderer and at the same time passes a this pointer to the constructor of the renderer class which then stores this reference as a local member to have access to the engine and its components.
So basically often it is the case that if a certain class needs something from the timer class, it accesses it by using its local pointer to the engine class, which then gets its timer component.

If the renderer needs a timer, then just pass it a timer! Don't pass it some other object that is able to provide a timer indirectly.
 
 

In my application I create my components as pointer to an object. So during runtime these pointers are getting passed around as e.g. Core* core or Renderer* renderer as parameters of the functions. As I understand this only passes a copy of the pointer to this specific object to the other class so it is rather quick, right ? Now when returning e.g. floats would I then return the address to this float member to make it as quick as possible ? Also what is the best way to pass an std::vector?

Big, complex objects (engine classes, std::vector, etc), which are expensive to copy (or can't be copied), IMO you should prefer passing by const-reference (but const-pointer is also valid).
For primitive types, like float/int/etc, you should always pass them by value.
The exception to these rules is when you need an "out argument" -- objects that will be modified by a function -- in which case you should pass by non-const pointer.
e.g.
float CalculateSum( const std::vector<float>& input );
void PushToVector( int input, std::vector<int>* output );
Foo* GetFooComponent();
const Foo& GetFooComponent() const;
Note that the use of pointers vs references is just a style choice!
But, using values vs pointers/references is a performance issue for large objects. Edited by Hodgman
2

Share this post


Link to post
Share on other sites

Big, complex objects (engine classes, std::vector, etc), which are expensive to copy (or can't be copied), IMO you should prefer passing by const-reference (but const-pointer is also valid).

Any unnecessary use of pointers is an opportunity for errors. With a reference, you know there's something at the other end, plus the code just looks cleaner and easier to read. Use pointers if you actually need a memory address, or if being able to pass in a nullptr instead of a valid pointer is a desirable thing in that specific piece of code.

It is no longer strictly true that you should pass and return vectors and other containers by pointers/references. Due to compiler optimizations and move semantics, passing things by value may occasionally even be more efficient. That said, passing non-basic types as references/pointers is fine, and you should always do so if you don't understand when move semantics would work.

Finally, it is frequently good style to pass smart pointers like std::unique_ptr and std::shared_ptr instead of a pointer. Passing unique_ptrs requires you to understand moving, though.
1

Share this post


Link to post
Share on other sites
Hi hodgman,
Great post, the example with the material is actually how I did it but again with each one having a connection to each other.

But let's take the other example of let's say the application class that is being created inside the entry function.
Another question on the side. I've read that it's always preferred to create objects on the stack instead of the heap / dynamically allocated, with that in mind would it make sense to create the application class or components of my engine like the timer class as an object on the stack or a pointer to this object as a dynamically allocated object?

Now getting back to the example...when I think about the engine class I think about it having every single component inside it. Now let's say this class creates an instance of another class that also at one point creates a class that needs to have a reference of some kind to use functions of e.g. the timer class. So the best way of doing that would then be passing the timer class inside the second class, just so it can provide the third class with this reference ?

And why is it that I should pass regular types like floats or ints per value and not per reference? I thought you should always try to pass things by reference as doing it per value is much slower because it creates a copy of this object. Edited by lipsryme
0

Share this post


Link to post
Share on other sites

And why is it that I should pass regular types like floats or ints per value and not per reference? I thought you should always try to pass things by reference as doing it per value is much slower because it creates a copy of this object.

Making a copy of a basic type just means copying a few bytes of raw memory; this costs almost nothing. When you pass something by pointer, the pointer itself is passed by value, which means again copying a few bytes of raw memory, which also costs almost nothing. (And references are pretty much pointers underneath.) So passing a pointer/reference to a basic type gives no benefit. Once inside the function, messing with the copied value on the stack is basically free, whereas going through the redirection of a pointer costs something, so that is actually slower. A type must be large enough in size, and/or expensive enough to copy construct, before passing by pointer/reference pays off in performance.
1

Share this post


Link to post
Share on other sites

Any unnecessary use of pointers is an opportunity for errors. With a reference, you know there's something at the other end, plus the code just looks cleaner and easier to read. Use pointers if you actually need a memory address, or if being able to pass in a nullptr instead of a valid pointer is a desirable thing in that specific piece of code.

I completely agree - I much prefer the style of using references personally.
However, note that a nerfarious programmer can still create a "null reference"...
void DoStuff( int& in_out ) { int value = in_out; ... }//might crash here..
...
int* foo = 0;
DoStuff( *foo );                                       //...instead of here

I've read that it's always preferred to create objects on the stack instead of the heap / dynamically allocated, with that in mind would it make sense to create the application class or components of my engine like the timer class as an object on the stack or a pointer to this object as a dynamically allocated object?

It doesn't matter that much for objects you're creating once and only once -- stack allocations are faster than heap allocations, but if you're doing something once per run, instead of 1000 times per frame, then it doesn't really matter if it's fast or "slow".
But yes, you can write:
int main()
{
  Timer t;
  Renderer r( t );
  DoGameLoop( t, r );//pass by reference
}

So the best way of doing that would then be passing the timer class inside the second class, just so it can provide the third class with this reference ?

Yes, that approach keeps dependencies to a minimum. Each class only "knows about" the other classes that it absolutely has to know about, which makes your code easier to maintain / more reusable.

when I think about the engine class I think about it having every single component inside it.

As in the above code block, you can also do that in your main function (create an instance of all your big engine components)... So this class is more of a helper/utility class just to save on typing when making a new application. Generally, parts of a library should not "know about" your helper/utility classes at all, but the helper/utility classes can "know about" the entire engine (a one way, top-down relationship, with the helpers existing on a layer at the top).
 
Stroppy coverd the pass-by-value thing -- when you "pass by pointer", you create a pointer variable and then pass it by value (which has the same cost as just passing an int/float/char/short/etc by value).
 
With "pass by reference", it's up to the compiler to decide what to actually do -- behind the scenes it usually just uses "pass by pointer", but it does also have the option to optimize the code to use pass-by-value if it's valid to do so... so passing an argument as a "const int&" may end up creating the same assembly code as just "int" woudl have, or it may end up creating the code that you'd use for a "const int*". IMO it's better to just make things obvious though, and default to passing primitive types by value.
1

Share this post


Link to post
Share on other sites

However, note that a nerfarious programmer can still create a "null reference"...

People have pointed this out to me before as an example of why pointers are better than references, to which I normally point out "Yes, and I can always turn up at said programmer's home in the small hours with a pointy stick to help me teach them the error of their ways" ;) biggrin.png
2

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