Use categories in Objective C to add instance variables to a CCSprite or anything else

Ok, I’ve got this down to about 10 lines of code.

I’ve got some button sprites in Cocos2D that need an instance variable.  I’m already using userData, and found that I was starting to overload it to use it the text in there for more than one purpose… if you start stripping off substrings from the userData to code for things, you might be interested in having another instance variable.

So you want to add “.mydata” to your sprites?  Try this:

1.  Decide where you want the scope of this.  In my app, there’s only one scene with one layer, and so in the MyLayer.m file where all my button sprites are, I’m adding this code at the top, just above the @interface MyLayer line:

@interface CCSprite (MyExtras)
@property (nonatomic,retain) id mydata;
#import <objc/runtime.h>
static char const * const mydataKey = "mydata";
@implementation CCSprite (MyExtras)
@dynamic mydata;
- (id) mydata { return objc_getAssociatedObject(self, mydataKey); }
- (void) setMydata:(id)new { objc_setAssociatedObject(self, mydataKey, new, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }

That’s 10 lines.

2. Test it out.  Something like this:

CCSprite* buttonTest = [CCSprite spriteWithFile:@"buttontest.png"];
[buttonTest setMydata:[NSNumber numberWithInt:6]];   //use official setter method
buttonTest.mydata = [NSString stringWithFormat:@"hello"];  //ok too

All this does is attach an object to your sprite button, and spit it back out into the console log.  It’s flexible… attach any object you want this way, pretty much.  The second line makes it a NSNumber object, and the 3rd line replaces that with a NSString.  The only restriction here is you have to give .mydata an ID… that means you can’t say:

[buttonTest setMydata:6];

Because 6 is an int, not an id.  Get around that by using NSNumber.

If you want to read more, there’s lots of blog posts out there about this topic (try “associative references” in Objective C), and how to add other methods to extend existing classes, etc.  But this is just one common use case, simplified to its essentials.


Real quick new Kobold2D templates

So, in the project I’m working on now, we need new Kobold2D templates every week or two.  Why?  It’s easier this way… the two of us code separately mostly in different parts of the app, then merge every few days or weekly, and going forward we make a new template to use.  Then we can start and try test apps with new features, and not worry about them.

Real quick new template procedure:

1.  Clean Up Your App:  You have a working app in Kobold2D that you want to use as your new template.  Great.  Now clean it up.  I recommend putting all your Resources neatly organized in a /Resources folder, and all your classes in a /Classes folder, inside your /Projectfiles folder.  Really.  Do this.  In a Finder window, so that the Groups of classes and Resources in XCode match the folder organization in Finder.  Feel free to have folders in folders.  Ok… is it clean and tidy?  Groups in XCode match folders in Finder exactly?  Ready to move on.

2. Make A New Template App:  Close XCode, and use Kobold2D Project Starter app to make a new app, named “_Code7-Template_” (this template will then show up as “Code7″ in the Kobold2D Project Starter app when you’re done).  Click Create Project From Template to start this up in XCode.   Use the Empty-Project template if this is your first template, or use your latest template as a starter, since that should be similar to this one structurally.

[You don't have to close XCode first, but it needs to restart.  Choose Revert usually, if it asks because you didn't follow my suggestion to close it.]

3. Clean Up The Template in XCode:   In your new “_Code7-Template_” app in XCode, open the Projectfiles group.  Get rid of the HelloWorldLayer and AppDelegate class files (select, right click, DELETE – not just remove references).  Do the same for the Resources group (including the config.lua file… you’re going to use a new one, right?).  Leave the “Supporting Files” group alone (main.m and two pch files).  These deletes will render the app useless and broken for the moment.  That’s ok.

4.  Close XCode.  You don’t want it open right now while you change things in Finder.

5.  Clean Up The Template in Finder:  In Finder, go to your new template app (something like: ~/Kobold2D/Kobold2D-1.0.1/_Code7-Template_/).  Clean out the Projectfiles folder.  It will probably still have a /Resources folder in it.  Get rid of it.  Leave the other three Supporting Files files.

6. Add Your New Files In Finder:  Open another Finder window and copy (Option-Drag-Drop) your neatly organized Classes and Resources folders full of goodies from your working app, into the Projectfiles folder of the Code7 template you just cleaned up.

7.  Add New Files in XCode:  Restart XCode with your Kobold2D workspace.  Navigate to the Projectfiles group of your Code7 app.  Right click on Projectfiles, and select the Add files to “_Code7-Template_” option.  Browse to your Projectfiles folder of the Code7-Template, and select the Classes folder, and add it, being sure to select Create Groups for any added Folders.  Repeat to add the Resources folder.

8.  Bingo.  Test your app in the simulator to make sure it works just like the one you’re copying from.  Now it’s ready to become the newest template in your arsenal.

9. Make it a template: Close XCode (or not).

(a) In Finder, copy your _Code7-Template_ folder over into the ~/Kobold2D/Kobold2D-1.0.1/__Kobold2D__/templates/project folder, where the other templates reside.

(b) Make a _description_ file, by copying one from another template into your template folder, opening it up in a text editor, and rewrite it to describe this template.  Save.

(c) You’re done.  If you’ll be sharing this template with anybody, get rid of your build products taking up space… right click on the _Code7-Template_.xcodeproj package, and select Show Package Contents to open it in a new Finder window.  Trash the xcuserdata folder, it’s unwanted now.

10.  Test it… use Kobold2D Project Starter app and your new template should show up.  Start a new app (Code8 of course) and keep moving.

I know, ten steps doesn’t sound so “real quick” but I’ve got the whole thing down to about 5 minutes now, because my app structure is clean and I know what to do.  You can too.








Kobold2D HowTo – making a new template for Cocos2D apps

For a new project, I’m learning Cocos2D, and for this I’m using Steffan Itterheim’s Kobold project.  It’s pretty cool.  You hit the Kobold2D Project Starter app, select a template to work with and a name for your app, and hit go…. it builds a basic Cocos2D app for you, ready and working, and formatted to compile for iPhone or iPad, and in many cases for OS-X compilation (not all Cocos2D features will work – YMMV).

So far, I’m finding Kobold2D (and Cocos2D) pretty nice.  But what I wanted to do was build a whole series of test apps that will provide the building blocks I need for future development.  Like… there’s no way in Cocos2D to make a text box that displays formatted HTML.  So what you need to do is use a UIWebView… but tied into Cocos2D somehow.  Kobold2D has a sample template that shows one way to do this, and I used that as a starting point, but it seems cludgy to me.

So I went looking and found CCUIViewWrapper, an extension for Cocos2D that seems to do pretty much what I want… just instantiate a UIWebView and build a wrapper around it and add the HTML code into it that you want.  I’ll probably write another post about that (cause it’s nice), but this post is because I want now to put a new template into the Kobold builder that includes all the extra stuff I want (and none of the Kobold stuff I don’t) and use this new template for my next building block explorations.

So I read Steffen’s “Creating a Kobold2D Template Project” page, and didn’t quite get it.  The nuggets are there, but it’s not a step-by-step, so I’m making one here, in case you’re looking for one.

1. Read his “Creating” page first.  Pay attention to the naming section.

2. Use his Kobold2D Project Starter app to start a new project.  Pick one that’s close to what you want.  Name it according to the rules in Step 1, like “_My-First-Attempt-Template_”.  Key: it starts with an underscore, and ends with “-Template_” and everything inside is just alphanumeric text and hyphens, nothing fancy.

[EDIT]   (A LATER NOTE) Also, I’ve found that any hyphen-separated parts of the name should be Capitalized.  Like “_My-second-attempt-Template_” may not work, whereas “_My-Second-Attempt-Template_” would.  I did this wrongly, and the template never showed up in the Kobold2D Project Starter menu!  [/END EDIT]

3. That should open you into Xcode with your new project started.  The first thing is to compile it with one of the simulators, like the ones you’re aiming for.  This is just to be sure it works.  It should.

4. Change the source codes to whatever you want.  Compile along the way, making sure it all works.  Add resources, etc.  Make sure everything you need is copied into the template folder so it doesn’t rely on outside things, unless that’s the way you want to roll.

5. When you’re satisfied, you’re done here.  Go to the Finder and find the the _My-First-Attempt-Template_ folder you’ve been working on.  Copy that entire folder over to the __Kobold2D__/templates/project folder.  That will allow Kobold2D to find and display it as one of it’s templates.

6.  Open the _My-First-Attempt-Template_.xcodeproj to see the Package contents (right click on it in Finder, choose “Show Package Contents”.  In this package, delete the “xcuserdata” folder (this is not needed, and will give the wrong scheme names in your new templated projects if it is not deleted).

7.  Add a new plain text file in the root of the _My-First-Attempt-Template_ folder called “_description_”.  Write a brief description of the template in this file.

8. Try it!  Run Kobold project starter and it should let you make a new project just the way you want it with your new template.  Test the new project thoroughly to see it works right.  It should.

9. Use your new template yourself, or share with everyone else by giving your code an MIT open source license and release it on github and tell Steffan about it.  If it’s useful, he says he’ll consider packaging it up with his templates too.