Jump to content

  • Log In with Google      Sign In   
  • Create Account






So just what is this different way of building games?

Posted by Norman Barrows, 10 March 2014 · 631 views

So just what is this different way of building games?

in a nutshell:
non-OO c++ syntax
ADTs (abstract data types) composed of:
* PODs (plain old data)
* standalone functions
the use of data structures statically allocated in the data segment vs dynamically allocated on the heap
procedural code calling hierarchy used to define flow control

this avoids some of the headaches related to OO game coding.
and many of the advantages of OO game coding do not apply to a small team or single developer in control of their own project, which makes this a viable alternative - but only in such cases.

if you need true inheritance and polymorphism in your code, you're going to have to do the OO thing (at least a little bit).

however i've found this method perfectly satisfactory for writing games both large and small. and I have yet to require OO syntax in my code for any game i've made, am working on now, or plan to make in the future. surely those who didn't learn programming until after OO syntax was added to c++ will find this shocking. but yes, its true. games were being written for computers long before OO syntax existed. OO was invented as a means to handle the difficulties of software development in general, with no special regard to games or their special needs. so it was a dual edged blade, OO power, but with OO issues and OO overhead. Non OO syntax if done correctly (basically in an OO style) could get the job done just fine without the issues or overhead. it was only in the "OO syntax only" capabilities that it showed advantage. and those capabilities are largely related to modification of large code bases by multiple coders over long periods of time. Almost the opposite of the single developer on a single version of a single game.

so how does one do this?

well, instead of an object, you'd have a data structure (most likely a struct) for its member variables, and a number of standalone functions that accessed the data structure (IE implement the methods as stand alone functions). you take all that and put it together along with your struct definition and any related #defines, and any other variable declarations you need, and you have the non-OO equivalent of an object - an ADT (abstract data type). then if you want to, you can put it in its own source file, with it own header, thereby turning it into a code module. other code will only be able to use the exposed API in the header file. variables declared in the module will essentially be private to the module, unless exposed through the header API. so you have nice clean APIs and data hiding, with no OO syntax, no object hierarchy headaches, no memory leaks (as the variables in the module are static, allocated at load time in the data segment, not at runtime on the heap), etc.

however, you will need to write explicit init and shutdown routines to replace any custom constructors and destructors. the nice thing about explicit init and shutdown routines is you have complete control over initialization and shutdown order of all your modules in the game.

note that there's nothing wrong with using the heap when called for. if you need a big temporary buffer, the heap is obviously the way to go. but the key word there is temporary. there's no real reason to keep permanent data structures on the heap. its adds allocation and de-allocation overhead, and introduces the possibility of memory leaks. sure, one malloc or new at game start isn't a biggie, but constantly newing and disposing every little data structure in the game is just inefficient. another place where the heap is useful/required is when you simply don't know how big the data structure must be, or want it to use all available ram. but there are very few places in a game where the maximum possible number is not known or at least estimate-able, and static data structures can be declared appropriately large to handle the worst case anticipated scenario, then either degrade gracefully, or perhaps fall back to using supplemental memory from the heap to enlarge the structure, if you want to get fancy. the trade off is some unused memory at the end of the slightly oversize static data structure, vs the overhead, possible extra code work, and possibility of introducing memory leaks using dynamic allocation and reallocation.

what it boils down to is that the heap has allocation and deallocation overhead that the data segment doesn't. and its possible to make coding errors using the heap which are not possible using the data segment. so the heap can be used, but not like a madman due to overhead, and you have to make sure you dot your i's and cross your t's.

the use of procedural code implicitly defines flow control in the program, so "game states" for flow control purposes is unnecessary. game states are then only required for the more fitting purpose of making the game run in multiple modes, such as fps mode vs rts mode in a fps/rts hybrid.

further topics:
relational databases
shared resources
use of globals
level based vs non level based game code - the differences between shooter and simulator code
gameplay vs realism, the differences between shooter and simulator game design
"generic" routines




What you are describing is often called 'object oriented C'.

Sorry? What?

 

 

 

well, instead of an object, you'd have a data structure (most likely a struct) for its member variables, and a number of standalone functions that accessed the data structure (IE implement the methods as stand alone functions). you take all that and put it together along with your struct definition and any related #defines, and any other variable declarations you need, and you have the non-OO equivalent of an object - an ADT (abstract data type). then if you want to, you can put it in its own source file, with it own header, thereby turning it into a code module. other code will only be able to use the exposed API in the header file. variables declared in the module will essentially be private to the module, unless exposed through the header API. so you have nice clean APIs and data hiding, with no OO syntax, no object hierarchy headaches, no memory leaks (as the variables in the module are static, allocated at load time in the data segment, not at runtime on the heap), etc.

 

What do you think the difference is between this and using a class? What on earth are you talking about with "the variables in the module are static"?

class Y
{
public:
    virtual void f() = 0;
};

class X : public Y
{
public:
    virtual void f(){ /* ... */ }

    int y, z;
};

void f()
{
    X x;
    x.f();
}

Where do you think the heap allocations are in this code?

Sorry? What?

 

 

 

well, instead of an object, you'd have a data structure (most likely a struct) for its member variables, and a number of standalone functions that accessed the data structure (IE implement the methods as stand alone functions). you take all that and put it together along with your struct definition and any related #defines, and any other variable declarations you need, and you have the non-OO equivalent of an object - an ADT (abstract data type). then if you want to, you can put it in its own source file, with it own header, thereby turning it into a code module. other code will only be able to use the exposed API in the header file. variables declared in the module will essentially be private to the module, unless exposed through the header API. so you have nice clean APIs and data hiding, with no OO syntax, no object hierarchy headaches, no memory leaks (as the variables in the module are static, allocated at load time in the data segment, not at runtime on the heap), etc.

 

What do you think the difference is between this and using a class? What on earth are you talking about with "the variables in the module are static"?

class Y
{
public:
    virtual void f() = 0;
};

class X : public Y
{
public:
    virtual void f(){ /* ... */ }

    int y, z;
};

void f()
{
    X x;
    x.f();
}

Where do you think the heap allocations are in this code?

 

 

"X x" declares and instance of an object, which as i recall allocates memory on the heap for the member variables "y" and "z" (and the VMT? or is that only once per class definition? ) and should also run the constructor method (default or otherwise).  is this correct? its been YEARS since i evaluated exactly what c++ OO syntax does "under the hood".

What you are describing is often called 'object oriented C'.

 

i learned the design style under the more general term "abstract data type" it was a topic of major emphasis in my Motorola 68000 assembly class at OSU back in the day. they taught you assembly, how to make a macro processor, and language-agnostic abstract data type design in 90 days.  and you implemented the objects with 68000 assembly! <g>.

 

Sorry? What?

 

 

 

well, instead of an object, you'd have a data structure (most likely a struct) for its member variables, and a number of standalone functions that accessed the data structure (IE implement the methods as stand alone functions). you take all that and put it together along with your struct definition and any related #defines, and any other variable declarations you need, and you have the non-OO equivalent of an object - an ADT (abstract data type). then if you want to, you can put it in its own source file, with it own header, thereby turning it into a code module. other code will only be able to use the exposed API in the header file. variables declared in the module will essentially be private to the module, unless exposed through the header API. so you have nice clean APIs and data hiding, with no OO syntax, no object hierarchy headaches, no memory leaks (as the variables in the module are static, allocated at load time in the data segment, not at runtime on the heap), etc.

 

What do you think the difference is between this and using a class? What on earth are you talking about with "the variables in the module are static"?

class Y
{
public:
    virtual void f() = 0;
};

class X : public Y
{
public:
    virtual void f(){ /* ... */ }

    int y, z;
};

void f()
{
    X x;
    x.f();
}

Where do you think the heap allocations are in this code?

 

 

"X x" declares and instance of an object, which as i recall allocates memory on the heap for the member variables "y" and "z" (and the VMT? or is that only once per class definition? ) and should also run the constructor method (default or otherwise).  is this correct? its been YEARS since i evaluated exactly what c++ OO syntax does "under the hood".

 

X x declares an instance of an object on the STACK.  Only using x* = new X() would it be defined in the heap (and even then you'd need to delete it).

 

I'm all for avoiding OO if it's unnecessary, and your defining of an externl API set using procedural function calls are fine, but the rest of it, you're kind of talking our of your arse. 

PARTNERS