Wednesday, June 18, 2014

The dawn of a new game engine

After my [de]motivating blog post earlier about making your own game engine, this will be THE post talking about how I got started!

A long, long time ago, I set out to create my own engine.  I've always been one to need to figure everything out to understand how things work, and game engines were no exception.  In my moments of weakness, I did consider pre-made things (hey, I even used RPG Maker wayyy back in the day).  If you're curious, I even went on and on about Unity and why I didn't use it here.

It's been a while since I started the adventure of this engine, so I'm looking back on the commit history of that first legendary day of when my engine was first put in to version control (protip: use version control software when making your own engine - I MEAN IT).

According to my own notes, version 0.0.0 of my engine had the following capacities:

  • Integrated libpng (for loading png files)
  • Integrated zlib (for compression/decompression of data)
  • Engine definitions (TRUE = 1, FALSE = 0, etc)
  • Device capability reading (number of cores in the CPU, display resolution...)
  • Engine init and deinit code, engine version numbers, etc
  • Fixed math placeholders (float replacements)
  • Real basic and rudimentary bitmap font loading
  • A very, very broken, misguided, and naive set of game configuration routines
  • A box structure, used to define a region for 2d interaction on the screen (think buttons)
  • An actual button structure, the intention was to be able to just put clickable buttons anywhere
  • OpenGL initialization stuff
  • Life cycle logic (useful mainly for mobile platforms)
  • Platform agnostic logging routines (to stdout, or to a file)
  • Very, very basic network socket operations
  • Very broken sound code that required extensive rework later
  • Very broken storage routines (come to find out, this is much more complicated in cross-platform)
  • Super basic time and date handling routines
  • A generic "timer" structure to keep track of elapsed time (in milliseconds)
  • Some engine utility functions (stuff like check if integer is even, get process id, check for file exist)
  • Basic 2d vector structure
In theory, I could drop this v0.0.0 of my engine with some game logic and a real basic game could be made.  How wrong I was.

According to my version control, the notes I left for myself were mostly unhelpful at first, but it is quite apparent that I grew frustrated with myself with how broken a lot of stuff was; especially with my early cross-platform issues.  At first, this engine was to be used for Android development specifically to replace the very clunky Java solutions out there.  What I was finding out was that there is a crazy amount of problems to be solved here at these fundamental levels for a couple platforms, not just Android.

It looks like the first thing I did was make some test functions to specifically help with testing new engine code.  This way I could make an engine testing program that can test engine functionality in a sandbox-like environment -- definitely want a controlled system when testing engine logic; many times I've driven myself to the brink of insanity debugging engine code when I came to find out that it was a bug in a game's logic that actually was bugged, not the engine!  Then again, some of these sanity checks lead to more robust and fault tolerant code in the engine, so it wasn't ALL for naught.

I just realized I should back up a bit; hey there's a bit of history here and I'm getting old!

Let's go back to the design goals of the engine.
  • It should have fantastic performance
  • It must be cross-platform to the major platforms (windows, android, iOS, OSX, linux)
  • It must cater to doing as little work across platforms as possible for games using it
  • It should help me later when making games by doing a lot of the less... interesting things
  • It should help me later when making games by doing a lot of the tedious things
To cater to the first two points, I settled on making the engine in C.  Yes, straight C programming.  Not even C++ (though one of my early iterations of an engine I did utilized C++, and I certainly miss some of those features).  C's performance has the potential to destroy the competition in the compiled languages sphere (well, short of FORTRAN, I suppose, though the race is close!).

Performance issues aside, C is also one of the few (maybe only?) languages that is supported by all the mentioned platforms.  The goal here is such that I need to be able to do as little rework as possible across all platforms.

I couldn't do Java, because iOS doesn't support it (a decision I agree with!).  Getting it to work on OSX can also be a bit finicky.  I certainly have a fair share of less-than pleasurable experiences with JVMs on Windows as well... and dalvik with Android... with its extensive device fragmentation issues is a whole other terribad experience I don't recommend to anyone.  Those things ruled Java out pretty quickly -- not to mention early versions of a Java engine I attempted were met with very bad performance metrics (especially on Android).  As a game engine, the fastest performance possible should be a high priority (I'll make an article about this later in much more elaborate detail).  Java was out.

C# was a candidate.  Unity uses it, it's not completely terrible.  The Mono framework supports it on non-windows solutions.  I dabbled with it briefly, but I found myself throwing features out that were suppose to be "pros" of C#.  Stuff like garbage collection, exceptions, reference counting, running unsafe code, etc... all these things were starting to add up against C#.  Though, unlike Java, C# at least seems to be deployable to the major platforms in some way... yet I found myself too often "going native" and making the hardcore components with C/C++ anyways.

So, to cater to this newfound information, I pretty much settled that the engine must be C/C++ flat out.  It's not all bad, I mean, every major Operating System in the world uses it.  Games on consoles use it.  How bad can it be?  Thankfully, these are my primary languages as well, so there's a comfort factor involved too.  Well, time will be a casualty with how complicated "going native" is for each supported platform... the tradeoff, though, is fantastic performance, and surprisingly little administrative overhead for "write code once, run anywhere"... which is a common advantage for Java/C# -- and here we are emulating that slogan with C/C++!  While this sounds good, there are platform intrinsics that we have to manually cater to, which is far from fun and anything but straight forward.

The other bullets mentioned I will be getting to as we go forward with these articles.

This was a lot of stuff.  I settled on straight C for maximum control, portability, and performance.  I dropped a couple of months on trivial things, setting things up for the engine (the first bullet list in this article reflects that effort).  There was already a bit of cross-platform support right from the get go as reading back certain information, like getting the current process id, were platform dependent.

Remember, each platform is fueled by different system capacities and standard libraries for their respecitve operating systems.  I will be taking full advantage of POSIX functions and things in the standard C libraries when possible, and I recommend doing the same for your own game engine for maximum performance, predictability, accuracy, and compliance with those standard behaviors.

Not sure what to cover next, I'd like to talk about big picture things and super technical things... so if anyone has a request for something, by all means leave a comment and I'll focus on that specifically.


No comments:

Post a Comment