Wednesday, July 31, 2013

Object Pooling: Resource Recycling!

Alright, so we're 2-for-2 regarding posts about Spacer; I guess I've made quite a lot of progress on it lately. The concept of object pooling isn't new to programming, but it's new to me.  As an added bonus, this concept is very useful to programming things other than just video games; I could easily see it being used for particle physics or computational chemistry simulations (or, I suppose hundreds of business-type applications).  Therefore, that will be the topic of this post.  Object pooling is essentially digital recycling.

I guess Randall Munroe's stick figure comics are influencing me.  Also the fact that this comment is in the alt text.


The basic idea is to avoid constantly creating and destroying objects, which is very processor-expensive when you're dealing with potentially hundreds of complex objects. To accomplish this, we create a bunch of "empty" instances of the class we need, activate them as needed, and deactivate them for reuse when we're done. That is, instead of "new-ing up" a bullet every time an in-game gun fires, turn to your "bullet pool" and activate the next inactive bullet.

There are a couple keys to making object pooling work:

  1. All objects being pooled should have an empty constructor
  2. The pooled objects should all have some "initialize" or "activate" method
  3. Be sure to clean up the object after you are done using it... you don't want any residual properties left over from the previous usage. 
Without further adieu, here's the code sample for my generic pooling:
class ObjectPool<t> where T : IPoolableObject
{
 #region Private Fields

 private List<t> activeItems;
 private Queue<t> inactiveItems;
 private int resizeAmount;

 #endregion

 #region Public Properties

 public T this[int index] { get { return this.activeItems[index]; } }
 public int Count { get { return activeItems.Count; } }
 public List<t> InactiveObjects { get { return inactiveItems.ToList(); } }

 #endregion

 public ObjectPool(int Capacity, int ResizeAmount = 1)
 {
  if (Capacity < 1)
   throw new ArgumentOutOfRangeException("The capacity must be 1 or greater");
  if (ResizeAmount < 1)
   throw new ArgumentOutOfRangeException("The resize amount must be 1 or greater");

  activeItems = new List<t>();
  inactiveItems = new Queue<t>();
  resizeAmount = ResizeAmount;

  //Fill up the inactive items list
  System.Reflection.ConstructorInfo constructor = typeof(T).GetConstructor(new Type[0]);
  for (int i = 0; i < Capacity; i++)
  {
   inactiveItems.Enqueue((T)constructor.Invoke((object[])null));
  }
 }

 public T ActivateItem(string ObjectModelName, Vector2 StartingPosition, float StartingOrientation, Vector2? StartingVelocity = null, float StartingAngularVelocity = 0)
 {
  if (inactiveItems.Count == 0) //enlarge the queue if we've run out of objects! 
  {
   System.Reflection.ConstructorInfo constructor = typeof(T).GetConstructor(new Type[0]);
   for (int i = 0; i < resizeAmount; i++)
   {
    inactiveItems.Enqueue((T)constructor.Invoke((object[])null));
   }
  }

  T newItem = inactiveItems.Dequeue();
  activeItems.Add(newItem);
  newItem.ActivateObject(ObjectModelName, StartingPosition, StartingOrientation, StartingVelocity, StartingAngularVelocity);
  return newItem;
 }

 public void DeactivateItem(T item)
 {
item.DeactivateObject();
inactiveItems.Enqueue(item);
  activeItems.Remove(item);
 }

 // The stuff below just makes the ObjectPool fancy, allowing you to call a "foreach" loop on the pool or simply get a list of all the active objects:
 public IEnumerator<t> GetEnumerator()
 {
  return (IEnumerator<t>)this.activeItems.GetEnumerator();
 }

 public List<t> ToList()
 {
  return activeItems;
 }
}

IPoolableObject, here is a really simple interface inherited by your pooled objects. It's everything that's needed for a "new" bullet and everything it needs to clean up after itself (dirty object..).  The ObjectModelName is used as a lookup value for both a pre-loaded texture and the statistics library entry (ex: health, speed, armor, ect..., remember? See the previous post if you don't). StartingPosition, StartingOrientation, StartingVelocity, and StartingAngularVelocity are all relavent quantities for the physics engine.  As for cleanup, everything of relevance gets overwritten during the ActivateObject call, so there really isn't anything to the DeactivateObject method.  Your objects don't have to have these parameters (or lack-there-of), I'm just throwing them out there as an example.

interface IPoolableObject
{
 void ActivateObject(string ObjectModelName, Vector2 StartingPosition, float StartingOrientation, Vector2? StartingVelocity = null, float StartingAngularVelocity = 0);
void DeactivateObject();
}

Also, the bit about the "ResizeAmount" isn't really necessary. I just wanted something to fall back on if the initial capacity guess was too small and isn't the app barfing by throwing a "queue empty" exception.  You could just have the pool return null; make sure your code knows how to interpret a null object, though.

As for how it's implemented in the larger game, it's super effective!

// Create and construct the pool:
public ObjectPool<bullet> BulletPool;
BulletPool = new ObjectPool<bullet>(100);

// Activate a new bullet:
Bullet newBullet = bulletPool.ActivateItem(BulletTypeFired, StartingLocation, StartingOrientation, StartingVelocity);

// Insert a bunch of code about how your app uses the newBullet

// and finally, deactivate it when you are finished:
BulletPool.DeactivateItem(newBullet);

So there you have it.  I'd like to give some credit to a couple other sources that I kinda picked and chose my favorite parts from: Jason Mitchell, Thomas Aylesworth, and the XNA Wiki: Generic Resource Pool.

Thursday, July 18, 2013

Keeping track of object stats: A database-like layer to video games

First post, woo-hoo!
I love video games; so much so that I'm going to start things off with a video game I've been working on (and is still very much in development).  It's a top-down 2D RPG/Adventure where the player pilots a customized ship, written in C#/XNA, and this will probably not be the last time you read about "Spacer" on this blog...  The quandary conquered stems from setting up something that's considered good programming practice: layers. Maybe it's already a known solution, but I couldn't find much when I was researching it...

I don't really see much of this in scientific applications, but in a lot of business-type applications, there are often three layers: database, logic, and view.  I'm going to focus on the separation of the database and logic layers.

In my initial commit to the game, every object had it's own hard-coded statistics associated to it.  For instance, the WeaponSystem class looked something like this:

class WeaponSystem : Equipment {
bool canFire;
double rateOfFire;
float energyPerShot;
int numOfShots, numOfShotsMax;
TimeSpan timer;

public WeaponSystem() {
//arbitrary values:
rateOfFire = 0.25; //in seconds
energyPerShot = 10f;
energyReserve = 100f;
numOfShotsMax = 50;
equipmentName = "Weapons";

//calculated quantities:
energyReserveMax = numOfShotsMax * energyPerShot;
numOfShots = (int)(energyReserve / energyPerShot);
timer = TimeSpan.Zero;
}

//There is more code about how the WeaponSystem works, but not important here
}

(hey, don't judge. Guns are important to a space adventurer... Jayne agrees, see?)

This would mean creating a class for every type of weapon system: lasers, missiles, torpedoes, etc... Bleck! The logic of the game shouldn't depend on the specific values you give to your objects, and honestly, shouldn't care what the values are so long as they are valid values!

Since hard-coding values is bad, the idea was to create a sort of "Stats Library;" a single place to store all these values to look up as needed. This really just meant separating the "arbitrary values" in the above code to it's own serializable class, and dumping it's corresponding XML file(s) into the content project. For organization's sake, all these "StatsLibrary objects"  were put into a separate VS project (this project comprises the data layer, the original game project is the logic layer).  The separation turned out something like this:
public class WeaponSystemStats
{
public string Name { get; set; }
public string BulletTypeFired { get; set; }
public double CoolDownTime { get; set; }
public float EnergyPerShot { get; set; }
int MaximumNumberOfShotsStored { get; set; }
}

<?xml version="1.0" encoding="utf-8" ?>
<XnaContent>
<Asset Type="Spacer.StatsLibrary.WeaponSystemStats">
<Name>TestGun</Name>
<BulletTypeFired>TestLaser</BulletTypeFired>
<CoolDownTime>0.25</CoolDownTime>
<EnergyPerShot>10.0</EnergyPerShot>
<MaximumNumberOfShotsStored>50</MaximumNumberOfShotsStored>
</Asset>
</XnaContent>

(keep in mind, this advancement didn't happen over sequential commits, the WeaponSystem class evolved a bit.  For instance "RateOfFire" turned into "CoolDownTime", also note the "BulletTypeFired" string: this same idea was applied to the projectiles fired from the WeaponSystem)

Now, we have a nice way to store data, a "database layer," if you will. Rather than each object storing it's own statistics, all it has to know is what kind of object it is (use a unique string, enum, or whatever other identifying method you want; in this example, it happens to be the "Name" property), everything else can just be looked up from a single source! Originally, this single-source was just a Dictionary object, which works fine for getting things to work, but why stop there?

Being completely OCD, the content project was already organized into folders of XML documents for each game object, why not load everything at once? Create a generic StatisticsLibrary object:
class StatisticLibrary<t> where T : IStatsLibraryEntry //This is just an interface with a single property: Name
{
Dictionary<string, T> library;

public void LoadLibrary(ContentManager Content, string contentFolder)
{
library = new Dictionary<string, T>();

DirectoryInfo di = new DirectoryInfo(Content.RootDirectory + "\\" + contentFolder);
if (!di.Exists)
throw new DirectoryNotFoundException();

FileInfo[] files = di.GetFiles("*.*");
foreach (FileInfo file in files)
{
string extensionlessFile = Path.GetFileNameWithoutExtension(file.Name);
T value = Content.Load<t>(contentFolder + "\\" + extensionlessFile);
string key = value.Name;

library.Add(key, value);
}
}

public T GetStatistic(string Name)
{
return library[Name];
}
}

Now, we can use a static object of each game object to lookup whatever data we need, where ever we need it! Just use the static object to access it from the logic layer. (Theoretically, you can access our new statistics library from the view layer, but it's generally regarded as bad coding practice; only allow a layer to access an adjacent layer). Now, you don't have to load everything in the specified content sub-folder, I just do because Spacer is still in pretty early development stages, and I don't have enough content to notice a difference in performance, but if you've got a lot of complex content, selectively loading material would be worth considering.

So there you have a basic database layer for a game:

  •  Create a serializable model for each game object (you can use inheritance to greatly simplify these!) that is separate from your logic that updates the object
  • Make as many XML (or JSON, or binary,...) docs as you want for object data
  • Populate and access via a static library object