Hey Cocos2d Noob - A Letter To Past Me: Part 3

If you missed them, catch Part 1 here, and Part 2 here.

Wow, it's been a long time. I shouldn't have left you. Without a new post to review. I was sucked into a new project that started off as a Ludum Dare Jam and then turned into a full game (check it out after you read this). I learned a lot going through the process and I recommend a game jam for any aspiring game dev. Unfortunately, adding a new project into the mix meant the next installment of this series was delayed. Time to dig back into my archive of horrible mistakes...

Filename@2x.png

When the retina display came out Apple gave us a simple way to automagically load retina assets instead of "SD" assets. Adding "@2x" to the retina version of the asset solved all our woes.

When I first came to cocos2d I was already in the habit of adding "@2x" to all of my retina assets, so I continued to do so. This is not the recommended naming convention for cocos2d because Apple's special handling of the "@2x" images can cause subtle bugs when interacting with cocos2d. Instead we should add "-hd" to all retina display images. Do this from the start and avoid having to unleash your command-line-fu to rename all your images. It is amazing how many issues can be avoided by just reading the documentation.

Another bonus of the "-hd" suffix is that it works for other asset types such as plists, fonts, and TMX files. If you need to load different assets for retina than for sd, then just slap an "-hd" on the end of the retina version.

Ticking Away The Moments

Within the main game loop a monster lies in wait. It will devour noob game developers indiscriminately. It doesn't matter if you are using cocos2d, another framework, or no framework at all. This monster's name: Time.

A very important thing to recognize is that not every step through the game loop will take the same amount of time. This is especially true as you move from one device for another.

A Story: You have carefully tweaked your monster speed to charge at your player at a rate of 10.698 units per update. The time through your update loops are variable, but you haven't caught on because they are always similar enough for the effect to go unnoticed. Now you load the game on your friend's shiny new iPhone 5 with it's awesomely powerful processor. Holy badunks! You can no longer react fast enough to escape the monsters! This game is impossible!

Since you used a fixed delta per game loop iteration and since the faster device can iterate more times per second, you have ended up with a game that no longer works as intended on a faster device. Don't feel too bad about it, many older games built for a specific platform suffer the same fate when run on newer hardware.

Luckily, this is an easy to solve problem if you address it from the start. Take the delta into account with each update and you will avoid this problem entirely. If you have a step method that looks like this:

-(void) step: (ccTime) delta;

Multiply the time delta by a velocity to determine the positional delta.

If you are using a physics system like chipmunk, then the delta should be passed into the step method (cpSpaceStep). The physics engine will then take care of all of the grunt work for you, but you still need to be careful with anything you update from outside the physics system.

Aside: There are two ways that physics engines can be updated, fixed step or variable step. There are pros and cons to each approach. Engines will facilitate one or both approaches. A good starting place to learn more is this thread on gamedev.stackexchange.

Shuffling

The last tip of the day comes in the form of some FREE code. The internet is just full of this stuff! (Always check the attached licenses) Odds are you will eventually need to shuffle the elements of an array. An easy way to do this is to create a category for NSMutableArray. This will give you a highly reusable piece of code, and then you won't have to write new shuffling code everywhere you need to randomize array elements.

A discussion on shuffling an NSMutable array using a category can be found over on StackOverflow. I have included my own slightly modified version below for ease of Copy-Paste.

52616e646f6d; 52616e646f6d

If you want to randomize an array the first time you generate it, and then want to be able to regenerate that same sequence again in the future then you can make a few modifications to the code above (or add a new method).

Replace:

int n = (arc4random() % nElements) + i;

With:

int n = (rand() % nElements) + i;

Then before calling shuffle, call srand() and pass in your saved seed value.

// Our first play of Taco Madness

srand(423452345);

[tacos shuffle];

// resulting order => beef, chicken, fish, pork, brain

 

// Replaying Taco Madness later using the same seed

srand(423452345);

[tacos shuffle];

// resulting order => beef, chicken, fish, pork, brain

// same results as first run

 

// Starting a new game of Taco Madness and therefore using a new seed

srand(76864432);

[tacos shuffle];

// resulting order => pork, chicken, brain, fish, beef

// new results due to new seed

This will guarantee that shuffling will return the same result as the last time you shuffled the same array with the same seed. They call them pseudo-random for reason.

Next Time

In the next installment I am going to go through an entirely new class of errors related to process of releasing a game on the App Store. It will be chock full of mistakes you will absolutely want to avoid.

Editor's Notes:

  1. Glad to see that tacos were used in an example. I was beginning to grow worried.
  2. A video of the Melbourne Shuffle… really?
  3. I am imaginary.
  4. Speaking of tacos: