Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » Games: Data driven, OOP, or Engine?

This thread is locked; no one can reply to it. rss feed Print
Games: Data driven, OOP, or Engine?
BitCruncher
Member #11,279
August 2009
avatar

Game development has changed over the decades as technology continues to advance. Video games which previously measured KBs in size are now on the GB scale. What was previously coded by single people now involve hundreds of developers.

Now more than ever, you don't need to know how to code in order to get a game on the market. With the use of engines and very simple scripting languages, very complex games can be made with little or no actual code to speak of.

Of course in the end, how the game is made is up to you; a game is a game.

There are different flavors of development, and I have spent lots of time thoroughly exploring each of them. Here are the types I have discovered: data driven (or procedural, linear), object-oriented design, and game engines.

Data driven design: DDD is prominently known by its heavy use of methods and subroutines, data lookup tables, linear simplicity, and simple aggregate structures (such as STRUCTS)

Object-oriented Design: OOP in game design is known by its heavy use of object structures (such as classes), encapsulation, polymorphism, and code reuse.

Game engines: Game engine development is uniquely identified by its use of engine software as a wrapper, giving the user access to more advanced functionality through abstractions and user interfaces. Usually, a scripting language is used alongside the engine to enhance functionality.

The question is this: which is you, and why do like it over the others?

EDIT: Of course, this is not an exhaustive list of game dev methods, simply the ones that I can discern.

Eric Johnson
Member #14,841
January 2013
avatar

Most of my code is procedural, but I also like to write "lightweight" classes; that is to say that I use classes, but I do not much care for inheritance or polymorphism. I find that most code does not require these things.

I mostly use classes for organizational purposes. I'd rather have all of my player-related code in a player class than stuff it into a struct or have free-floating variables all over the place. When used for one-off instances like these, the need for polymorphism is nonexistent. But that's just me.

I've also recently taken an interest in functional programming, but haven't fully explored all it has to offer yet.

Mark Oates
Member #1,146
March 2001
avatar

I think you're misusing the term "data driven", which typically refers to a strategy for making decisions. Usually when you say something is "data driven", you mean that you've done a bunch of market research and know what features/aspects of your product to focus on, and you have data to back it up.

But to answer your question, I've been taking a strong object oriented approach and use my own engine. I don't have any scripting features in place, and simply write most of those things directly in C++.

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Chris Katko
Member #1,881
January 2002
avatar

Data driven and data-oriented design (99% likely the one you meant) are different.

http://www.dataorienteddesign.com/dodmain/

I haven't made a new game yet. Although I started one last year/beginning of this one trying to "upgrade" my mental model. Straight DoD is simply not viable for someone who needs to code as fast as possible. It's way easier to treat objects as unique except for particles. However, I still try to learn from DoD where possible.

I'm also moving toward unit testing as well as a few other changes but .... it's been months since I did anything so I can't remember the rest.

I was looking into symbolic/AST parsers for forcing code correctness but D only has 1 library for AST checking and it has no documentation. Meanwhile, libclang is supposed to be amazing.

It did finally dawn on me one day that I can treat CHECKING separate from COMPILING. So while it's great to use a compiler's built-in functionality, there's no reason I couldn't simply so REGEX matching as well as an external AST pass separate from the compilation step.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Mark Oates
Member #1,146
March 2001
avatar

I'm also moving toward unit testing as well

Awesome! What library are you using for your unit tests?

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

princeofspace
Member #11,874
April 2010
avatar

I haven't made a new game yet. Although I started one last year/beginning of this one trying to "upgrade" my mental model. Straight DoD is simply not viable for someone who needs to code as fast as possible. It's way easier to treat objects as unique except for particles. However, I still try to learn from DoD where possible.

I'm a big DoD fan, and I would say the biggest problem is changing gears to learn how to write data-oriented code. Once you've got in the groove it doesn't take any longer at all, but that may be my anti-OOP bias talking.

Somebody a while back on SO (I couldn't find the reference) said that DoD code was inherently more readable than OOP, so I thought I'd right a game with zero comments in full DoD c89.

I'm glad I did. I can't go back.

Remember there's a strict, clear division between code and data, and that your data is the first class citizen, not the code.

Join us. Embrace the data, not the code.

bamccaig
Member #7,536
July 2006
avatar

Side note: first-class code is far more powerful than first-class data. But at the end of the day, the data is what matters. Code that has no data is useless. Data without code is still potentially useful. In any case, I'm not overly familiar with DoD nor processor design and "engineering" software to run on the machine best, but it does seem to somewhat align with what I said earlier about having separate state and behavior. The state simply is transformed into several sequential blocks of related data instead of an "object" or "struct" of related data. Ultimately, you still have the potential to have a stateless "service" object operate on it. This is very specific to game's programming though. In my experience it's rare for business software to do this kind of thing.. As a side note, I guess the game "object" could always contain pointers to these arrays to give you both.

Arthur Kalliokoski
Second in Command
February 2005
avatar

bamccaig said:

Side note: first-class code is far more powerful than first-class data.

Didn't we just talk about caching? It's rather like the difference between a billiards player who knows how to set up the next shot vs. a billiard player who can shoot more accurately. Setting up your structs, alignment and so on is extremely important.

They all watch too much MSNBC... they get ideas.

BitCruncher
Member #11,279
August 2009
avatar

You guys are right about what I call data-driven. What I'm actually referring to is closer to what I mean by procedural programming. It is not heavily object-oriented, uses arrays and lookup tables extensively, and mainly data is used to control the program.

Chris Katko
Member #1,881
January 2002
avatar

What library are you using for your unit tests?

D builds it straight into the language! Also, there are many 3rd party D unit testing extensions. But I'm not using them (yet?).

https://dlang.org/spec/unittest.html

[edit] Oh, on DoD one thing that finally clicked for me. How to avoid branching? It took a couple different lectures because nobody plainly just says it. But it's finally obvious. YOU SORT. Branching is slow, so you sort your data.

But wait, sorting requires BRANCHING to sort, right? (Maybe? Usually?) So isn't sorting once every frame gonna be just as slow as branching?

I think they're using some sort of heuristic "better than nothing but super fast" sorting because 99% of objects aren't going to change booleans.

There was another thing related to that but I forgot it and I need to head out for work...

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

princeofspace
Member #11,874
April 2010
avatar

Oh, on DoD one thing that finally clicked for me. How to avoid branching? It took a couple different lectures because nobody plainly just says it. But it's finally obvious. YOU SORT. Branching is slow, so you sort your data.

But wait, sorting requires BRANCHING to sort, right? (Maybe? Usually?) So isn't sorting once every frame gonna be just as slow as branching?

Usually this is done by starting with sorted data and keeping it meticulously organized. Everything gets it's own array, sorted by what is active.

struct {

    struct SPRITE entity[MAX_SPRITES]
    int count;

} sprites;

Everything below the value of "count" is active, everything about is not. This avoids branching.

for (i = sprites.count; i--; ) {
    /* do something with active sprites */
}

Chris Katko
Member #1,881
January 2002
avatar

I missed an important sentence as I was trying to type out the window.

You SORT BEFORE, and your data won't change much every frame so you only need to "resort some". I think that's what they do.

ALSO, pre-sorting. Instead of having one array with 8 different monsters. You have 8 arrays each with their own monster. (Where possible.)

And ANY BOOLEANS are actually branches. So be super careful with booleans. Instead of having a is_dead flag, you could have an array of DEAD guys and an array of LIVING guys.

Now you're not checking EVERY object if it's a dead guy.

ALSO, "optimize for the common case." MOST PEOPLE are alive (unless you keep dead people around for a long time). So you make the MOST COMMON CASE the fastest. The first if branches, etc. You can also pre-calculate data for the common case that gets THROWN AWAY in edge cases, because 99% of the time it's still going to be used.

A good, simple, scratch paper way to decide if branching is a problem? Print your boolean to stdout and a text file. Now, zip it. The zip compression ratio is your ENTROPY. You want the maximum compression (=least branches).

It's so fun thinking about stuff like that. Because a program goes from a logical thing, to a super-powered assembly machine where bottlenecks are both apparent, AND solveable.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Well, that's interesting. Didn't really think about branching much before.

Question - how would you do data driven design with a program like Spiraloid?

My Spiraloid 2 program (Major WIP, very basic, but a successor to my original Spiraloid program).

The basics of how it works is this : There is a spiral function that maps from theta = 0 to theta = theta_max (depends on spiral_width and other things). Theta max changes fairly often. There is an array of original position data that I transform on each iteration to create the new frames spiral data. This is done at the refresh rate of the monitor.

For instance, how would I optimize this? My data processing speed is the only bottleneck of the program. But I could probably blast everything out in the gpu at a single throw after calculating every thing.

Spirals.cpp

princeofspace
Member #11,874
April 2010
avatar

And ANY BOOLEANS are actually branches. So be super careful with booleans. Instead of having a is_dead flag, you could have an array of DEAD guys and an array of LIVING guys.

If you absolutely must use a boolean, use a flags variable (the way Allegro uses bitfields) to consolidate several bools into one. But yes, avoid booleans, for sure.

Go to: