Archive for category cocos2d iPhone
Easy To Create Buttons with Cocos2D
Posted by Eric in cocos2d iPhone, iPhone Development on October 8th, 2009
Those of you who use cocos2d a lot might understand why I created this class as some point. Hopefully it may save some of you those nasty 5 line blobs that you normally need to create a simple button. Usage is simple, just do:
[self addChild:[Button buttonWithText:@"back" atPosition:ccp(80, 50) target:self selector:@selector(back:)]]; [self addChild:[Button buttonWithImage:@"openFeint.png" atPosition:ccp(400, 50) target:self selector:@selector(openOpenFeint:)]];
You’ll need to create your own button.png and button_p.png (the second one is the image shown when you are touching the button). Also you’ll need to choose your own font. Here is the code…
// // Button.h // StickWars - Siege // // Created by EricH on 8/3/09. // @interface Button : Menu { } + (id)buttonWithText:(NSString*)text atPosition:(CGPoint)position target:(id)target selector:(SEL)selector; + (id)buttonWithImage:(NSString*)file atPosition:(CGPoint)position target:(id)target selector:(SEL)selector; @end @interface ButtonItem : MenuItem { Sprite *back; Sprite *backPressed; } + (id)buttonWithText:(NSString*)text target:(id)target selector:(SEL)selector; + (id)buttonWithImage:(NSString*)file target:(id)target selector:(SEL)selector; - (id)initWithText:(NSString*)text target:(id)target selector:(SEL)selector; - (id)initWithImage:(NSString*)file target:(id)target selector:(SEL)selector; @end
// // Button.m // StickWars - Siege // // Created by EricH on 8/3/09. // #import "Button.h" @implementation Button + (id)buttonWithText:(NSString*)text atPosition:(CGPoint)position target:(id)target selector:(SEL)selector { Menu *menu = [Menu menuWithItems:[ButtonItem buttonWithText:text target:target selector:selector], nil]; menu.position = position; return menu; } + (id)buttonWithImage:(NSString*)file atPosition:(CGPoint)position target:(id)target selector:(SEL)selector { Menu *menu = [Menu menuWithItems:[ButtonItem buttonWithImage:file target:target selector:selector], nil]; menu.position = position; return menu; } @end @implementation ButtonItem + (id)buttonWithText:(NSString*)text target:(id)target selector:(SEL)selector { return [[[self alloc] initWithText:text target:target selector:selector] autorelease]; } + (id)buttonWithImage:(NSString*)file target:(id)target selector:(SEL)selector { return [[[self alloc] initWithImage:file target:target selector:selector] autorelease]; } - (id)initWithText:(NSString*)text target:(id)target selector:(SEL)selector { if(self = [super initWithTarget:target selector:selector]) { back = [[Sprite spriteWithFile:@"button.png"] retain]; back.anchorPoint = ccp(0,0); backPressed = [[Sprite spriteWithFile:@"button_p.png"] retain]; backPressed.anchorPoint = ccp(0,0); [self addChild:back]; self.contentSize = back.contentSize; Label* textLabel = [Label labelWithString:text fontName:@"take_out_the_garbage" fontSize:22]; textLabel.position = ccp(self.contentSize.width / 2, self.contentSize.height / 2); textLabel.anchorPoint = ccp(0.5, 0.3); [self addChild:textLabel z:1]; } return self; } - (id)initWithImage:(NSString*)file target:(id)target selector:(SEL)selector { if(self = [super initWithTarget:target selector:selector]) { back = [[Sprite spriteWithFile:@"button.png"] retain]; back.anchorPoint = ccp(0,0); backPressed = [[Sprite spriteWithFile:@"button_p.png"] retain]; backPressed.anchorPoint = ccp(0,0); [self addChild:back]; self.contentSize = back.contentSize; Sprite* image = [Sprite spriteWithFile:file]; [self addChild:image z:1]; image.position = ccp(self.contentSize.width / 2, self.contentSize.height / 2); } return self; } -(void) selected { [self removeChild:back cleanup:NO]; [self addChild:backPressed]; [super selected]; } -(void) unselected { [self removeChild:backPressed cleanup:NO]; [self addChild:back]; [super unselected]; } // this prevents double taps - (void)activate { [super activate]; [self setIsEnabled:NO]; [self schedule:@selector(resetButton:) interval:0.5]; } - (void)resetButton:(ccTime)dt { [self unschedule:@selector(resetButton:)]; [self setIsEnabled:YES]; } - (void)dealloc { [back release]; [backPressed release]; [super dealloc]; } @end
2D Scrolling Game with Cocos2D TileMap with Zoom!
Posted by Eric in cocos2d iPhone, iPhone Development on October 6th, 2009
A common question I see on the cocos2d forums is ‘when I want to make my game scroll, do I move the camera or the layer?’ or some variant of that. I also got some more detailed questions about how to make a functioning 2D scroller, so I’m going to described how I got it to work.
First of all, the answer to the above question is you move the layer. If you follow the examples in the cocos2d download, you have a “GameScene” and a “GameLayer”. Well, everything that needs to be moved when your game scrolls should be added as a child to that GameLayer. If you are using a TileMap as a background, this includes that tilemap. The only thing that you don’t add as a child to the GameLayer is stuff that does not move with the scrolling view, such as your HUDLayer that has text that shows your characters health. Other than that, your character, the background, other characters, should all be added to the GameLayer.
You have to remember which objects are absolute (attached to your GameScene or other layers) versus those that are relative (a child of your GameLayer) when you set up your touch handling code. For my HUD that has buttons you can press at any time, say to pause the game, you want to add the touch handling object to your GameScene or HUDLayer class, since it doesn’t move. But if you want to be able to touch objects that scroll along with your view in the game itself, your touch handling code needs to be in an object that is a child of your GameLayer.
This might be a little confusing, so let’s see some code:
gameLayer = [GameLayer node]; [gameScene addChild:gameLayer z:zOrder_GameLayer]; hudLayer = [HUDLayer node]; [gameScene addChild:hudLayer z:zOrder_HudLayer]; tileMap = [BGTileMap node]; [gameLayer addChild:tileMap z:-1]; [gameScene addChild:[PauseGameButton node] z:zOrder_GameButtons]; [gameLayer addChild:[FireGunAtTouchPoint node]];
The pause game button is always on your screen, while the point at which your character fires the gun depends on how how far your view has been scrolled (by moving GameLayer).
Let’s see some of the code that actually moves this game layer:
- (void)setViewpointCenter:(CGPoint)point { CGPoint centerPoint = ccp(240, 160); viewPoint = ccpSub(centerPoint, point); // dont scroll so far so we see anywhere outside the visible map which would show up as black bars if(point.x < centerPoint.x) viewPoint.x = 0; if(point.y < centerPoint.y) viewPoint.y = 0; // while zoomed out, don't adjust the viewpoint if(!isZoomedOut) gameLayer.position = viewPoint; }
When do you call that method? Well, it depends on what you want, but generally these scrolling games follow around the movement of your ‘main’ character, right? So whatever character the you want to follow, add this to override the standard CocosNode setPosition method so you update your viewpoint whenever the character moves
- (void)setPosition:(CGPoint)point { [[StandardGameController sharedSingleton] setViewpointCenter:point]; [super setPosition:point]; }
Note that the StandardGameController is a construct of mine that I use to separate the game logic out from the display code. It doesn’t matter exactly how you do it, you just need a way to have your main character object call back to something that contains a reference to GameLayer so it can adjust the position of your GameLayer.
Now remember, for your background to scroll properly, you need to add your background tileMap as a child of your GameLayer that is being moved around.
That being said, I found that an important method was missing from the cocos2d tilemap that I need to use in order to detect collisions based on the types of tiles encountered. I created a subclass of TMXTiledMap and added in these methods:
- (CGPoint)coordinatesAtPosition:(CGPoint)point { return ccp((int)(point.x / self.tileSize.width), (int)(self.mapSize.height - (point.y / self.tileSize.height))); } - (unsigned int)getGIDAtPosition:(CGPoint)point { return [layer tileGIDAt:[self coordinatesAtPosition:point]]; }
That way it’s easy to figure out what tile any individual object is colliding with. For example, in my main character object I can have code that runs in step: function with this:
BGTileMap* tileMap = [StandardGameController sharedSingleton].tileMap; CGPoint coordinate = [tileMap coordinatesAtPosition:self.position]; BBLog(@"Right now on tile %d",[tileMap.layer tileGIDAt:coordinate]);
Now I know what type of tile I am overlapping, and I can respond to the environment accordingly.
This is really all the code that you need to make a scrolling game view…I think some people overthink it and try adjusting the position of every object individually with some offset, but it’s not necessary since your objects can use relative positions with their parent.
I have one last bit of code to add, and this is something kind of fun. It’s not complete, but at least it’s a start. What this allows you to do is ‘zoom out’ so you can see your entire map with ALL the objects shrunk down, and then zoom back in to your character. The only tricky part is when you zoom back in, you have to slowly adjust your viewpoint in steps so the zoom in action is centered on your character, instead of jumping at the end.
#define ZOOM_BACK_IN_INTERVALS 10 #define ZOOM_OUT_RATE 0.3 // TODO need to refine this so for each step it uses the new viewpoint - (void)setZoom:(BOOL)zoomedIn { BBLog(@"Zooming in %d", zoomedIn); // this scales it out so the whole height of the tilemap is in the screen float zoomScaleFactor = 320 / (tileMap.mapSize.height * tileMap.tileSize.height); if(zoomedIn) { [gameLayer runAction:[ScaleTo actionWithDuration:ZOOM_OUT_RATE scale:1.0]]; [gameLayer runAction:[MoveTo actionWithDuration:ZOOM_OUT_RATE position:viewPoint]]; [self schedule:@selector(setZoomedBackIn:) interval:ZOOM_OUT_RATE]; // need this for the transistion } else { [gameLayer runAction:[ScaleTo actionWithDuration:ZOOM_OUT_RATE scale:zoomScaleFactor]]; [gameLayer runAction:[MoveTo actionWithDuration:ZOOM_OUT_RATE position:ccp(0,0)]]; isZoomedOut = YES; } } // need this small correction at the end to account of the player is moving and the viewpoint has changed to avoid jitter #define ZOOM_OUT_CORRECTION_RATE 0.3 - (void)setZoomedBackIn:(ccTime)dt { [self unschedule:@selector(setZoomedBackIn:)]; [gameLayer runAction:[MoveTo actionWithDuration:ZOOM_OUT_CORRECTION_RATE position:viewPoint]]; [self schedule:@selector(setZoomedBackInFinished:) interval:ZOOM_OUT_CORRECTION_RATE]; // need this for the transistion } - (void)setZoomedBackInFinished:(ccTime)dt { [self unschedule:@selector(setZoomedBackInFinished:)]; isZoomedOut = NO; }
Now you see why my viewPoint variable was a class member…you’ll need it for these methods to work.
The reason this code isn’t complete is when it starts zooming in, it creates the actions to zoom in on the current viewpoint, but if the player is moving by the time the zoom animation is done, that viewpoint is changed and needs to ’snap’ to the new viewpoint. That is the reason for the setZoomedBackIn: method, which really shouldn’t have to move the gameLayer anymore. However, I haven’t yet written the code to continuously create smaller move actions to take into account a moving viewpoint as the animation continues, but doing so shouldn’t be that hard. If you want to see that bit when I finish, post in the comments and I’ll add it in.
Using Box2D Physics Engine with Cocos2D iPhone
Posted by Eric in cocos2d iPhone, iPhone Development on July 27th, 2009
Starting work on my new project, I’ve found Box2D to be a far superior physics engine than chipmunk. It is more mature, the API more flexible, and it seems to even perform faster. However, the cocos2d code for it was rather space, so here is a sort of helper file I had to create to make it work with my game.
Keep in mind this game is in progress and I’ve only been using this for a few days, so it may have issues that might pop up later on. It seems to be working very well for now though, with 40-50 objects on screen moving around with ~40 FPS.
Here is the header
// // Box2DEngine.h // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. // #import "Box2D.h" // made this an extern constant to avoid obj c lookup overhead extern b2World *bb_world; @interface Box2DEngine : CocosNode { } + (Box2DEngine *)sharedSingleton; - (void)createWorld:(CGSize)size; - (void)deleteWorld; - (void)runSimulation; @end
The .mm file
// // Box2DEngine.mm // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. // #import "HookActor.h" #import "Box2DEngine.h" #import "SuperBox2DActor.h" b2World* bb_world; const float32 timeStep = 1.0f / 60.0f; const int32 velocityIterations = 10; const int32 positionIterations = 10; #define MAX_NUM_COLLISIONS 2048 // TODO make sure this buffer is the right size b2ContactResult contactResultCache[MAX_NUM_COLLISIONS]; int32 contactResultCount = 0; class MyContactListener : public b2ContactListener { public: void Add(const b2ContactPoint* point) { } void Persist(const b2ContactPoint* point) { } void Remove(const b2ContactPoint* point) { } void Result(const b2ContactResult* point) { // TODO we are making a deep copy of every contact point here // check the box2d contact masks to make sure we minimize unwanted contact results contactResultCache[contactResultCount++] = *point; } }; void handleCachedContactResults() { b2ContactResult contactResult; for(int i = 0; i < contactResultCount; i++) { #ifdef BBDEBUG if(contactResultCount >= MAX_NUM_COLLISIONS) { NSLog(@"RAN OUT OF BUFFER"); assert(NO); } #endif contactResult = contactResultCache[i]; SuperBox2DActor* actorOne = (SuperBox2DActor*)contactResult.shape1->GetBody()->GetUserData(); SuperBox2DActor* actorTwo = (SuperBox2DActor*)contactResult.shape2->GetBody()->GetUserData(); if(!actorOne.isDead && !actorTwo.isDead) { [actorOne collisionResultOne:&contactResult withActor:actorTwo]; [actorTwo collisionResultTwo:&contactResult withActor:actorOne]; } } // clear the collision cache contactResultCount = 0; } void removeDeadActors() { b2Body* node = bb_world->GetBodyList(); while (node) { b2Body* b = node; node = node->GetNext(); SuperBox2DActor* actor = (SuperBox2DActor*)b->GetUserData(); if (actor.isDead) { // remove from physics engine bb_world->DestroyBody(b); // remove from game engine (cocos2d) [[StandardGameController sharedSingleton] removeGameActor:actor]; } } } @implementation Box2DEngine + (Box2DEngine*)sharedSingleton { static Box2DEngine* sharedSingleton; if (!sharedSingleton) sharedSingleton = [[Box2DEngine alloc] init]; return sharedSingleton; } - (void)createWorld:(CGSize)size { b2AABB worldAABB; worldAABB.lowerBound.Set(0, 0); worldAABB.upperBound.Set(size.width * BOX2D_SCALE_FACTOR_INVERSE, size.height * BOX2D_SCALE_FACTOR_INVERSE); // TODO this shouldnt be inverse? b2Vec2 gravity(0.0f, -30.0f); bool doSleep = true; bb_world = new b2World(worldAABB, gravity, doSleep); bb_world->SetContactListener(new MyContactListener()); } - (void)deleteWorld { [self unschedule:@selector(step:)]; delete bb_world; bb_world = NULL; } - (void)runSimulation { [self schedule:@selector(step:)]; } - (void)step:(ccTime)dt { // step the world bb_world->Step(dt, velocityIterations, positionIterations); // TODO do i use timestep or dt here? //BBLog(@"Num of contact results is %d",contactResultCount); // do stuff with collisions handleCachedContactResults(); // remove all actors that are marked as dead removeDeadActors(); // update cocosnode positions for (b2Body* b = bb_world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData() != NULL) { SuperBox2DActor *actor = (SuperBox2DActor*)b->GetUserData(); b2Vec2 position = b->GetPosition(); actor.position = b2toCGPoint(position); actor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()); //NSLog(@"obj %@ %4.2f %4.2f\n",actor, position.x, position.y); } } } @end
Create multi-line labels with cocos 2d iphone
Posted by Eric in cocos2d iPhone, iPhone Development on June 20th, 2009
Update: This code is obsolete now. You can just do
Label *messageLabel = [Label labelWithString:message dimensions:CGSizeMake(380, 120) alignment:UITextAlignmentCenter fontName:@"your_custom_font" fontSize:26];
by using the new FontManager class. For example, run this once in your app delegate when your program first loads
[[FontManager sharedManager] loadFont:@"your_custom_font"];
It can take a long NSString and create multiple labels without breaking up a word. I’ll eventually use this for my help screen, replacing the current 6 different 480×320 png images that I load for each one
.
The code is simple, but hopefully it might save somebody the time it took me to write it. I had to look up some very basic elements of ObjC here, so if there is a much easier way to do this, please let me know but don’t make too much fun of me.
You can easily switch out the BitmapFontAtlas for just a normal Label and it would work just fine.
(void) setTipString:(NSString*)str { NSInteger lineChars = 0; BOOL isSpace = NO; NSInteger index = 0; NSInteger numLines = 0; NSMutableString *line = [NSMutableString stringWithCapacity:LINE_LENGTH]; while (index <= [str length]) { if(index == [str length]) { BitmapFontAtlas *tip = [[BitmapFontAtlas bitmapFontAtlasWithString:[NSString stringWithString:line] fntFile:@"text.fnt" alignment:UITextAlignmentLeft] retain]; [tip setPosition: cpv(30,210 - 20 * numLines)]; [self addChild:tip]; return; } NSString *tmp = [str substringWithRange:NSMakeRange(index, 1)]; [line appendString:tmp]; if([tmp isEqual:@" "]) isSpace = YES; else isSpace = NO; if(lineChars >= LINE_LENGTH && isSpace) { BitmapFontAtlas *tip = [[BitmapFontAtlas bitmapFontAtlasWithString:[NSString stringWithString:line] fntFile:@"text.fnt" alignment:UITextAlignmentLeft] retain]; [tip setPosition: cpv(30,210 - 20 * numLines)]; [self addChild:tip]; lineChars = -1; [line setString:@""]; numLines++; } lineChars++; index++; } }
Using Cocos2D AtlasSpriteManager
Posted by Eric in cocos2d iPhone, iPhone Development on May 2nd, 2009
After having a lot of trouble dealing with the issue myself and seeing others having trouble on the cocos2d discussions, I’m going to go ahead and post my solution which so far is working pretty well for me. Please take into account I’m new with Objective C when looking this over.
The problem is each AtlastSpriteManager can 1) refer only to a single sheet image, and 2) each AtlasSprite can only be a child of at AtlasSpriteManager. So what happens if you a few game characters that you want to share certain animations, but not all?
I took the super-class of all my game Sprites, changed its type from a Sprite (or AtlasSpriteManager, which I tried but failed to well) to a generic CocosNode. I added in this to the header. Note that I only needed my Sprites to switch between two different sprite sheets as most.
@interface Touchable : CocosNode <Collideable> { AtlasSprite *activeSprite; AtlasSprite *sprite1; AtlasSprite *sprite2; ... } - (id)init:(CGSize)sizeOne two:(CGSize)sizeTwo; - (AtlasSpriteManager *)spriteManager1; - (AtlasSpriteManager *)spriteManager2; - (void)setSprite:(NSUInteger)newstate; - (void)setActiveSpriteOne; - (void)setActiveSpriteTwo; ...
To the implementation file…
- (AtlasSpriteManager *)spriteManager1 { return nil; } - (AtlasSpriteManager *)spriteManager2 { return nil; } - (id)init:(CGSize)sizeOne two:(CGSize)sizeTwo { self = [super init]; if(self) { sprite1 = [[AtlasSprite spriteWithRect:CGRectMake(sizeOne.width, sizeOne.height, sizeOne.width, sizeOne.height) spriteManager: [self spriteManager1]] retain]; [[self spriteManager1] addChild:sprite1]; if(![[self spriteManager1] parent]) [[[LevelController sharedSingleton] gameLayer] addChild:[self spriteManager1] z:1]; [sprite1 setVisible:NO]; sprite2 = [[AtlasSprite spriteWithRect:CGRectMake(sizeTwo.width, sizeTwo.height, sizeTwo.width, sizeTwo.height) spriteManager: [self spriteManager2]] retain]; [[self spriteManager2] addChild:sprite2]; if(![[self spriteManager2] parent]) [[[LevelControler sharedSingleton] gameLayer] addChild:[self spriteManager2] z:1]; [sprite2 setVisible:NO]; } return self; } - (void)setSprite:(NSUInteger)newstate { } - (void)setActiveSpriteOne { activeSprite = sprite1; [sprite2 setVisible:NO]; [sprite1 setVisible:YES]; } - (void)setActiveSpriteTwo{ activeSprite = sprite2; [sprite2 setVisible:YES]; [sprite1 setVisible:NO]; }
A few methods of my superclass ‘Sprite’ object that I modify slightly to take changes into account. You can no longer want to run the actions (which include animations) on yourself, but on the active sprite.
- (void)setState:(enum ActionFigureState)newstate{ [activeSprite stopAction:[stateActions objectAtIndex:state]]; [self setSprite:newstate]; [activeSprite runAction:[stateActions objectAtIndex:newstate]]; state = newstate; } - (void) setPosition:(CGPoint)pos { [super setPosition:pos]; [activeSprite setPosition:pos]; }
So what we have now is a superclass that is calling a instance method [self spriteManager1] in order to manage attach its two different sprites. Notice that no animation specific code is here in the superclass, except for the fact that a single ‘Sprite’ can work with 2 AtlasSheetManagers at most. You could easily take away this restriction, but I had no need to complicate things.
This gets helpful when I get into a child class that actually knows how to draw itself. Here is my “SmallStick.m” which has an empty header file except for being a child of Touchable. This implementation code also contains all the this object’s interaction with the game, such as collision detection, how to handle if it is touched, and how to add itself to the Chipmunk space.
#define width1 79 #define height1 68 #define columns1 7 #define width2 79 #define height2 68 #define columns2 7 @implementation SmallStick static AtlasSpriteManager *spriteManager1 = nil; static AtlasSpriteManager *spriteManager2 = nil; + (AtlasSpriteManager *)spriteManager1 { @synchronized(spriteManager1) { // I dont actually know if synchronized is needed here if (!spriteManager1) { [Texture2D saveTexParameters]; [Texture2D setAliasTexParameters]; spriteManager1 = [[AtlasSpriteManager alloc] initWithFile:@"smallstick_common.png" capacity:50]; [Texture2D restoreTexParameters]; } return spriteManager1; } return nil; } + (AtlasSpriteManager *)spriteManager2 { @synchronized(spriteManager2) { if (!spriteManager2) { [Texture2D saveTexParameters]; [Texture2D setAliasTexParameters]; spriteManager2 = [[AtlasSpriteManager alloc] initWithFile:@"smallstick_weaponless.png" capacity:50]; [Texture2D restoreTexParameters]; } return spriteManager2; } return nil; } - (AtlasSpriteManager *)spriteManager1 { return [SmallStick spriteManager1]; } - (AtlasSpriteManager *)spriteManager2 { return [SmallStick spriteManager2]; } -(id)init { self = [super init:CGSizeMake(width1, height1) two:CGSizeMake(width2, height2)]; if(self) { ... } return self; } - (void) setSprite:(NSInteger)newstate { switch (newstate) { case fState_charging: case fState_attacking: case fState_recovering: [self setActiveSpriteTwo]; break; default: [self setActiveSpriteOne]; break; } } - (void)loadActions { AtlasAnimation *chargeA = [AtlasAnimation animationWithName:@"weaponless_charge" delay:0.05f]; for(int i=0;i<18;i++) { int x= i % columns2; int y= i / columns2; [chargeA addFrameWithRect: CGRectMake(x*width2, y*height2, width2, height2) ]; } Action *charge = [RepeatForever actionWithAction:[Animate actionWithAnimation: chargeA]]; ... stateActions = [[NSArray alloc] initWithObjects: charge, dying, ..., nil];
The part I like about this is when I want to reuse the ’smallstick_common.png’ atlas sheet I can just not override the class method +(AtlasSpriteManager *)spriteManager2 in my child classes.
Don’t forget to remove the sprite from the AtlasManager when you’re done.
[[self spriteManager1] removeChild:sprite1 cleanup:YES]; [[self spriteManager2] removeChild:sprite2 cleanup:YES];
First of all, I hope this doesn’t have some hidden downside. As I said, I’m new to the platform. But I hope somebody finds this useful.
Integrating Chipmunk into Objective C iPhone Games
Posted by Eric in cocos2d iPhone, iPhone Development on April 13th, 2009
When working with Cocos 2d iPhone, a lot of the sample code provided that shows how to hook into the Chipmunk physics engine using static functions outside of any Objective C object. While this is the most straightforward way to get the engine up and running, it restricts what you can do later on.
Instead, you need to create a static function which takes two parameters, the object being updated and the objects container. Here’s some code:
This is the static function which goes outside your class implementation. Notice that we don’t actually do any of the object updates here. This is good, because we can’t access any of the internal state in the container in this method.
static void updateEachShapeCallback(void *ptr, void *parent) { GameController *parentObject = (GameController *)parent; [parentObject updateShape: ptr]; }
Note that my container, called GameController, is a CocosNode so when were ready to start the game we can call
[self schedule: @selector(step:)];
to update the shapes.
This goes inside the implementation of your container.
- (void) step: (ccTime) delta { int steps = 1; cpFloat dt = delta/(cpFloat)steps; for(int i=0; iactiveShapes, &updateEachShapeCallback, self); cpSpaceHashEach(space->staticShapes, &updateEachShapeCallback, self); }
Add in the updateShape:(void *)ptr method to your GameController interface file and then implement as
- (void) updateShape:(void *)ptr { cpShape *shape = (cpShape*) ptr; Touchable *obj = shape->data; if(obj) { cpBody *body = shape->body; [obj setPosition: cpv( body->p.x, body->p.y)]; [obj setRotation: (float) CC_RADIANS_TO_DEGREES( -body->a )]; // do whatever else you want here! you have all the the state you should need! // just avoiding creating objects anywhere from here as that will slow your game down } }
I adopted this solution from instructions I found at Using native Objective-C methods for C Callbacks.
AdMob Ad to Landscape Orientation
Posted by Eric in cocos2d iPhone on April 13th, 2009
I checked around the net and found some people looking for this, but nobody who actually posted a code example. So here it is, code to make your Ad Mob ad shift to landscape orientation. My code makes it appear on the bottom, enlarged a little bit, with the device rotated to landscape counter-clockwise.
Add in a UIView called adContainer in your delegate interface declaration, and then update the standard Ad Mob methods as follows.
- (void)showAd { adMobAd = [AdMobView requestAdWithDelegate:self]; // start a new ad request [adMobAd retain]; // this will be released when it loads (or fails to load) adContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 432, 320, 48)]; }
#define degreesToRadians(x) (M_PI * x / 180.0) - (void)didReceiveAd:(AdMobView *)adView { [adContainer addSubview: adMobAd]; [window addSubview:adContainer]; CGAffineTransform makeLandscape = CGAffineTransformMakeRotation(degreesToRadians(90)); makeLandscape = CGAffineTransformTranslate(makeLandscape, -480/2 + 48/2, 320/2 - 48/2 - 12); makeLandscape = CGAffineTransformScale(makeLandscape, 480.0/320, 480.0/320); adContainer.transform = makeLandscape; autoslider = [NSTimer scheduledTimerWithTimeInterval:AD_REFRESH_PERIOD target:self selector:@selector(refreshAd:) userInfo:nil repeats:YES]; }
- (void)removeAd { [adMobAd removeFromSuperview]; [adMobAd release]; adMobAd = nil; [adContainer removeFromSuperview]; [adContainer release]; adContainer = nil; }
This was actually my first experience using the CG transforms, so please be gentle if there’s a better way
.
Recent Comments