Categories
Cocos2d-iPhone Programming

Sprite Playground

I wrote a little Cocos2d-iPhone test app and committed the project to GitHub. (Like every dutiful hacker should.) You’re welcome to download the project and fool around with the code.

My goal was to figure out how to create a composite sprite, make sure it could respond to touches, and rotate and move it round the screen. With a little trial and error I got it working. Here’s how I did the hard parts:

I created a sprite from an PNG file and got it’s dimensions:

CCSprite * cocos2dGuy = [CCSprite spriteWithFile:@"avatar_n1_tang.png"];
CGSize spriteSize = cocos2dGuy.textureRect.size;

Then I created a CCNode to be the parent of my sprite sandwich. (I originally tried to use my cocos2dGuy CCSprite as the parent but Cocos2d stacks added on top of partent nodes and I wanted the particle effect under the cocos2dGuy not on top of him.) I set the size of the CCNode to the dimensions of the cocos2dGuy so that when you touch the CCNode it responds as if you’re touching the image. Then I add the CCNode to the layer. It’s also important to set the CCNode’s anchorPoint to the center (0.5, 0.5) as it’s default is bottom-left. CCSprite default to center anchorPoints and I want my CCNode to act like a proper sprite.

CCNode * composite = [CCNode node];
composite.anchorPoint = ccp(0.5,0.5);
composite.contentSize = spriteSize;
composite.position = centerPt;
[self addChild:composite z:0 tag:kCompositeTag];

Next I added the cocos2dGuy sprite to the center of the composite CCNode. These two entities are now both the same size and stacked on top of each other. (It’s probably not necessary for me to set the cocos2dGuy’s anchorPoint and so I’ll remove that line of code down the road.)

cocos2dGuy.anchorPoint = ccp(0.5, 0.5);
CGSize compositeSize = composite.contentSize;
CGPoint compositeCenterPt = ccp(compositeSize.width/2,compositeSize.height/2);
cocos2dGuy.position = compositeCenterPt;
[composite addChild:cocos2dGuy z:1 tag:1];

Finally I created the custom CCParticleSystem from a Particle Designer file and added it to the composite CCNode but at a lower Z value show it shows up under the cocos2dGuy sprite. See how I had to set the position and the anchorPoint to ensure everything lined up.

CCParticleSystem * fx = [ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"fireball_small_fast_green.plist"];
fx.anchorPoint = ccp(0.5, 0.5);
fx.position = compositeCenterPt;
fx.duration = kCCParticleDurationInfinity;
fx.scale = 2.0f;
fx.autoRemoveOnFinish = YES;
[composite addChild:fx z:0 tag:1];

Movement and animation are easy in the world of Cocos2d. I really like the visual effect of CCEaseExponentialInOut. It has a nice punch.

Handling touches are bit harder. I had to remember to set self.isTouchEnabled = YES in my init method and override both registerWithTouchDispatcher and ccTouchBegan. The key bit of code for responding to a touch is isTouchForMe:

bool hit = false;
for(CCNode * node in [self children]) {
    if(node.tag == kCompositeTag) {
         hit = CGRectContainsPoint([node boundingBox], touchLocation);
         if (hit) {
             [self hiliteSprite:(CCSprite *)node];
         } else {
             [self turnSprite:(CCSprite *)node andAttackPoint:touchLocation];
         }
    }
}

The main thing here is to give each touchable node a unique tag and test each node in the layer for that tag. If you touch the sprite he inflates for a moment. If you touch an empty part of the screen the sprite turns and launches himself at your finger. Ouch!

Categories
Cocos2d-iPhone Programming

Cocos2d-iPhone Sprite Rotation to an Arbitrary Point

I had some time during the Thanksgiving weekend to work on Dungeonators. I’m hoping to get an upgrade out to the App Store soon. One thing I needed //TODO: is refactor my rather poor implementation of rotating a sprite to face another sprite. My original code worked ok, in a roundabout way, but was ugly and mysterious.

(My iPhone game is full of ugly mysterious code: As I implement my ideas in code I first focus on getting it to work. Then, if I have time I go back and fix it to work cleanly. Generally cleanly means boiling the code down to the fewest number of lines possible, making it as functional as possible, and using functions from the operating system or open source code libraries as much as possible.)

I won’t show you my original sprite rotation implementation. Some things are too gross even for the Internet. Let’s just say it was written as if I was trying to remember high school trigonometry by trial and error.

Instead below is the final implementation, useful to any Cocos2d-iPhone programmer who wants a sprite to rotate to face an opponent at any point on the screen:

- (void) rotateSourceSprite:(CCSprite *)sourceSprite toFaceTargetSprite:(CCSprite *)targetSprite {

    // Calculation
    CGPoint difference = ccpSub(sourceSprite.position, targetSprite.position);
    CGFloat rotationRadians = ccpToAngle(difference);
    CGFloat rotationDegrees = -CC_RADIANS_TO_DEGREES(rotationRadians);
    rotationDegrees += 90.0f;
    CGFloat rotateByDegrees = rotationDegrees - sourceSprite.rotation;

    // animation
    CCRotateBy * turnBy = [CCRotateBy actionWithDuration:0.5f angle:rotateByDegrees];
    CCEaseIn * ease = [CCEaseIn actionWithAction:turnBy rate:4];
    [sourceSprite runAction:ease];
}

And here’s how it works, just incase you’re curious…

  • The method takes a source sprite and as target sprite as parameters. After it runs it will rotate the source to face the target.
  • The calculation part uses Cocos2d helper functions and macros (ccpSub, ccpToAngle, and CC_RADIANS_DEGREES) to figure the maths. In my original implementation I had written my own versions. Using the Cocos2d API makes my code easier to read and perhaps faster.
  • The maths work like this: Create a vector out of the distance between to points. Convert the vector to an angle. Convert the angle from radians to degrees and negate it*. Add 90 degrees to the angle to get the proper orientation. (The +=90.0f value is game specific: My sprites are rotated 90 degrees to start with.) Subtract the penultimate result of the calculation from the current rotation property (angle) of the source sprite to get the final result (the amount to rotate by).
  • The animation, which could easily be factored out into a method of its own, uses Cocos2d’s CCRotateBy and CCEaseIn to turn the source sprite to face the target in half a second. This part didn’t change from the original code. I didn’t factor it out because in my game I always use these two pieces together. I don’t like to do too much work in the abstract because I usually end up making my code too complex.

* The need to negate the value produced by CC_RADIANS_DEGREES is a bit of a mystery to me. If I don’t do it my sprite ends up facing in the opposite direction.

Categories
Cocos2d-iPhone

Invalid Code Signing Entitlements

Finally after 11 months I submitted my iPhone game, Dungeonators, to iTunes Connect for inclusion in the Apple App Store. I’m planning on giving it away so the “warcraft meets angry birds” funs is accessible to everyone! I hope it passes Apple’s review process 🙂

It took several attempts for me to get to “waiting for review status”. Here are the step I went through in hope that if you, in the near future, need to submit an app for the first time, you can succeed on your first attempt.

At this point set a cold beer within easy reach…

  1. Most of the info from Apple and the books on iPhone development is based on the old Xcode 3-based application submission process. Google Xcode 4 iTunes connect for the most recent tips. I’m not even going to include a link because the info goes stale fast. Just google it.
  2. Set your app’s pricing and metadata in iTunes connect. You’ll need a 512 x 512 icon too.
  3. Follow all the directions about distribution certificates and distribution provisioning profiles. Like a double-linked list your distro cert should point to your provisioning prof and vice versa.
  4. First create the distro cert via keychain access app on your map. Make sure there is key associated with your distro cert after you install it in your key chain.
  5. Second create your distro provisioning prof and install it in Xcode 4. Make sure your target’s code signing entitlements point to your distro provisioning profile for the release build.
  6. Clean and Archive your app with the Product menu in Xcode 4. The Archive scheme should be set to your release configuration.
  7. In the Organizer window validate and submit your app. You’ll need your iTunes Connect password.
  8. If you get the dreaded “invalid coding signing entitlements” don’t panic. It could mean a lot of different things. But if your iOS app is simple then it’s most likely  the iCloud entitlement. (Google it for more info.)
  9. Go to App IDs in the provisioning portal. Scroll to the bottom. Click configure on the row that represents your app’s ID. Uncheck Enable for iCloud. Click Done. Create a new distro provisioning prof and reinstall it into Xcode 4. Go to iTunes Connect and reject your binary. Go back to Xcode’s Organizer window and do Validate and Submit again.
  10. If you get an invalid binary. Drink a beer and try again! (do while beer != empty)
See, that was easy!
Categories
Cocos2d-iPhone Programming

Cocos2d Tip#3: Making Your iPhone Game Fast

My first iPhone game, that might actually make it to the App Store, is just about done. (Not done done but almost ready for testing and tweaking.)

With the idea that other people beside me might actually play my game I’ve started to do a very dangerous and high risk activity: Optimization!

The all the famous Computer Scientists and Software Architects agree: Optimizing your program is a necessary evil, like the death penalty or bug tracking systems, and to be avoided at all costs.

Well, that’s not quite true. It’s premature optimization that is the bad guy here. Don’t start out programming with performance or scalability in mind, least you optimize code that doesn’t need it and miss optimizing code that really needs it. (It’s the same for bug tracking systems–don’t put all your junk in Jira or Bugzilla–just the verifiable, repeatable bugs that you might actually fix one day.)

When I started my game I knew my ideas were going to evolve and I didn’t want to hard code myself into a corner. I consciously wrote code that I knew would be slow but flexible.

For example I used expensive string comparisons instead of testing for cheap numeric constants. During the initial development phases of my game the strings were self-documenting (I could read them like little notes to myself) and easy to change. Later, when my game solidified and the string comparison were easy to find using Xcode’s Find in Project command (which is now Xcode 4’s Find in Workspace command)…

// before optimization
if ([goodGuy.role isEqualToString(@"tank") {
   // aggro something
} else if ([goodGuy.role isEqualToString(@"healer") {
   // fix something
}
// after optimization
switch (goodGuy.role) {
   case CharacterRole_tank:
      // aggro something
      break;
   case CharacterRole_healer:
      // fix something
      break;
   default:
      // oops! This should not happen
      CCLOG(@"unknown character role in %@",NSStringFromSelector(_cmd));
      break;
}

Changing the type of Character’s role property from NSString* to CharacterRole (a typedef’ed int) sped up my game in update methods which were called with every frame change. It was a pain to add all the constants but at least I only had to do it once–even though my conception of a Character and a role changed quite a bit.

// Character.h
typedef enum {
    CharacterRole_tank = 0,
    CharacterRole_healer = 1,
    CharacterRole_melee = 2,
    CharacterRole_ranged = 3,
    CharacterRole_boss = 4,
    CharacterRole_add1 = 5,
    CharacterRole_add2 = 6,
} CharacterRole;

// Character.m
@interface Character : NSObject  {
   NSString* _name;
   CharacterRole _role;
   // ...
}
@property (readwrite, copy, nonatomic) NSString* name;
@property (readwrite, assign, nonatomic) CharacterRole role;

Another big optimization I did was to replace for-loops and with Cocos2d’s fast enumeration macros…

// before optimization
Character* goodGuy = nil;
for (int i = 0; i < self.goodGuys.count; i++) {
   goodGuy = [self.goodGuys objectAtIndex:i];
   // do something with good guy
}

// after optimization
Character* goodGuy = nil;
CCARRAY_FOREACH(self.goodGuys, goodGuy) {
   // do something with good guy
}

My list of good guys changes during the game so I stored them in a CCArray–Cocos2d’s version of a NSMutuableArray. CCARRAY_FOREACH is a Cocos2d macro supercharged for fast array access. I could have used the Objective-C version of a fast numerator but I like to use as much of Cocos2d as I can. That way when I port my game to Cocos2d-x (C++ cross-platform version) it will be less work 🙂

One of the biggest optimizations I did was to upgrade from Cocos2d 0.99.5-rc1 to 1.0.0-rc. You have to be smart about when you upgrade to a new version of a framework. I don’t agree with the idea of living on the bleeding edge–stability of the platform allows me to focus on my own bugs!

Upgrading from Xcode 3 to Xcode 4 also seemed to help, or at least not hurt, performance. The new version of Cocos2d seems to work better with Xcode 4. I’m still getting used to the shiny new Xcode–it acts weird and slow on my Mac Mini.

To make framework and Xcode upgrades easy I put very little code into my app delegate class. All I have to do is to comment out…

//[[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];

and replace it with…

[[CCDirector sharedDirector] runWithScene: [HomeScreen scene]];

There are lots more optimization I can do if my performance isn’t where I want it to be: Unroll my loops and get rid of Character lists altogether. But I’ll lose a lot of abstraction and flexibility so I’m not going to optimize anything more unless my beta testers complain!

 

 

Categories
Cocos2d-iPhone Programming

Cocos2d Tip #2: Using CCTimer in Your iPhone Game

If you’re writing almost any type of game, from a puzzler to a FPS to an RTS, tracking time is critical element of the game play. (Except for Angry Birds. You can ponder an Angry Birds level until your iPhone battery runs dry without penalty.)

Cocos2d-iPhone provides several means for tracking time in your game and scheduling methods to be called at both regular heartbeats and at arbitrary points in the future. The easiest way to manage time in a Cocos2d-based game is to use a CCLayer’s scheduleUpdate or schedule methods. Both methods are explained nicely in the timers section of the Cococ2d Best Practices guide. The guide also explains why you should try to avoid using iOS’s NSTimer class. (Your game will miss out on automatic pause and resume if do use NSTimer.)

But what if you can’t use scheduleUpdate or schedule in your game because you’re putting your game logic into a custom class instead of a CCLayer or CCSprite?

For the sake of simplicity and portability I don’t subclass CCSprite in my games. Instead, I create custom classes to represent my game objects and call update:(ccTime)delta on them from CCLayer objects to make stuff happen. I needed a nice timer class and started to write my own. About halfway though this project I ran into Cocos2d’s CCTimer. Since I’m trying to write as little code as possible I abandoned my timer. CCTimer is a nicely written, lightweight timer class that uses NSInvoker to create callbacks and still integrates will with the rest of the Cocos2d-iPhone framework.

Here’s how it use it…

First, I store a reference to a timer in my non-Cocos2d class:

// Zombie.h
#import "cocos2d.h"
@interface Zombie : NSObject {
    BOOL _resurrectable;
    ccTime _resurrectionCountdown;
    CCTimer* _resurrectionTimer; // own
    float _rebirthPenalityPercent;
}
@property (readwrite, nonatomic, assign) BOOL resurrectable;
@property (readwrite, nonatomic, assign) ccTime resurrectionCountdown;
@property (readwrite, nonatomic, retain) CCTimer* resurrectionTimer; // own
@property (readwrite, nonatomic, assign) float rebirthPenalityPercent;
- (void) startResurrectionTimer;
- (void) stopResurrectionTimer;
@end
// Zombie.m
#import Zombie.h
@implementation Zombie
@synthesize resurrectable = _resurrectable;
@synthesize resurrectionCountdown = _resurrectionCountdown;
@synthesize resurrectionTimer = _resurrectionTimer;
@synthesize rebirthPenalityPercent = _rebirthPenalityPercent;
+ (id) getInstance {
    CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
    return [[[self alloc] init] autorelease];
}

- (id) init: {
    CCLOG(@"** %@: %@", NSStringFromSelector(_cmd), self);
    if ((self = [super init])) {
        self.resurrectable = YES;
        self.resurrectionCountdown = 30.0f; // half a second
        self.resurrectionTimer = nil;
        self.rebirthPenalityPercent = 0.10f; // 10% less health
    }
}
@end

Second, I define a call back method to stop the timer:

// later on in Zombie.m
- (void) stopResurrectionTimer {
    CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
    if (self.resurrectionTimer != nil) {
    // the only way stop a timer is to deallocate it
        [_resurrectionTimer release];
        _resurrectionTimer = nil;
    }
    // do stuff when the zombie dies...
}

Third, I define a method to start the timer:

- (void) startResurrectionTimer {
    CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
    if (self.resurrectionTimer != nil) {
         // if there is an old resurrectionTimer then deallocate it
         // (this is the only way to stop a timer)
        [_resurrectionTimer release];
        _resurrectionTimer = nil;
    }
    //  allocate a new timer with a built-in factory method
    // the stopResurrectionTimer method is called when
    // the time is up!
    self.resurrectionTimer = [CCTimer timerWithTarget:self 
                                             selector:@selector(stopResurrectionTimer)
                                             interval:self.resurrectionCountdown];
}

Fourth, I add a call to update the timer in my CCLayer’s update method:

// ZombieGameLayer.m
- (void) update:(ccTime)delta {
    [self.zombie.ressurrectionTimer update:delta];
}

Finally, Somewhere deep in my game logic I start the timer at the right dramatic moment.

// somewhere else in Zombie.m
- (void) die {
    // call when killed
     CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);

    // if resurrectable start resurrectionTimer
    if (self.resurrectable) {
        [self startResurrectionTimer];
    }
}

It’s important to note that at the time of this blogging, the CCTimer documentation claims the interval value used to determine the length is in seconds, but really it’s in milliseconds (that is 1/60 of a second). If want your timer’s call back to run in 2 minutes set the CCTimer interval to 160.0f.

It’s also important to note that CCTimer is independent of frame rate: This might be obvious but sometime you want to execute an action every frame an sometime you want to execute it every few milliseconds.

If things aren’t working make sure you’ve told your layer to schedule updates in it’s init method and make sure you are updating your timers from your layer 🙂

Categories
Cocos2d-iPhone Programming

Objective-C Memory Management For Newbies

Below you will find a list of memory management rules that will make your Coco2d game coding experience easier and your games less buggy. But before you dive in please read the caveats below:

  • These rules are based on several sources written by engineers with much more experienced than me in Objective-C and Cocos2D development. Any mistakes are my own, any good idea belong to them.
  • My sources include this tutorial by Ray Wenderlich and the comments by fufie. Ray’s tutorials should be read by every aspiring Coco2d-iPhone developer!
  • I also found this Stack Overflow question and answer on property declarations and this blog post on properties by CocoaCast really helpful as well.
  • Of course all this info is contained in Apple’s Memory Management Programming Guide in greater detail. RTFM, as they cheerfully remind us in IRC, is a great way to learn.
  • In many ways this blog post is just a briefing on all of the above material and my motivation for posting this info is just to have it all in once place and neatly summarized.
  • Finally, these rules are guidelines for inexperienced developers, not laws. Engineers who are experienced with Objective-C memory management and Cocos2d don’t need these rules and shouldn’t even bother to waste their time and read my post. Following these rules will keep newbies out of trouble with their first game or two.

10 rules that make memory management a breeze…

Rule #1: Always create properties for your ivars in your .h and synthesize them in your .m.

// in MyClass.h
@interface MyClass : NSObject {
@public NSString* _moniker;
}
@property (nonatomic, copy, readwrite) NSString* moniker;
@end

// in MyClass.m
@implementation MyClass
@synthesize moniker = _moniker;
@end

Rule #2: Use an underscore so the compiler can distinguish an ivar from a property with a common root name. When synthesizing remember to associate the ivar with the property. This way the compiler will yell at you if you try to set an ivar as if it were a property.

@synthesize moniker = _moniker; // ivar = property

Rule #3: Always access your internal properties through self using dot syntax to ensure proper memory management through property declarations.

self.moniker = @"Betty"; // the ivar _moniker now refers to a new copy of the string "Betty"

Note: In C, C++, and Objective-C the operand on the left must be an lvalue so you can’t chain assignments. Code like

self.rectangle.top = 10;

generates a compiler error.
Instead you have to unpack the object and assign it back to the property:

Rectangle* rect = self.rectangle;
rect.top = 10;
self.rectangle = rect;

Rule #4: Always provide an object factory class method to create an instance of your custom objects using autorelease for simple memory management.

+(id) getInstance {
	return [[[self alloc] init] autorelease];
}

Rule #5. When declaring a property for a basic type or for objects you don’t own use the assign attribute.

@interface MyClass : NSObject {
@public int  _counter;
@public NotYourObject* _notMyObject;
}
@property (nonatomic, assign, readwrite) int counter;
@property (nonatomic, assign, readwrite) notMyObject;
@end

Rule #6: When declaring a property for a subclass of NSObject use the retain attribute to retain the object on assignment and release the original object (if any) associated with the property.

@interface MyClass : NSObject {
@public Something*  _thing;
}
@property (nonatomic, retain, readwrite) Something*  thing;
@end

Rule #7: When declaring a property for an NSString use the copy attribute to create a copy on assignment and release the original string (if any) associated with the property. You probably want the assigned string and the original string to be two independent objects.

@interface MyClass : NSObject {
@public NSString*  _moniker;
}
@property (nonatomic, copy, readwrite) NSString* moniker;
@end

Rule #8: Use the autorelease to automatically release objects when you are done with them.

self.thing =  [[[SomeThing alloc] init] autorelease];

Rule #9: Apple API calls that begin with “init” or “copy” need to be managed by you and released when you’re done with them. You can use rule #8 to make releasing objects at the end of a method painless.

self.moniker = [NSString stringWithFormat:@"%@ number %i", @"Betty, 2]; // already autoreleased
self.moniker = [[NSString initWithFormat:@"%@ number %i", @"Betty, 3] autorelease]; // needs autorelease

Rule #10: Always use the nonatomic attribute when declaring properties unless you are working with multiple threads–and if you are working with multiple threads you are way above the level of these rules.

@property (nonatomic, assign, readwrite) int counter;

Rule #11: Always use the ivar in your delloc method to release objects you own and set them to nil (to ensure safe subclassing).

[_moniker release];
_moniker = nil;

A thought on static analysis…

Xcode’s Build and Analyze (Shift-Cmd-A) always drives me crazy with false positives. First, it finds potential issues with the Cocos2D framework source files and I’m not about to worry about or fix any of those. Second, all the issues it finds in my source files are either trivial non-issues (an assignment hidden inside an if-then-else block) or not problems at all (releasing object that it thinks I don’t own). But perhaps it’s my coding style that needs to improve. I hate letting warnings go by unresolved and as soon as figure out a way to write code the makes static analysis happy I’ll let you know.

Categories
Tech Trends

Nope: I Haven’t Stopped Reading

Steve Jobs is a man we all admire and respect. Even when he says something, well, dumb. Back in January of 2008 Steve told a NY Times reporter that “… American’s have stopped reading.” Steve cited this as the reason the Amazon Kindle would fail. In the past year and half the Kindle has been selling pretty well. But it gets even better: E-book readers from Amazon and Barns & Noble are popular applications for the iPhone. There are over 40o book-related applications available for download on Apple’s App Store. One of my favorites is the Kindle for iPhone.

I don’t own a physical Kindle. I don’t need a another electronic device. All the sockets on my power strip are filled! The iPhone is my phone, emailer, broswer, PIM, GPS, notebook, music player, camera, and now e-book reader. While I don’t agree with Steve that Americans have stopped reading I do agree that that specialized single function harward like the Kindle has a dim future. Kindle software, and book readers in general, on small multifunctional devices like the iPhone have a bright future.

Since I downloaded the Kindle software I’ve bought and read 6 digital books in 60 days. But here’s the important point–I would not have read these books otherwise. I don’t get a lot of traditional time to read between a busy startup and a house full of kids. With the iPhone I can read while waiting on a line at the pharmacy, or while sitting on a stalled train somewhere between Newark and New York, or while waiting for soccer practice to fishing up.

When I was younger (by several decades) I used to carry around a paperback book on the off chance that I’d get a moment to read it. These days I don’t have the time to think that far ahead. Downloading and reading books on the iPhone is just so damn convenient it has replaced Blocked as my favorite time biding activity.

The only real issue is Amazon’s digital supply of Nebula Award winners is running dry. Some important books from the list, The Quantum Rose, Parable of the Talents, Forever Peace, and Moon and Sun, are tragically missing. I’m anxious to download Barns & Noble’s Bookstore reader and see if they can pick up the digital slack.