OPifex Engine - Part 2

Previously we discussed an overview of the OPifex Engine.

The Core

The Core Layer is meant to provide the simplest elements of any game. This means no graphical interfaces, no gamepad input, no audio systems, just the basics.

  • System Types
  • Game Loop (Initialize, Update, Destroy)
  • Basic Math (absolute, floor, ceiling, etc)
  • Memory Allocation
  • Error Logging

System Types

The Core is the first layer of the engine, which is the foundation for every layer after it. To make it work we've implemented defines to handle all of our targeted platforms.

#define OPIFEX_WIN32
#define OPIFEX_WIN64
#define OPIFEX_LINUX32
#define OPIFEX_LINUX64
#define OPIFEX_OSX32
#define OPIFEX_OSX64

The operating system defines are created when generating the project with cmake (we'll cover this later). Using these we can generate the appropriate system types but to make things easier, we defined a few more.

#define OPIFEX_OS32
#define OPIFEX_OS64

Most of the code doesn't care whether we're on a 32 bit or 64 bit architecture. Also, very little changes from Linux to OSX since they're both based on Unix.
Using these generic defines keeps the code clean while still allowing us to fine tune each operating system as needed.

Once the operating system is chosen the base system types get type defined.

typedef char i8; // sizeof(1)
typedef short i16; // sizeof(2)
typedef int i32; // sizeof(4)
typedef long i64; // sizeof(8)
typedef unsigned short ui16; // sizeof(2)
typedef unsigned int ui32; // sizeof(4)
typedef unsigned long ui64; // sizeof(8)
typedef float f32; // sizeof(4)
typedef double d64; // sizeof(8)

These types make cross platform coding easier. For example, on Android a jchar is not the same size as a char. It's the size of an unsigned short, while a jbyte is the size of a char. In our engine we define these types like this:

typedef jchar ui16;
typedef jbyte i8;

Since our defined types are used in the engine instead of the platform specific types, we don't have to change anything in the above layers.

We've also added additional types which fluctuate based on the architecture type specifed (32 bit vs 64 bit).

// 32 bit architecture
OPint // sizeof(4)
OPuint // sizeof(4)

// 64 bit architecture
OPint // sizeof(8)
OPunit // sizeof(8)

These are very useful with pointers, which change size based on the architecture the engine is built for.

The Game Loop

The Game Loop in Core provides 3 functions that every game will need.

  • Initialize
  • Update
  • Destroy

Each function is a function pointer.

extern void (*OPinitialize) ();
extern int  (*OPupdate)     (OPtimer*);
extern void (*OPdestroy)    ();

By providing function pointers for the game loop we're inverting the process. Your application no longer worries about handling the game loop or the timer, the engine does. Your application just provides a pointer to the functions that the engine will call when appropriate.

The Game Loop then manages the timer which we call OPtimer. Every time the loop completes a cycle the global timer is updated and then the update function passed from the Application starts all over again with the now updated timer passed to it.

Math & Memory

We're only creating a basic layer for games to run on so the math and memory functions are very simple in the Core.

For memory management we only define simple wrappers around the normal c calls.

OPalloc() // malloc
OPrealloc() // realloc
OPfree() // free

Our math operations only cover the most common operations that could be needed which don't require a structure (Vectors, Matrices, etc)

OPint OPceil(OPfloat f);
OPint OPfloor(OPfloat f);
OPfloat OPabs(OPfloat f);
OPfloat OPabsf(OPfloat f);
OPint OPabsi(OPint i);
OPfloat OPsin(OPfloat f);
OPfloat OPcos(OPfloat f);
OPfloat OPtan(OPfloat f);
OPfloat OPasin(OPfloat f);
OPfloat OPacos(OPfloat f);
OPfloat OPatan(OPfloat f);
OPfloat OPpow(OPfloat b, OPfloat exp);
OPfloat OPsqrt(OPfloat f);
OPfloat OPlog10(OPfloat f);
OPfloat OPlog2(OPfloat f);
OPfloat OPln(OPfloat f);
OPfloat OPround(OPfloat f);
OPfloat OPnear(OPfloat value, OPfloat target, OPfloat threshhold);
OPfloat OPrandom();
OPfloat OPrandRange(OPfloat min, OPfloat max);

Error Logging

Nothing complicated here. We're using a very standard ASSERT method along with a variable argument logging method to output information to the console.

ASSERT( TRUE , "Much Error, Such Logging, Wow." );
OPlog( "Running %d threads.", 3 );

At its core

Its the most straight forward layer in the engine. It's small, light weight, and provides a minimal feature set. Every game that will use the OPifex Engine will include the Core Layer no matter what the game is.

This covers the direction of the Core Layer as the foundation for the engine. In the next post we'll go over our Data Layer.