Auferstehung 2 (Doom WAD) Released!

It’s been 12 years since Helldorado Team (formerly Brain Dead Studios, formerly Elioncho’s Engine team, formerly the Condor Team) announced the development of a Doom WAD – the never released Confessions from the Flesh. But today, after more than 10 years of projects sinking down the drain, prototypes never seeing the light of day, and other tech demos lost to numerous formatted hard drives, we can finally announce the release of our first 11-level Doom WAD (wow, it only took a decade): Auferstehung 2.

Auferstehung 2 is the unofficial sequel and homage to Nazi Auferstehung, named the Worst WAD of 2006 by Doomworld.

And now, from the depths of the Doom community a similar turd emerges, albeit not as worse, but still pretty bad. Following the footsteps of the predecessor’s creator (who used Slige), Helldorado Team leveraged Oblige to produce the 11 procedurally generated levels of Auferstehung 2.

Download Auferstehung 2!

Levels:

MAP01: Enter the Dominion
MAP02: The Prison of Despair
MAP03: Horrendous Temple
MAP04: Blood Shrine of the Beast
MAP05: The Nukage Shaft
MAP06: Thermal Terminal
MAP07: Pain from the Base
MAP08: Lockdown
MAP09: The Crossroads of Menace
MAP10: Arson Anthem
MAP11: Palace of Danger

Special thanks to:

  • Andrew Apted for creating Oblige and making this project possible
  • Dark Exodus for Nazi Auferstehung
  • id Software for Doom 2
  • Elioncho for emotional support

Tools used:

How to play:

  1. Drag and drop auferstehung2.wad into the .exe of your favorite Doom Source Port (for example GZDoom, Zandronum, etc). Boom-compatible port recommended.
  2. Make sure Doom2.wad is in the same folder as the Source Port’s .exe file

ARThings v1.2 Released

We made some tweaks over the past couple of weeks and finally ARThings v1.2 is out. Click on the cool Doomguy below to check it out:

Here’s a laundry list of the updates we made:

  1. Updated HUD graphics – Updated fire button graphics and added a new “switch weapon” button
  2. Removed the ability to spawn monsters by tapping on planes. This was an experimental feature pre-v1.2 that has now been removed
  3. Removed the tracking of vertical planes such as walls. This was not needed as monsters spawn on top of upward facing horizontal planes (such as floors).
  4. You can now pick up and use the Chaingun
  5. Updated spawner to spawn Shotgun and Chaingun ammo when you’re low on ammo
  6. Updated spawner to spawn 3 different types of monsters: 1) Zombiemen, 2) Chaingunners and 3) Demons
  7. Dead Zombiemen drop clips when dead
  8. Dead Chaingunners drop Chainguns when dead
  9. Added other effects such as blood and teleport fog when spawning monsters
  10. Other bug fixes

Also, here’s a video of some “in-game” action from v1.2:

Doom AI Game Logic Explained, kinda

A couple of weeks ago we released ARThings, please check it out when you get a chance. Like we mentioned previously this project began as an academic exercise to build something more interactive with ARCore, but to bring this Doom experience to ARCore we had to invest time to learn the inner workings of Doom’s AI/Game Logic system. Below is some light commentary on this experience, including some insights and lessons learned.

Picking a Doom Source Port

To port the Doom AI game logic we had to pick a Doom source port. We narrowed down the list to 2 contenders:

There’s pros and cons with each of these but ultimately we settled with Linux Doom. By doing so we could deal with less complexity. The ZDoom codebase is much more modernized but quite extensive – it not only runs Doom but also Heretic, Hexen, Strife and other “Doom clones”. Learning ZDoom meant going through a much more extensive and technically sophisticated codebase. ZDoom’s AI and gameplay code architecture was revamped to include ACS, a flexible scripting system that developers could use to extend gameplay and AI behavior without having to write additional code to be compiled with the master codebase.

Having settled with Linux Doom, we unzipped the codebase and immediately faced with an even bigger challenge – working with legacy code. Doom was written in the early 90s, and as such the code was meant to push the limits of the hardware of that era (i386, i486, etc). If you get some time, I recommend reading Fabien Sanglard’s Game Engine Black Book, he does an amazing job describing the technical limitations id Software’s faced when developing Wolfenstein 3D. These limitations drove the technical direction of both the Wolfenstein 3D and Doom codebase.

So we went deep into the Doom source code. After studying the source code for a couple of days we encountered these technical pain points:

  1. Reading C – Doom’s code is not Object Oriented. Functions are grouped into source files based on functionality. The code can be hard to come to terms with if you’ve been writing OO code for a while.
  2. Absence of Floating Point arithmetic – To cope with CPU’s lack of floating point arithmetic during the mid-90s Doom uses Fixed point arithmetic. The gist of this is that floating points are represented using 32-bit integers. 16 bits for the integer portion and the remaining 16 for the fractional part.
  3. Angles are represented using Binary Angle Measurement (BAM) – This is new territory if you’re used to storing angles as doubles (or floats) when working with computer graphics or trigonometric calculations. in BAM, angles are represented using the full range of values of a 32-bit unsigned int. The challenge with this is that some languages do not support unsigned int as a primitive data type (Java for instance). You have to provide your own wrapper functions when performing computations that result on unsigned integers. Another challenge with BAM is that to perform trigonometric calculations you have to provide your own special purpose sin/cos/tan lookup tables.
  4. Usage of C function pointers – These are used heavily in the code, sometimes to mimic Polymorphism. C function pointer code can be hard to port with the right architectural approach, especially if the language you are porting to implements similar functionality using a different programming paradigm. Again, using the Java example you can accomplish this using Lambda expressions and Method References.

The Basics – Understanding Mobjs and States

Let’s now get to the basics. To understand how the Doom AI works we need to understand how objects, or Things (mobjs), are represented in the codebase. Things are monsters, items, pickups, fireballs, projectiles, or anything that is represented by a sprite in the game. For example, when you shoot a monster and you see a blood effect rendered as a sprite, the blood effect is a mobj.

Things are represented by the mobj_t struct defined in p_mobj.h. mobjs and have attributes such as (x,y,z) position, (x,y,z) momentum (used to simulate physics) and many other object properties.

Side note: The Doom coordinate system treats Z as the vertical “up” axis, as opposed to the Y axis. If you are coming from the OpenGL world this might be hard to stomach.

For the purposes of AI, a mobj (I’ll be using mobj and Thing interchangeably) functions as a finite state machine. Through its lifespan a mobj will cycle through various states as determined by the core engine AI logic. A mobj’s current state is kept as a reference to a State object:

state_t* state;

The declaration of state_t is defined in info.h and all state definitions in the game are stored as a global array:

typedef struct
{
  spritenum_t	sprite;
  long		frame;
  long		tics;
  // void	(*action) ();
  actionf_t	action;
  statenum_t	nextstate;
  long	        misc1, misc2;
} state_t;

extern state_t	states[NUMSTATES];

The state object has the following attributes and characteristics:

  • sprite – Sprite to use to render the mobj for this state
  • frame – Animation frame to use for this state
  • tics – How many tics to run this state for (We will explain the concept of tics in the next section)
  • action – Pointer to the function to run when transitioning to this state
  • nextstate – State to transition to when this state is over
  • misc1 – Miscellaneous attribute
  • misc2 – Miscellaneous attribute

The list of all state numbers for all the different types of mobjs in the game are defined in the statenum_t enum in info.h.

To decide what states correspond to each mobj type there’s a structure called mobjinfo_t in info.h that contains this information. The engine stores this as a global array as well:

typedef struct
{
    int	doomednum;
    int	spawnstate;
    int	spawnhealth;
    int	seestate;
    int	seesound;
    int	reactiontime;
    int	attacksound;
    int	painstate;
    int	painchance;
    int	painsound;
    int	meleestate;
    int	missilestate;
    int	deathstate;
    int	xdeathstate;
    int	deathsound;
    int	speed;
    int	radius;
    int	height;
    int	mass;
    int	damage;
    int	activesound;
    int	flags;
    int	raisestate;
} mobjinfo_t;

extern mobjinfo_t mobjinfo[NUMMOBJTYPES];

Let’s provide an example with one of the Doom monsters, the Zombieman.

Here’s how the Zombieman’s mobjinfo is defined in the info.c file. Note that the Zombieman’s mobj type is MT_POSSESSED:

mobjinfo_t mobjinfo[NUMMOBJTYPES] = {
   .
   .
   .
   {
	// MT_POSSESSED
	3004,		// doomednum
	S_POSS_STND,	// spawnstate
	20,		// spawnhealth
	S_POSS_RUN1,	// seestate
	sfx_posit1,	// seesound
	8,		// reactiontime
	sfx_pistol,	// attacksound
	S_POSS_PAIN,	// painstate
	200,		// painchance
	sfx_popain,	// painsound
	0,		// meleestate
	S_POSS_ATK1,	// missilestate
	S_POSS_DIE1,	// deathstate
	S_POSS_XDIE1,	// xdeathstate
	sfx_podth1,	// deathsound
	8,		// speed
	20*FRACUNIT,	// radius
	56*FRACUNIT,	// height
	100,		// mass
	0,		// damage
	sfx_posact,	// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags
	S_POSS_RAISE1	// raisestate
   },
   .
   .
   .
};

The attributes in the mobjinfo object define the states to transition to and the sound effects to play for a particular mobj type when entering the following core mobj states handled by the engine:

  • Spawn State – state to transition to when the mobj is spawned in the game. This is the “idle” state where the monster just stands and does nothing, checking its field of view until it spots a player to chase and attack.
  • See State – state to transition to when the mobj spots an enemy (in this case the player) and should chase it. Inside the code this is sometimes referred to as the “Chase” state.
  • Pain State – state to transition to when the mobj takes damage
  • Melee State – state to transition to when the mobj is ready to perform a close range melee attack. Only applies to mobjs that perform melee attacks. For the Zombieman, notice that the meleestate attribute is “0”, which corresponds to the NULL state, since the Zombieman doesn’t have a melee attack.
  • Missile State – state to transition to when the mobj is ready to perform a ranged attack. Only applies to mobjs that perform ranged attacks such as a Zombieman firing his rifle or a Cacodemon belching a fireball.
  • Death State – state to transition to when the mobj is killed
  • XDeath State – state to transition to when the mobj is killed via explosion (gibbed)

Now let’s look at the complete list of Zombieman state definitions as defined in info.c:

state_t	states[NUMSTATES] = {   
    .
    .
    . 
    {SPR_POSS,0,10,{A_Look},S_POSS_STND2,0,0},	// S_POSS_STND
    {SPR_POSS,1,10,{A_Look},S_POSS_STND,0,0},	// S_POSS_STND2
    {SPR_POSS,0,4,{A_Chase},S_POSS_RUN2,0,0},	// S_POSS_RUN1
    {SPR_POSS,0,4,{A_Chase},S_POSS_RUN3,0,0},	// S_POSS_RUN2
    {SPR_POSS,1,4,{A_Chase},S_POSS_RUN4,0,0},	// S_POSS_RUN3
    {SPR_POSS,1,4,{A_Chase},S_POSS_RUN5,0,0},	// S_POSS_RUN4
    {SPR_POSS,2,4,{A_Chase},S_POSS_RUN6,0,0},	// S_POSS_RUN5
    {SPR_POSS,2,4,{A_Chase},S_POSS_RUN7,0,0},	// S_POSS_RUN6
    {SPR_POSS,3,4,{A_Chase},S_POSS_RUN8,0,0},	// S_POSS_RUN7
    {SPR_POSS,3,4,{A_Chase},S_POSS_RUN1,0,0},	// S_POSS_RUN8
    {SPR_POSS,4,10,{A_FaceTarget},S_POSS_ATK2,0,0},	// S_POSS_ATK1
    {SPR_POSS,5,8,{A_PosAttack},S_POSS_ATK3,0,0},	// S_POSS_ATK2
    {SPR_POSS,4,8,{NULL},S_POSS_RUN1,0,0},	// S_POSS_ATK3
    {SPR_POSS,6,3,{NULL},S_POSS_PAIN2,0,0},	// S_POSS_PAIN
    {SPR_POSS,6,3,{A_Pain},S_POSS_RUN1,0,0},	// S_POSS_PAIN2
    {SPR_POSS,7,5,{NULL},S_POSS_DIE2,0,0},	// S_POSS_DIE1
    {SPR_POSS,8,5,{A_Scream},S_POSS_DIE3,0,0},	// S_POSS_DIE2
    {SPR_POSS,9,5,{A_Fall},S_POSS_DIE4,0,0},	// S_POSS_DIE3
    {SPR_POSS,10,5,{NULL},S_POSS_DIE5,0,0},	// S_POSS_DIE4
    {SPR_POSS,11,-1,{NULL},S_NULL,0,0},	// S_POSS_DIE5
    {SPR_POSS,12,5,{NULL},S_POSS_XDIE2,0,0},	// S_POSS_XDIE1
    {SPR_POSS,13,5,{A_XScream},S_POSS_XDIE3,0,0},	// S_POSS_XDIE2
    {SPR_POSS,14,5,{A_Fall},S_POSS_XDIE4,0,0},	// S_POSS_XDIE3
    {SPR_POSS,15,5,{NULL},S_POSS_XDIE5,0,0},	// S_POSS_XDIE4
    {SPR_POSS,16,5,{NULL},S_POSS_XDIE6,0,0},	// S_POSS_XDIE5
    {SPR_POSS,17,5,{NULL},S_POSS_XDIE7,0,0},	// S_POSS_XDIE6
    {SPR_POSS,18,5,{NULL},S_POSS_XDIE8,0,0},	// S_POSS_XDIE7
    {SPR_POSS,19,5,{NULL},S_POSS_XDIE9,0,0},	// S_POSS_XDIE8
    {SPR_POSS,20,-1,{NULL},S_NULL,0,0},	// S_POSS_XDIE9
    {SPR_POSS,10,5,{NULL},S_POSS_RAISE2,0,0},	// S_POSS_RAISE1
    {SPR_POSS,9,5,{NULL},S_POSS_RAISE3,0,0},	// S_POSS_RAISE2
    {SPR_POSS,8,5,{NULL},S_POSS_RAISE4,0,0},	// S_POSS_RAISE3
    {SPR_POSS,7,5,{NULL},S_POSS_RUN1,0,0},	// S_POSS_RAISE4
    .
    .
    .
};

Before diving deeper into these states, let’s go back and cover the tics concept that I mentioned briefly.

Gametics and Thinkers

Doom doesn’t manage the concept of game updates in terms of milliseconds since the last game update. A gametic is the equivalent of a single game update – basically one iteration of the Doom game loop. During this iteration the engine performs one frame update and executes basic game loop activities such as capturing input, executing gameplay logic and rendering to the framebuffer.

Doom’s engine is clamped at 35 tics per second (35 iterations of the Doom game loop every second), so a single tic should take no longer than 1000/35 = 28.57 milliseconds. This is the behavior of Vanilla Doom, however some source ports have removed this technical restriction and allow uncapped framerates.

To understand the game loop, lets look at some code. The Doom main game loop makes a call to the G_Ticker() function in g_game.c. A call to P_Ticker() (defined in p_tick.c) is made as long as the game is running a level (gamestate=GS_LEVEL):

//
// G_Ticker
// Make ticcmd_ts for the players.
//
void G_Ticker (void) 
{    
    .
    .
    .
    // do main actions
    switch (gamestate) 
    { 
      case GS_LEVEL: 
	P_Ticker (); 
	ST_Ticker (); 
	AM_Ticker (); 
	HU_Ticker ();            
	break; 
	 
      case GS_INTERMISSION: 
	WI_Ticker (); 
	break; 
			 
      case GS_FINALE: 
	F_Ticker (); 
	break; 
 
      case GS_DEMOSCREEN: 
	D_PageTicker (); 
	break; 
    }
}

P_Ticker() handles game updates for the following:

  • All Players in the game (via a call to P_PlayerThink)
  • Mobjs (via call to P_RunThinkers)
  • Other things that we don’t care about right now
//
// P_Ticker
//

void P_Ticker (void)
{
  .
  .
  .
  for (i=0 ; i<MAXPLAYERS ; i++)
	  if (playeringame[i])
	      P_PlayerThink (&players[i]);
			
  P_RunThinkers ();
  P_UpdateSpecials ();
  P_RespawnSpecials ();
  .
  .
  .
}

Let’s digest all of this for a moment. You might think that to run game updates for all mobjs in the game you would just call an update() method passing the mobj as a parameter and do this for every single mobj in the game.

Doom handles game updates for mobjs a bit differently. Every mobj in the game has a Thinker object that manages the execution of game updates per frame for that specific mobj. The structure of a Thinker object looks like this (declared in d_think.h):

// Doubly linked list of actors.
typedef struct thinker_s
{
    struct thinker_s*	prev;
    struct thinker_s*	next;
    think_t		function;
    
} thinker_t;

As mobjs spawn in a level, their Thinkers are created and added to a doubly linked list (hence the prev and next pointers). This happens in the P_SpawnMobj() function (in p_mobj.c):

//
// P_SpawnMobj
//
mobj_t*
P_SpawnMobj
( fixed_t	x,
  fixed_t	y,
  fixed_t	z,
  mobjtype_t	type )
{
    .
    .
    .

    mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;	
    P_AddThinker (&mobj->thinker);

    return mobj;
}

Likewise, as mobjs are removed from a level (for example a projectile mobj hits a monster and needs to be removed after the explosion), their Thinkers are removed from the doubly linked list (P_RemoveMobj function in p_mobj.c):

//
// P_RemoveMobj
//
mapthing_t	itemrespawnque[ITEMQUESIZE];
int		itemrespawntime[ITEMQUESIZE];
int		iquehead;
int		iquetail;

void P_RemoveMobj (mobj_t* mobj)
{
    .
    .
    .
    
    // free block
    P_RemoveThinker ((thinker_t*)mobj);
}

Every Thinker object has a reference to a function (attribute think_t function) that executes an mobj’s game updates. This function is P_MobjThinker() in p_mobj.c:

//
// P_MobjThinker
//
void P_MobjThinker (mobj_t* mobj)
{
   // We'll go through this code later
}

P_RunThinkers() simply iterates through each mobj’s Thinker and executes its Thinker function – P_MobjThinker():

//
// P_RunThinkers
//
void P_RunThinkers (void)
{
    thinker_t*	currentthinker;

    currentthinker = thinkercap.next;
    while (currentthinker != &thinkercap)
    {
	if ( currentthinker->function.acv == (actionf_v)(-1) )
	{
	    // time to remove it
	    currentthinker->next->prev = currentthinker->prev;
	    currentthinker->prev->next = currentthinker->next;
	    Z_Free (currentthinker);
	}
	else
	{
	    if (currentthinker->function.acp1)
		currentthinker->function.acp1 (currentthinker);
	}
	currentthinker = currentthinker->next;
    }
}

We can think of P_MobjThinker() as the function called for each game state update for each mobj. This function will take care of movement, physics simulation and cycle between AI states.

We can summarize the above behavior with the following pseudocode:

G_Ticker() // g_game.c
{
	switch(gamestate)
	{
		case GS_LEVEL:
			P_Ticker() // p_tick.c
			{
				P_RunThinkers() // p_tick.c
				{
					// Iterate through all thinkers in level (linked list)
					foreach(thinker in thinker linked list)
					{
						function = thinker.function;
						// Execute thinker function
						// Runs P_MobjThinker() function in p_mobj.c
						function.run();
					}
				}
			}
	}
}

In the next article we’ll continue our deep dive into Doom AI game logic and the technical details of P_MobjThinker().

While you wait for the next article we recommend reading:

ARThings Released – Augmented Reality Doom

We recently released a Doom experience for Android’s ARCore titled ARThings. It was originally titled ARDoom but after receiving hate mail from the Google Play admins informing me that I couldn’t use the brand name “Doom” as part of the app title, I changed the name.

Click below to check it out!

We’re currently on v1.1 which has the following features:

  • Click on a horizontal tracked plane to spawn either 4 shotgun shells, a Zombieman or a Heavy weapon dude
  • Shotgun shells, Zombiemen or Heavy weapon dudes spawn randomly on detected horizontal planes
  • Blast away Zombiemen and Heavy weapon dudes with the proverbial Doom shotgun

Some technical facts:

Special thanks to:

  • John Carmack for answering some questions via email related to framerate clamping in the Doom Linux codebase
  • Fabien Sanglard for letting me proofread his upcoming Doom Game Engine Black Book. This was instrumental in helping me understand Doom monster AI

Game Engines – To build, or not to build: The Eternal Conundrum (Part 4)

Go to Part 3

Design and Development of Condor Wars

It was June 2012. It had been almost 1 year since I traveled to Bogota to work on The Descent for 3 long weeks as I mentioned already in Part 3. During that time we completely forgot about game development for a while and parted ways momentarily. Elias continued work on his baby YouOrderIt, which he was developing exclusively for his restaurant and I continued my graduate studies. In Summer 2012 I started a 12-week internship close to Detroit, Michigan and found myself with a lot of free time on my hands. I was also in a contemplative mood and somewhat depressed during the first couple of weeks of the internship. This was prompted by many factors – the work was not hands-on and I feared losing my technical coding skills. I had this paranoia that if I stopped coding in cold turkey fashion (my new career path demanded a different set of skills – mostly technical but strategic) I would be bound to forget coding and the dread of this idea made me feel worthless. I had been coding for almost 11 years so programming was an essential part of who I was. For this very reason I always kept a side project under my belt at all times in addition to my full-time professional responsibilities.

The situation in Summer 2012 accelerated my desire to get back into coding mainly to feel better about myself:

  • I was afraid that after taking a 1 year break my coding skills had decreased (a fallacy since Bill Gates in 1995 claimed he could write “slicker” code than John Carmack during a Windows 95 event even though he hadn’t obviously coded for many years)
  • I was living in the middle of nowhere and the setting was depressive, further exacerbating my depressive mood
  • I was running out of Breaking Bad episodes to watch, needed something to do ASAP

The situation above made me open Android Studio immediately. I had never played around with Android and OpenGL so I decided to develop a small proof of concept to test ideas and scrape off some of the coding rust I had hypothetically developed over the past year. Also, I didn’t want to deal with the Android Canvas library ever again so OpenGL seemed like the only alternative available. Ten years before during undergrad I had dirtied my hands with OpenGL for 3D programming so I didn’t expect a steep learning curve.

I toyed around with the concept of a spaceship flying through the vacuum of space, moving with the use of thrusters and attempting to avoid planets and asteroids. After a couple of days I had the proof of concept in pretty good shape and had tested out OpenGL graphics rendering, physics, game controls and some game mechanics ideas. It felt like I had gotten back in coding form so I archived the proof of concept and began brainstorming other ideas that had the potential of maturing into an actual game.

After a call with Elias we decided we wanted to release something very small – even smaller than The Descent. The Descent’s development was problematic due to the limitations we faced with the Android Canvas library and because of our lack of focus and direction. We also faced some challenges in our art department which added more obstacles into the mix. After learning from previous mistakes we didn’t want to play around with new ideas but rather with something simple that felt familiar. We also wanted to avoid having to depend on an artist or graphic designer to deliver game assets. We only had a 10-week timeframe and we wanted to control all aspects of development – adding a 3rd party artist/designer in a constrained timeline felt like adding a potential dependency for release. My internship ended by the end of August 2012 and we were targeting to release before I went back to graduate school.

We decided to resurrect a game that Elias had developed in assembly language many years previously called Condor Wars. We already had the game assets – there were only 2 types of sprites: the player’s spaceship and the enemy alien. The latter came in 2 flavors: green and red. The game is reminiscent of Galaga and Galaxian and the mechanics simple – you control a spaceship at the bottom of the screen that can only move left and right. Enemy aliens spawn from the top of the screen and zigzag downwards towards the player. They spawn randomly at certain intervals and the game objective is to dispose of them with the laser. The player can shoot the laser by tapping the screen. The simplicity of this gameplay made the technical requirements trivial:

  • Draw a player’s spaceship sprite and the spaceship laser
  • Draw a black background with randomly generated stars that move downwards to give the illusion the player is traveling through space
  • Draw enemy alien sprites
  • Manage an array of enemy objects that spawn randomly from the top of the screen (NPC object management).
  • Draw enemy alien explosion animation
  • Draw the player’s spaceship explosion animation
  • Draw raster fonts that display the player’s score
  • Handle collision detection between the player’s laser and enemy bounding boxes and also between the player’s bounding box and enemy bounding boxes (simple rectangle to rectangle collision detection).
  • Play sound effects (no music)

The limited technical requirements above didn’t warrant the use of an SDK such as Corona or Cocos2D so using OpenGL seemed like a decent option. There were only 5 types of objects displayed on the screen at any point in time: 1) the player’s spaceship, 2) the player’s laser, 3) the enemy aliens, 4) raster fonts and 5) the black background with stars. Managing the rendering order for these 5 objects was already predefined so a complex Scene Graph implementation was not required. For example we knew the rendering order would be the following:

  1. Background and fake star field
  2. Player’s spaceship and player’s spaceship explosion effects
  3. Enemy aliens and enemy alien explosion effects
  4. Player’s spaceship laser
  5. Raster fonts

In the draw() method we just made sure to render the game scene in the above order. Some complexities were involved with the loading, managing and rendering of OpenGL Textures for all of these 5 objects. However we developed boilerplate OpenGL texture loading and management code and carried on without deliberating if using an 2D Graphics SDK made better sense.

So after 10 weeks of development we were close to release. My internship ended in late August 2012 and I went back to school. Two weeks later we finished development and released Condor Wars to Google Play. After almost 8 years Condor Wars became Helldorado Team’s first game officially released to an app store. Everything we had done before amounted to technical demos, prototypes and proof of concepts. We downloaded v1.0 of the game from Google Play and the game crashed horribly due to a last minute production release bug. A fix was made and we released v1.1. So we downloaded the patched version and played the game again. But after playing the first couple of levels we realized something: The game was terrible. After 2 or 3 levels we found ourselves bored and you were lucky if you didn’t find yourself yawning beyond level 4. If you had to find 1 word to describe the game it would be sleep-inducing. A friend of ours claimed to have reached level 255 (the last level of the game and a nod to the 8-bit era) but I’m prepared not to believe such tales.

We realized we had found ourselves releasing yet another prototype masquerading as a game – but that had been our intention all along. We had committed to develop and release a technical demo to Google Play in order to experience the Android app release cycle, and we learned valuable lessons from that process. Before development both our mission and goal had been clearly defined and we followed through till the end. In some ways Condor Wars was a pilot test, and the foundational code that was developed for it served as springboard for the development of Condor Wars Redux, where we made some of our biggest mistakes in our game development experience. For Condor Wars we picked the right library (OpenGL) for the technical requirements which had been correctly identified before development, so the mistakes we made with The Descent were not repeated.

When tempted to release something (either a prototype, a demo or some semblance of a game) early due to external or egotistical pressures some conflicted developers advice to consider the words an ancient and wise monarch (who was also an accomplished coder) once said:

“Cherish an unpolished gem in your local machine. Abhor a turd in the App Store.”

– Alexander the Great of Macedon

With Condor Wars we learned that as a developer you are constantly faced with the following predicament: release your technical demo too early thinking that it can pass off as a game, or hold off the release of your pet project until it achieves a certain level of “perfection” or it fulfills your unrealistic ambitions. Most of us are tempted to lean towards the latter, and this is exactly what happened with The Descent (amongst other issues). Despite what Alexander the Great once said, it is better to release a piece of excrement to customers and gain valuable feedback than to let your closely guarded, ambitious project that hasn’t seen the light of day fall into oblivion due to your own high standards.

The disappointing story of Condor Wars Redux, as it was codenamed, will be the subject of our next article.

Game Engines – To build, or not to build: The Eternal Conundrum (Part 2)

Go to Part 1

As game developers, our main goal is to make games, and fun games. And by fun we mean games we want to play and games that will be dowloaded and played by others consistently. For the purposes of this article we are not going to dwell on all aspects of game development – from coding to play-testing all the way to marketing. We will only focus on development aspects because these are the areas we as game developers can control and cannot be affected my external factors or luck. After all whether your game gets 1000+ downloads on its release date is due to many factors. These factors will generate their own set of headaches so it would be best not to focus on them at this time.

However, you can control gameplay mechanics – if you want the game character to move left or right depending on the intensity of the swipe of the screen (for a mobile game) you just open Xcode (or Android Studio or any other IDE) and code the damn thing. Whether you succeed in this attempt might also depend on the number of brain cells you have but we assume your brain is perfectly equipped with these otherwise you wouldn’t be reading this article.

The game development landscape has changed quite a bit. We now have countless of avenues that can be used to release our games for instance Steam, Google Play, and App Store to name a few. Literally thousands of games are released every day and there is no such exclusivity and scarcity of games like there was in the past. In the early-to-mid 90s if you wanted a free PC game you had to scavenge your way through shareware sites and you were lucky if any of the titles available tickled your fancy. We now have an irresponsible overload of games and the result of this folly is that Google Play and App Store have become graveyards for the majority of games that are released every day. There are just too many games out there. Sometimes I ponder why we haven’t faced a mobile game industry crash like we did with video games back in 1983 (thanks Atari!).

Even if you (as an independent developer) think your game is solid and “fun”, there is no guarantee that your precious little addictive game will not be at the bottom of the App Store rankings and fall into oblivion together with thousands of other orphaned titles. The basic lesson is that game development is hard not because coding is hard but because game development is an intractable medium with success factors that cannot be easily controlled. And we have to learn to live with it. For many aspiring developers months of sweat and blood during development hells amount to nothing. Titles are released by countless of established companies or independent teams with the uncertainty of Julius Caesar crossing the Rubicon. You push the Archive button in Xcode with high hopes to release your title to the App Store and literally alea iacta est, the die is cast – leaving almost everything to chance and leaving your hundreds of hours of development at the mercy of App Store ranking algorithms.

My wish is not to alarm readers – I am just basing the above observations on previous experience. In a nutshell the main lesson is that game development as a whole is hard albeit fun and rewarding and we should be prepared to navigate the harshness of the terrain ahead throughout our game development journey. This demands a different kind of mentality. Understanding the uncertainties ahead we have to cherrypick our battles wisely and know what to focus on when we sit down and open Xcode to start developing.

As a game developer it is very easy and quite tempting to get bogged down on unnecessary details such as the technical intricacies of developing a new “font rendering library” or a “special effects/particle rendering system” for our little game even if it sounds fun to write and perhaps because I could potentially learn something useful from writing it. We sometimes face Developer’s FOMO (Fear of Missing Out). If we don’t know something inside out, or if we don’t develop it ourselves we don’t feel in control. Using someone else’s library or SDK equates to surrendering control to a 3rd party who is a subject matter expert in a specific area, and this thought hurts our ego. We are even prepared to think: “That random guy in Ukraine created this beautiful PNG compression library for hardware accelerated games which I probably need for my game. He built this library with his bare hands, so I can too! I will probably learn something cool and useful along the way!”. These internal monologues pigeonhole us into unrealistic and inefficient paths – further detracting us from our goal to focus on more value-add game development activities for example pure gameplay mechanics.

We even make ourselves believe that some 3rd party developer’s highly acclaimed font rendering library could become obsolete or deprecated in the future and create a potential external dependency. An asteroid could also fall on the 3rd party developer’s head and kill him and that would be a frustrating inconvenience – who will support that library in the future? Better to write it ourselves right? We dread having to deal with these external dependencies and factors so we have the temptation to avoid them at all costs. The worst thought of all is thinking we need to write something ourselves because we could potentially modularize it, package it and use it in the future for another game. Maybe even sell it! The possibilities are boundless! Chances are your nice little font rendering library works fine for a subset of use cases isolated to your game and cannot be used a second time because your next game might have different font rendering requirements. Chances are that perhaps you are never going to build a second game because you wasted all of your energies writing foundational engine code, recreating graphics, physics and all other core libraries yourself and no longer have the stamina to focus on gameplay code for your hypothetical second game.

Our optimism and wishful thinking clouds our judgement and we easily lose interest or get burnt out along the way. We end up with games made up of 70% foundational engine code and 30% half-baked game play mechanics because we didn’t have the discipline to focus on gameplay code – we have no gas left in the tank because we focused all our energies on library and game engine development and nothing else. We end up with half-finished prototypes or nifty technical demos that only we can admire in silence as we secretly pat ourselves in the back on a job well done. We are delusional to a certain degree. We admire the intricacies and the complex moving puzzles of our custom-made particle rendering system even though users are not playing (or reviewing) our games over and over again due our special effects library’s ability to render 50K particles in a single frame with ease. It’s how gameplay mechanics interact with other technical subsystems to provide players with an overarching experience that is fun, challenging, rewarding, immersive and stimulating. As game developers this should be our sole focus.

At Helldorado Team we’ve fallen prey to all of the above fallacies during the past several years and we would like to share our experiences answering the to build or not to build question in our past game projects whenever we’ve faced a decision to build core engine code. Stay tuned for more.

Go to Part 3

Game Engines – To build, or not to build: The Eternal Conundrum (Part 1)

A young, aspiring game developer is one of the most admirable human specimens you will ever encounter. Eager, hopeful of the future and with a insaciable hunger for knowledge, he strives to learn by experience anything he can lay his hands on. In this respect a young game developer is like a sponge and no dissimilar to a 2 year old toddler. Knowledge is a precious jewel to be acquired and guarded at all costs. During the early phases of learning a game developer has no focus since everything could be potentially useful – algorithms, design patterns, optimization, data structures, game engine fundamentals, graphics programming, rudimentary AI concepts, Newtonian Physics, etc. The list goes on an on, but during those stages everything is interesting and non-trivial – as each of these topics has the potential of building on the foundation necessary to become a well-rounded game developer.

Many of us recall those early phases – deep-diving on graphics programming concepts, contemplating on the differences between 2D and 3D graphics programming, understanding (or more accurately attempting to understand in vain) Binary Space Partition (BSP), texture mapping, software rendering, shadowing, the different kind of shading (flat vs Gouraud vs Phong vs whatever else became industry-standard these days). Those are just a subset of topics, but we all recall the countless prototypes we put our hearts and souls in – written in old Microsoft DirectDraw interfaces or OpenGL. Little algorithms that we feel so proud of, that satisfied intellectual and problem-solving longings but more than that strengthened our foundations in game development. These little programs served their purpose at the time – we needed to understand these concepts well and we felt a fascination in understanding the complex, interwoven puzzles that make up what we see on the screen – from the pixel drawn on the screen to the shadow cast by a game character in a simulated ground.

Our first prototype of a pixelated blur resembling a spaceship blasting away more pixelated bullets was one of the most gratifying moments of our professional or amateur careers. We understood why those pixels move and how the algorithms we wrote organize the bullets to be shot. The academic and intellectual goal reached, we moved on to more ambitious prototypes in the search of testing newer and more complex ideas. At the time, the more we read and the more the dug inside the game engine trenches the more intriguing the whole experience became. Today we learn how a pixel is drawn on screen, tomorrow we could learn how to perform full object culling with algorithms written with our bare hands. The possibilities seemed limitless.

The desire and the hunger for dwelling to such granular levels in our conquest of every game programming is also rooted by the stories we read as well – real or romanticized. We read how highly motivated duos, trios, quartets or even lone wolves lock themselves up in a basement or a lake house and by the sheer momentum of ambition pull off a feat unmatched by today’s standards. These are the Carmacks, Romeros, Sweeneys and the Ken Silvermans that we both admire and to some degree despise. Admire because of their monumental accomplishments that we so highly covet. But we also hate them because they accomplished so much with so little at their disposal at the time. In turn today we have so many resources at our disposal that this comparison seems both unfair and shameful. John Carmack and Tim Sweeney both developed game engines from scratch on their own. Carmack single-handedly developed software and hardware-rendered graphic engines for multiple blockbuster titles with the rapidity of an industrial assembly line. Sweeney performed a similar feat. Ken Silverman is honored now with a more memorable task given a shorter timeframe – this one man army surpassed the technical accomplishments of id Tech 1 (Doom Engine) and produced the Build Engine now used by several games that are household names in the PC gaming community. He was 17 at the time. We need not pay homage to Romero here. This self-proclaimed “Project Specialist” did everything that was necessary to ship a game out the door – game design, game programming, graphics programming, tools programming, level design and even PR to name a few.

I believe that the stories we read in books such as Masters of Doom both inspire and set the wrong expectations for aspiring game developers in 2016. Inspiration is most welcome, but truly speaking the dangers of setting wrong expectations (or standards) as game developers far outweigh the benefits of short-term inspiration. After reading such books we want to be heroes like the ancient legends of the 80s and 90s and feel the need to become jack of all trades like them. We want to be able to write gameplay code, draw digital art ourselves as well and similarly be able to re-create Carmack’s Reverse Shadow Volume Algorithm by memory. This is what developers did back then so why should we be deprived of similar honors?

Our egos are already hurt because we are now one Google keyword away from finding that solution in StackOverflow that has eluded us for days on end. However we now live in a highly-specialized environment. We are no longer in the nostalgic era of the 90s. Highly dedicated garage duos are now seldom producing and releasing blockbuster titles in Steam, Google Play or the iOS App Store. In the same vein there are no such things as prodigious hermits living in grandma’s basements developing every single granular component of a game (graphics, physics, AI, network, etc.) themselves. This myth is both dangerous and irresponsible to believe. The current environment is forcing us to become more specialized in the skill sets we can offer to the game industry.

To be a game developer 25-30 years ago meant many things. Nowadays it has a very different meaning.

In this series of articles I will be discussing one of the conundrums that has been a heavy burden for Helldorado Team (for many years actually) regarding Game Engine Development: to build or not to build your own game engine. I will discuss the wonders, perils and what to take into consideration when attempting to tackle this dilemma. Stay tuned!

Go to Part 2

John Romero’s Core Principles for Game Development

Taken from his talk in GDC Europe 2016. You can watch the video at Youtube.

No prototypes. Just make the game. Polish as you go. Don’t depend of polish happening later. Always maintain constantly shippable code.

It’s incredibly important that your game can always be run by your team. Bulletproof your engine by providing defaults upon load failure.

Keep your code absolutely simple. Keep looking at your functions and figure out how you can simplify further.

Great tools help make great games. Spend as much time on tools as possible.

We are our own best testing team and should never allow anyone else to experience bugs or see the game crash. Don’t waste others’ time. Test thoroughly before checking in your code.

As soon as you see a bug, you fix it. Do not continue on. If you don’t fix your bugs your new code will be built on a buggy codebase and ensure an unstable foundation.

Use a superior development system than your target.

Write your code for this game only – not for a future game. You’re going to be writing new code later because you’ll be smarter.

Encapsulate functionality to ensure design consistency. This minimizes mistakes and saves design time.

Try your code transparently. Tell your lead and peers exactly how you are going to solve your current task and get feedback and advice. Do not treat game programming like each coder is a black box. The project could go off the rails and cause delays.

Programming is creative art form based in logic. Every programmer is different and will code differently. It’s the output that matters.