cocos2d的虚拟摇杆
星期二, 六月 14th, 2011https://github.com/sneakyness/SneakyInput
https://github.com/sneakyness/SneakyInput
CCSpriteBatchNode
1 2 3 4 5 6 7 | CCSpriteBatchNode* batch = [CCSpriteBatchNode batchNodeWithFile:@"bullet.png"]; [self addChild:batch]; for (int i = 0; i < 100; i++) { CCSprite* sprite = [CCSprite spriteWithFile:@”bullet.png”]; [batch addChild:bullet]; } |
child必须用同样材质的sprite,否则报错
如果继承CCSprite的类中这样使用会造成死循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | -(id) init { CCLOG(@"Ship init called ... this will repeat infinetely."); // WARNING: this will cause an infinite loop! // Reason: [super initWithFile:...] will call -(id) init which is overridden here ... repeat ad infinitum // Fix: rename this init method, for example initWithShipImage // Tip: never call anything but [super init] in your class' init method. If you call any initWith* method // then you should never do this from the -(id) init method. if ((self = [super initWithFile:@"ship.png"])) { [self scheduleUpdate]; } return self; } |
为CCSprite添加动画效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // load the ship's animation frames as textures and create a sprite frame NSMutableArray* frames = [NSMutableArray arrayWithCapacity:5]; for (int i = 0; i < 5; i++) { NSString* file = [NSString stringWithFormat:@"ship-anim%i.png", i]; CCTexture2D* texture = [[CCTextureCache sharedTextureCache] addImage:file]; CGSize texSize = texture.contentSize; CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height); CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:texture rect:texRect offset:CGPointZero]; [frames addObject:frame]; } // create an animation object from all the sprite animation frames CCAnimation* anim = [CCAnimation animationWithName:@"move" delay:0.08f frames:frames]; // add the animation to the sprite //[self addAnimation:anim]; // run the animation by using the CCAnimate action CCAnimate* animate = [CCAnimate actionWithAnimation:anim]; CCRepeatForever* repeat = [CCRepeatForever actionWithAction:animate]; [self runAction:repeat]; |
可以通过animation的名字获取对象
1 2 3 4 5 | CCAnimation* anim = [CCAnimation animationWithName:@"move" delay:1 frames:frames]; // Store the animation in the CCSprite node [mySprite addAnimation:anim]; // Sometime later: retrieve the move animation from the CCSprite node CCAnimation* moveAnim = [mySprite animationByName:@”move”]; |
animation help
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @implementation CCAnimation (Helper) // Creates an animation from single files +(CCAnimation*) animationWithFile:(NSString*)name frameCount:(int)frameCount delay:(float)delay { // Load the animation frames as textures and create the sprite frames NSMutableArray* frames = [NSMutableArray arrayWithCapacity:frameCount]; for (int i = 0; i < frameCount; i++) { // Assuming all animation files are named "nameX.png" NSString* file = [NSString stringWithFormat:@"%@%i.png", name, i]; CCTexture2D* texture = [[CCTextureCache sharedTextureCache] addImage:file]; // Assuming that image file animations always use the whole image CGSize texSize = texture.contentSize; CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height); CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:texture rect:texRect offset:CGPointZero]; [frames addObject:frame]; } // Return an animation object from all the sprite animation frames return [CCAnimation animationWithName:name delay:delay frames:frames]; } @end |
Texture Atlas
工具zwoptex(收费)
TexturePacker (免费)
TexturePacker-2.2.0-win32.zip
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | -(id) initWithShipImage { // Load the Texture Atlas sprite frames, this also loads the Texture with the same name. CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; [frameCache addSpriteFramesWithFile:@"ship-and-bullet.plist"]; // Loading the Ship's sprite using a sprite frame name (eg the filename) if ((self = [super initWithSpriteFrameName:@"ship.png"])) { // load the ship's animation frames as textures and create a sprite frame NSMutableArray* frames = [NSMutableArray arrayWithCapacity:5]; for (int i = 0; i < 5; i++) { NSString* file = [NSString stringWithFormat:@"ship-anim%i.png", i]; CCSpriteFrame* frame = [frameCache spriteFrameByName:file]; [frames addObject:frame]; } // create an animation object from all the sprite animation frames CCAnimation* anim = [CCAnimation animationWithName:@"move" delay:0.08f frames:frames]; // add the animation to the sprite (optional) //[self addAnimation:anim]; // run the animation by using the CCAnimate action CCAnimate* animate = [CCAnimate actionWithAnimation:anim]; CCRepeatForever* repeat = [CCRepeatForever actionWithAction:animate]; [self runAction:repeat]; [self scheduleUpdate]; } return self; } |
helper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // Creates an animation from sprite frames. +(CCAnimation*) animationWithFrame:(NSString*)frame frameCount:(int)frameCount delay:(float)delay { // load the ship's animation frames as textures and create a sprite frame NSMutableArray* frames = [NSMutableArray arrayWithCapacity:frameCount]; for (int i = 0; i < frameCount; i++) { NSString* file = [NSString stringWithFormat:@"%@%i.png", frame, i]; CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; CCSpriteFrame* frame = [frameCache spriteFrameByName:file]; [frames addObject:frame]; } // return an animation object from all the sprite animation frames return [CCAnimation animationWithName:frame delay:delay frames:frames]; } |
释放无用cache
1 2 | [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames]; [[CCTextureCache sharedTextureCache] removeUnusedTextures]; |
清除所有
1 2 | [CCSpriteFrameCache purgeSharedSpriteFrameCache]; [CCTextureCache purgeSharedTextureCache]; |
Scene显示或被替换的3个重要事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | -(void) onEnter { // Called right after a node’s init method is called. // If using a CCTransitionScene: called when the transition begins. [super onEnter]; } -(void) onEnterTransitionDidFinish { // Called right after onEnter. // If using a CCTransitionScene: called when the transition has ended. [super onEnterTransitionDidFinish]; } -(void) onExit { // Called right before node’s dealloc method is called. // If using a CCTransitionScene: called when the transition has ended. [super onExit]; } |
Scene替换时发生的事件顺序
1 2 3 4 5 6 7 | 2011-06-12 19:35:39.374 ScenesAndLayers[21353:207] =========================================== 2011-06-12 19:35:39.375 ScenesAndLayers[21353:207] scene: OtherScene 2011-06-12 19:35:39.375 ScenesAndLayers[21353:207] init: <OtherScene = 06465B40 | Tag = -1> 2011-06-12 19:35:44.743 ScenesAndLayers[21353:207] onEnter: <OtherScene = 06465B40 | Tag = -1> 2011-06-12 19:35:47.760 ScenesAndLayers[21353:207] onExit: <FirstScene = 0783AD30 | Tag = -1> 2011-06-12 19:35:47.761 ScenesAndLayers[21353:207] onEnterTransitionDidFinish: <OtherScene = 06465B40 | Tag = -1> 2011-06-12 19:35:47.761 ScenesAndLayers[21353:207] dealloc: <FirstScene = 0783AD30 | Tag = -1> |
由此可知,在init后才发生onEnter,如果init初始化数据较多,就会导致画面tingdun,所以要加入loading scene
一个范例方法
LoadScene.h
1 2 3 4 5 6 7 8 9 10 11 12 13 | typedef enum { TargetSceneINVALID = 0, TargetSceneFirstScene, TargetSceneOtherScene, TargetSceneMAX, } TargetScenes; // LoadingScene is derived directly from Scene. We don't need a CCLayer for this scene. @interface LoadingScene : CCScene { TargetScenes targetScene_; } |
LoadScene.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | +(id) sceneWithTargetScene:(TargetScenes)targetScene; { // This creates an autorelease object of the current class (self == LoadingScene) return [[[self alloc] initWithTargetScene:targetScene] autorelease]; } -(id) initWithTargetScene:(TargetScenes)targetScene { if ((self = [super init])) { targetScene_ = targetScene; CCLabel* label = [CCLabel labelWithString:@"Loading ..." fontName:@"Marker Felt" fontSize:64]; CGSize size = [[CCDirector sharedDirector] winSize]; label.position = CGPointMake(size.width / 2, size.height / 2); [self addChild:label]; // Must wait one frame before loading the target scene! [self scheduleUpdate]; } return self; } -(void) update:(ccTime)delta { [self unscheduleAllSelectors]; // Decide which scene to load based on the TargetScenes enum. switch (targetScene_) { case TargetSceneFirstScene: [[CCDirector sharedDirector] replaceScene:[FirstScene scene]]; break; case TargetSceneOtherScene: [[CCDirector sharedDirector] replaceScene:[OtherScene scene]]; break; default: // Always warn if an unspecified enum value was used NSAssert2(nil, @"%@: unsupported TargetScene %i", NSStringFromSelector(_cmd), targetScene_); break; } } |
为什么不能在LoadScene 的init方法中直接调用replaceScene
原因1:永远不要在node的init中调用CCDirector的replaceScene方法
原因2:这样调用会让app崩溃,因为无法在初始化(init)一个node的同时replace掉它
Singleton
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | +(MultiLayerScene*) sharedLayer { NSAssert(multiLayerSceneInstance != nil, @"MultiLayerScene not available!"); return multiLayerSceneInstance; } -(void) dealloc { // MultiLayerScene will be deallocated now, you must set it to nil multiLayerSceneInstance = nil; // don't forget to call "super dealloc" [super dealloc]; } |
当一个Scene上有两个层重叠,如何判断哪个层接收touch事件
假设UserInterfaceLayer* uiLayer 在GameLayer* gameLayer之上
UserInterfacelayer.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | -(void) registerWithTouchDispatcher { [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES]; } // Implements logic to check if the touch location was in an area that this layer wants to handle as input. -(bool) isTouchForMe:(CGPoint)touchLocation { CCNode* node = [self getChildByTag:UILayerTagFrameSprite]; return CGRectContainsPoint([node boundingBox], touchLocation); } -(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent *)event { CGPoint location = [MultiLayerScene locationFromTouch:touch]; bool isTouchHandled = [self isTouchForMe:location]; if (isTouchHandled) { // Simply highlight the UI layer's sprite to show that it received the touch. CCNode* node = [self getChildByTag:UILayerTagFrameSprite]; NSAssert([node isKindOfClass:[CCSprite class]], @"node is not a CCSprite"); ((CCSprite*)node).color = ccRED; // 省略 } return isTouchHandled; } -(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent *)event { CCNode* node = [self getChildByTag:UILayerTagFrameSprite]; NSAssert([node isKindOfClass:[CCSprite class]], @"node is not a CCSprite"); ((CCSprite*)node).color = ccWHITE; } |
在registerWithTouchDispatcher中将自己获取touch的优先级设为最高,然后在-(BOOL) ccTouchBegan:withEvent:中如果返回YES,这次的touch将被uiLayer吞掉(swallow),如果返回NO,则由gameLayer继续处理。
使用CCMultiplexLayer在不同Layer间切换
1 2 3 4 5 6 7 8 | CCLayer* layer1 = [CCLayer node]; CCLayer* layer2 = [CCLayer node]; CCMultiplexLayer* mpLayer = [CCMultiplexLayer layerWithLayers:layer1, layer2, nil]; // Switches to layer2 but keeps layer1 as child of mpLayer. [mpLayer switchTo:1]; // Switches to layer1, removes layer2 from mpLayer and releases its memory. // After this call you must not switch back to layer2 (index: 1) anymore! [mpLayer switchToAndReleaseMe:0]; |
CCColorLayer
1 2 3 | // Set background color to magenta. The most unobtrusive color imaginable. CCColorLayer* colorLayer = [CCColorLayer layerWithColor:ccc4(255, 0, 255, 255)]; [self addChild:colorLayer z:0]; |
Sprite Class
Spider.h
1 2 3 4 5 6 7 8 9 | @interface Spider : NSObject { CCSprite* spiderSprite; } +(id) spiderWithParentNode:(CCNode*)parentNode; -(id) initWithParentNode:(CCNode*)parentNode; @end |
Spider.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #import "Spider.h" @implementation Spider // Static autorelease initializer, mimics cocos2d's memory allocation scheme. +(id) spiderWithParentNode:(CCNode*)parentNode { return [[[self alloc] initWithParentNode:parentNode] autorelease]; } -(id) initWithParentNode:(CCNode*)parentNode { if ((self = [super init])) { CGSize screenSize = [[CCDirector sharedDirector] winSize]; spiderSprite = [CCSprite spriteWithFile:@"spider.png"]; spiderSprite.position = CGPointMake(CCRANDOM_0_1() * screenSize.width, CCRANDOM_0_1() * screenSize.height); [parentNode addChild:spiderSprite]; // Manually schedule update via the undocumented CCScheduler class. [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO]; } return self; } -(void) dealloc { // Must manually unschedule, it is not done automatically. [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; [super dealloc]; } |
通过注册CCScheduler的 scheduleUpdateForTarget:priority:方法
使Spider每帧调用update:方法
让Spider类实现CCTargetedTouchDelegate协议,使Spider类接收touch
@interface Spider : NSObject
{
…
}
修改后的Spider.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | -(id) initWithParentNode:(CCNode*)parentNode { if ((self = [super init])) { … // Manually schedule update via the undocumented CCScheduler class. [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO]; // Manually add this class as receiver of targeted touch events. [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES]; } return self; } -(void) dealloc { // Must manually unschedule, it is not done automatically! [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; // Must manually remove this class as touch input receiver! [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; [super dealloc]; } // Extract common logic into a separate method accepting parameters. -(void) moveAway:(float)duration position:(CGPoint)moveTo { [spiderSprite stopAllActions]; CCMoveBy* move = [CCMoveBy actionWithDuration:duration position:moveTo]; [spiderSprite runAction:move]; } -(void) update:(ccTime)delta { numUpdates++; if (numUpdates > 50) { numUpdates = 0; // Move at regular speed. CGPoint moveTo = CGPointMake(CCRANDOM_0_1() * 200 - 100, CCRANDOM_0_1() * 100 - 50); [self moveAway:2 position:moveTo]; } } -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { // Check if this touch is on the Spider's sprite. CGPoint touchLocation = [MultiLayerScene locationFromTouch:touch]; BOOL isTouchHandled = CGRectContainsPoint([spiderSprite boundingBox], touchLocation); if (isTouchHandled) { // Reset move counter. numUpdates = 0; // Move away from touch loation rapidly. CGPoint moveTo; float moveDistance = 60; float rand = CCRANDOM_0_1(); // Randomly pick one of four corners to move away to. if (rand < 0.25f) moveTo = CGPointMake(moveDistance, moveDistance); else if (rand < 0.5f) moveTo = CGPointMake(-moveDistance, moveDistance); else if (rand < 0.75f) moveTo = CGPointMake(moveDistance, -moveDistance); else moveTo = CGPointMake(-moveDistance, -moveDistance); // Move quickly: [self moveAway:0.1f position:moveTo]; } return isTouchHandled; } |
CCProgressTimer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Progress timer is a sprite only partially displayed to visualize some kind of progress. CCProgressTimer* timer = [CCProgressTimer progressWithFile:@"firething.png"]; timer.type = kCCProgressTimerTypeRadialCCW; timer.percentage = 0; [self addChild:timer z:1 tag:UILayerTagProgressTimer]; // The update is needed for the progress timer. [self scheduleUpdate]; -(void) update:(ccTime)delta { CCNode* node = [self getChildByTag:UILayerTagProgressTimer]; NSAssert([node isKindOfClass:[CCProgressTimer class]], @"node is not a CCProgressTimer"); // Updates the progress timer CCProgressTimer* timer = (CCProgressTimer*)node; timer.percentage += delta * 10; if (timer.percentage >= 100) { timer.percentage = 0; } } |
CCParallaxNode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // Load the sprites for each parallax layer, from background to foreground. CCSprite* para1 = [CCSprite spriteWithFile:@"parallax1.png"]; CCSprite* para2 = [CCSprite spriteWithFile:@"parallax2.png"]; CCSprite* para3 = [CCSprite spriteWithFile:@"parallax3.png"]; CCSprite* para4 = [CCSprite spriteWithFile:@"parallax4.png"]; // Set the correct offsets depending on the screen and image sizes. para1.anchorPoint = CGPointMake(0, 1); para2.anchorPoint = CGPointMake(0, 1); para3.anchorPoint = CGPointMake(0, 0.6f); para4.anchorPoint = CGPointMake(0, 0); CGPoint topOffset = CGPointMake(0, screenSize.height); CGPoint midOffset = CGPointMake(0, screenSize.height / 2); CGPoint downOffset = CGPointZero; // Create a parallax node and add the sprites to it. CCParallaxNode* paraNode = [CCParallaxNode node]; [paraNode addChild:para1 z:1 parallaxRatio:CGPointMake(0.5f, 0) positionOffset:topOffset]; [paraNode addChild:para2 z:2 parallaxRatio:CGPointMake(1, 0) positionOffset:topOffset]; [paraNode addChild:para3 z:4 parallaxRatio:CGPointMake(2, 0) positionOffset:midOffset]; [paraNode addChild:para4 z:3 parallaxRatio:CGPointMake(3, 0) positionOffset:downOffset]; [self addChild:paraNode z:0 tag:ParallaxSceneTagParallaxNode]; // Move the parallax node to show the parallaxing effect. CCMoveBy* move1 = [CCMoveBy actionWithDuration:5 position:CGPointMake(-160, 0)]; CCMoveBy* move2 = [CCMoveBy actionWithDuration:15 position:CGPointMake(160, 0)]; CCSequence* sequence = [CCSequence actions:move1, move2, nil]; CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence]; [paraNode runAction:repeat]; |
CCRibbon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | -(void) resetRibbon { // Removes the ribbon and creates a new one. [self removeChildByTag:ParallaxSceneTagRibbon cleanup:YES]; CCRibbon* ribbon = [CCRibbon ribbonWithWidth:32 image:@"spider.png" length:32 color:ccc4(255, 255, 255, 255) fade:0.5f]; [self addChild:ribbon z:5 tag:ParallaxSceneTagRibbon]; } -(CCRibbon*) getRibbon { CCNode* node = [self getChildByTag:ParallaxSceneTagRibbon]; NSAssert([node isKindOfClass:[CCRibbon class]], @"node is not a CCRibbon"); return (CCRibbon*)node; } -(void) addRibbonPoint:(CGPoint)point { CCRibbon* ribbon = [self getRibbon]; [ribbon addPointAt:point width:32]; } -(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent *)event { [self addRibbonPoint:[MultiLayerScene locationFromTouch:touch]]; return YES; } -(void) ccTouchMoved:(UITouch*)touch withEvent:(UIEvent *)event { [self addRibbonPoint:[MultiLayerScene locationFromTouch:touch]]; } -(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent *)event { [self resetRibbon]; } |
CCMotionStreak
用来替换CCRibbon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -(void) resetMotionStreak { // Removes the CCMotionStreak and creates a new one. [self removeChildByTag:ParallaxSceneTagRibbon cleanup:YES]; CCMotionStreak* streak = [CCMotionStreak streakWithFade:0.7f minSeg:10 image:@"spider.png" width:32 length:32 color:ccc4(255, 0, 255, 255)]; [self addChild:streak z:5 tag:ParallaxSceneTagRibbon]; } -(void) addMotionStreakPoint:(CGPoint)point { CCMotionStreak* streak = [self getMotionStreak]; [streak.ribbon addPointAt:point width:32]; } |
获取真实texture大小
[player texture].contentSize.height
Odd??
1 2 3 4 5 6 7 8 9 | -(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { // ERROR: lvalue required as left operand of assignment // player.position.x += acceleration.x * 10; CGPoint pos = player.position; pos.x += acceleration.x * 10; player.position = pos; } |
判断Sprite当前是否有执行Action
[spider numberOfRunningActions] == 0
简单的测试碰撞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | -(void) checkForCollision { // Assumption: both player and spider images are squares. float playerImageSize = [player texture].contentSize.width; float spiderImageSize = [[spiders lastObject] texture].contentSize.width; float playerCollisionRadius = playerImageSize * 0.4f; float spiderCollisionRadius = spiderImageSize * 0.4f; // This collision distance will roughly equal the image shapes. float maxCollisionDistance = playerCollisionRadius + spiderCollisionRadius; int numSpiders = [spiders count]; for (int i = 0; i < numSpiders; i++) { CCSprite* spider = [spiders objectAtIndex:i]; if ([spider numberOfRunningActions] == 0) { // This spider isn't even moving so we can skip checking it. continue; } // Get the distance between player and spider. float actualDistance = ccpDistance(player.position, spider.position); // Are the two objects closer than allowed? if (actualDistance < maxCollisionDistance) { // No game over, just reset the spiders. [self resetSpiders]; } } } |
显示分数
1 2 3 4 5 6 7 8 | // Update the Score (Timer) once per second. totalTime += delta; int currentTime = (int)totalTime; if (score < currentTime) { score = currentTime; [scoreLabel setString:[NSString stringWithFormat:@"%i", score]]; } |
这个例子里使用度过时间的总和作为分数
如果用CCLabel每帧都改变其内容将会严重影响app运行速度
所以这里可以使用CCBitmapFontAtlas作为替代,只需要将创建CCLable的那一行修改
1 2 | scoreLabel = [CCBitmapFontAtlas bitmapFontAtlasWithString:@"0" fntFile:@"bitmapfont.fnt"]; |
要生成fnt文件,用这个java app: http://slick.cokeandcode.com/demos/hiero.jnlp
播放音乐音效
1 2 3 4 5 | #import "SimpleAudioEngine.h" [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"blues.mp3" loop:YES]; [[SimpleAudioEngine sharedEngine] playEffect:@"alien-sfx.caf"]; // preload [[SimpleAudioEngine sharedEngine] preloadEffect:@"alien-sfx.caf"]; |
注意背景音乐(mp3)同一时间只能播放一个,虽然也有情况可以同时播多个mp3,但是cpu在解码mp3的时候是逐个处理的
所以音效最好使用caf格式
转换成caf格式的文件可能需要工具,这个坑爹的软件卖$15,应该有其他的免费替代品吧。。
http://dekorte.com/projects/shareware/SoundConverter/
遍历,停止所有action
1 2 3 4 | CCARRAY_FOREACH([self children], node) { [node stopAllActions]; } |
决定还是把cocos2d研究一下,虽然不打算在ios上主攻游戏开发,但2d引擎还是很多地方会用到。
到google code下载最新的稳定版本,下载完成后解压,用shell cd到解压路径,执行
./install-templates.sh -f -u
因为无论是书还是网上的教程,基本都用的是cocos2d 0.99和xcode3,所以我不知道是为什么,这样安装后不能在xcode4中看到工程模板,可以参见在xcode4中添加cocos2d的工程模板 的解决办法。
首先接触到的是CCDirector类,顾名思义是2d动画的导演类,负责
获取CCDirector对象
1 | CCDirector *director = [CCDirector sharedDirector]; |
设置游戏的设备方向
1 2 3 4 5 | #if GAME_AUTOROTATION == kGameAutorotationUIViewController [director setDeviceOrientation:kCCDeviceOrientationPortrait]; #else [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft]; #endif |
设置动画间隔
1 | [director setAnimationInterval:1.0/60]; |
是否显示FPS数据
1 | [director setDisplayFPS:YES]; |
启动场景
1 | [[CCDirector sharedDirector] runWithScene: [HelloWorldScene node]]; |
获得所有可视区域的Size
1 | CGSize winSize = [[CCDirector sharedDirector] winSize]; |
CCNode是所有节点的父类包括(Layer, Sprite, Scene)
创建Node
1 | CCNode* childNode = [CCNode node]; |
添加Node
1 | [myNode addChild:childNode z:0 tag:123]; |
获取Node
1 | CCNode* retrievedNode = [myNode getChildByTag:123]; |
删除Node(Tag方式)
1 | [myNode removeChildByTag:123 cleanup:YES]; |
删除Node(指针方式)
1 | [myNode removeChild:retrievedNode]; |
全部删除
1 | [myNode removeAllChildrenWithCleanup:YES]; |
把自己从父节点删除
1 | [myNode removeFromParentAndCleanup:YES]; |
Node可以执行Action
声明并运行action
1 2 3 | CCAction* action = [CCBlink actionWithDuration:10 blinks:20]; action.tag = 234; [myNode runAction:action]; |
获取action
1 | CCAction* retrievedAction = [myNode getActionByTag:234]; |
停止action
1 2 3 4 | // by tag [myNode stopActionByTag:234]; // by pointer [myNode stopAction:action]; |
停止所有action
1 | [myNode stopAllActions]; |
scheduleUpdate方法会执行update:方法,每帧执行一次
或者指定定时方法
1 2 3 4 5 6 7 8 | -(void) scheduleUpdates { [self schedule:@selector(updateTenTimesPerSecond:) interval:0.1f]; } -(void) updateTenTimesPerSecond:(ccTime)delta { // this method is called according to its interval, ten times per second } |
停止所有定时方法
1 | [self unscheduleAllSelectors]; |
停止指定定时方法
1 | [self unschedule:@selector(updateTenTimesPerSecond:)]; |
trick — 用_cmd关键字代替当前执行的方法,也就是current method
1 2 3 4 5 6 7 8 9 | -(void) scheduleUpdates { [self schedule:@selector(tenMinutesElapsed:) interval:600]; } -(void) tenMinutesElapsed:(ccTime)delta { // unschedule the current method by using the _cmd keyword [self unschedule:_cmd]; } |
优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // in Node A -(void) scheduleUpdates { [self scheduleUpdate]; } // in Node B -(void) scheduleUpdates { [self scheduleUpdateWithPriority:1]; } // in Node C -(void) scheduleUpdates { [self scheduleUpdateWithPriority:-1]; } |
ABC3个节点的优先级为C>A>B
scene永远是一个场景上的根节点
1 2 3 4 5 6 7 | +(id) scene { CCScene *scene = [CCScene node]; CCLayer* layer = [HelloWorld node]; [scene addChild:layer]; return scene; } |
1 2 3 4 | // only use this to run the very first scene [[CCDirector sharedDirector] runWithScene:[HelloWorld scene]]; // use replaceScene to change all subsequent scenes [[CCDirector sharedDirector] replaceScene:[HelloWorld scene]]; |
1 2 | [[CCDirector sharedDirector] pushScene:[Settings scene]]; [[CCDirector sharedDirector] popScene]; |
replaceScene和pushScene可以使用转换效果(popScene无法使用效果)
1 2 3 4 5 6 | // initialize a transition scene with the scene we’d like to display next CCFadeTransition* tran = [CCFadeTransition transitionWithDuration:1 scene:[HelloWorld scene] withColor:ccWHITE]; // use the transition scene object instead of HelloWorld [[CCDirector sharedDirector] replaceScene:tran]; |
有以下效果
CCFadeTransition: Fade to a specific color and back.
CCFadeTRTransition (three more variations): Tiles flip over to reveal
new scene.
CCJumpZoomTransition: Scene bounces and gets smaller, new scene
does the reverse.
CCMoveInLTransition (three more variations): Scene moves out, new
scene moves in at the same time, either from left, right, top or bottom.
CCOrientedTransitionScene (six more variations): A variety of
transitions flipping the whole scene around
CCPageTurnTransition: An effect like turning a page.
CCRotoZoomTransition: Scene rotates and gets smaller, new scene
does reverse.
CCShrinkGrowTransition: Current scene shrinks, new scene grows
over it.
CCSlideInLTransition (three more variations): New scene slides over
the current scene, either from left, right, top or bottom.
CCSplitColsTransition (one variation): Columns of scene move up or
down to reveal new scene.
CCTurnOffTilesTransition: Tiles randomly replaced by tiles of new scene.
一个场景(Scene)上可能有多个Layer
1 2 3 4 5 6 7 8 9 10 11 | +(id) scene { CCScene* scene = [CCScene node]; CCLayer* backgroundLayer = [HelloWorldBackground node]; [scene addChild: backgroundLayer]; CCLayer* layer = [HelloWorld node]; [scene addChild:layer]; CCLayer* userInterfaceLayer = [HelloWorldUserInterface node]; [scene addChild: userInterfaceLayer]; return scene; } |
* 和Scene一样,Layer没有宽高,只是一群可视对象的概念集合
获取Touch 输入
1 | self.isTouchEnabled = YES; |
用户Touch动作跟以下方法相关
Called when a finger just begins touching the screen:
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Called whenever the finger moves on the screen:
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
Called when a finger is lifted off the screen:
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Called to cancel a touch:
-(void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
获取Touch坐标
1 2 3 | UITouch *touch = [touches anyObject]; CGPoint touchLocation = [touch locationInView: [touch view]]; return [[CCDirector sharedDirector] convertToGL:touchLocation]; |
如果要指定层消耗触摸
1 2 3 4 5 | -(void) registerWithTouchDispatcher { [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES]; } |
如果此方法内容什么都不写,则不会接收任何触摸事件
这种情况下要用以下方法获取触摸事件
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {}
-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {}
-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event {}
和上面的方法略有不同,并且,注意ccTouchBegan:withEvent:返回是BOOL类型,如果返回YES,则触摸事件不会冒泡到优先级较低的Layer
接收accelerometer事件
1 2 3 4 5 | didAccelerate:(UIAcceleration *)acceleration { CCLOG(@"acceleration: x:%f / y:%f / z:%f", acceleration.x, acceleration.y, acceleration.z); } |
1 2 | CCSprite* sprite = [CCSprite spriteWithFile:@”Default.png”]; [self addChild:sprite]; |
锚点
1 2 3 | CCSprite* sprite = [CCSprite spriteWithFile:@”Default.png”]; sprite.anchorPoint = CGPointMake(0, 0); [self addChild:sprite]; |
1 2 | CCLabel* label = [CCLabel labelWithString:@"text" fontName:@"AppleGothic" fontSize:32]; [self addChild:label]; |
对齐
1 2 3 4 5 6 7 8 9 10 11 12 13 | // align label to the right label.anchorPoint = CGPointMake(1, 0.5f); // align label to the left label.anchorPoint = CGPointMake(0, 0.5f); // align label to the top label.anchorPoint = CGPointMake(0.5f, 1); // align label to the bottom label.anchorPoint = CGPointMake(0.5f, 0); // use case: place label at top-right corner of the screen // the label’s text extends to the left and down and is always completely on screen CGSize size = [[CCDirector sharedDirector] winSize]; label.position = CGPointMake(size.width, size.height); label.anchorPoint = CGPointMake(1, 1); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | CGSize size = [[CCDirector sharedDirector] winSize]; // set CCMenuItemFont default properties [CCMenuItemFont setFontName:@"Helvetica-BoldOblique"]; [CCMenuItemFont setFontSize:26]; // create a few labels with text and selector CCMenuItemFont* item1 = [CCMenuItemFont itemFromString:@"Go Back!" target:self selector:@selector(menuItem1Touched:)]; // create a menu item using existing sprites CCSprite* normal = [CCSprite spriteWithFile:@"Icon.png"]; normal.color = ccRED; CCSprite* selected = [CCSprite spriteWithFile:@"Icon.png"]; selected.color = ccGREEN; CCMenuItemSprite* item2 = [CCMenuItemSprite itemFromNormalSprite:normal selectedSprite:selected target:self selector:@selector(menuItem2Touched:)]; // create a toggle item using two other menu items (toggle works with images, too) [CCMenuItemFont setFontName:@"STHeitiJ-Light"]; [CCMenuItemFont setFontSize:18]; CCMenuItemFont* toggleOn = [CCMenuItemFont itemFromString:@"I'm ON!"]; CCMenuItemFont* toggleOff = [CCMenuItemFont itemFromString:@"I'm OFF!"]; CCMenuItemToggle* item3 = [CCMenuItemToggle itemWithTarget:self selector:@selector(menuItem3Touched:) items:toggleOn, toggleOff, nil]; // create the menu using the items CCMenu* menu = [CCMenu menuWithItems:item1, item2, item3, nil]; menu.position = CGPointMake(size.width / 2, size.height / 2); [self addChild:menu]; // aligning is important, so the menu items don’t occupy the same location [menu alignItemsVerticallyWithPadding:40]; |
1 2 3 | // I want myNode to move to 100, 200 and arrive there in 3 seconds CCMoveTo* move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100, 200)]; [myNode runAction:move]; |
Repeating Actions
1 2 3 | CCRotateBy* rotateBy = [CCRotateBy actionWithDuration:2 angle:360]; CCRepeatForever* repeat = [CCRepeatForever actionWithAction:rotateBy]; [myNode runAction:repeat]; |
Ease Actions(缓动)
1 2 3 4 5 | // I want myNode to move to 100, 200 and arrive there in 3 seconds CCMoveTo* move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100, 200)]; // this time the node should slowly speed up and then slow down as it moves CCEaseInOut* ease = [CCEaseInOut actionWithAction:move rate:4]; [myNode runAction:ease]; |
有以下缓动方式
CCEaseBackIn, CCEaseBackInOut, CCEaseBackOut
CCEaseBounceIn, CCEaseBounceInOut, CCEaseBounceOut
CCEaseElasticIn, CCEaseElasticInOut, CCEaseElasticOut
CCEaseExponentialIn, CCEaseExponentialInOut,
CCEaseExponentialOut
CCEaseIn, CCEaseInOut, CCEaseOut
CCEaseSineIn, CCEaseSineInOut, CCEaseSineOut
Action Sequences(序列)
1 2 3 4 5 | CCTintTo* tint1 = [CCTintTo actionWithDuration:4 red:255 green:0 blue:0]; CCTintTo* tint2 = [CCTintTo actionWithDuration:4 red:0 green:0 blue:255]; CCTintTo* tint3 = [CCTintTo actionWithDuration:4 red:0 green:255 blue:0]; CCSequence* sequence = [CCSequence actions:tint1, tint2, tint3, nil]; [label runAction:sequence]; |
或
1 2 3 | CCSequence* sequence = [CCSequence actions:tint1, tint2, tint3, nil]; CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence]; [label runAction:repeat]; |
Instant Actions(即时)
有3种形式
1 2 3 4 5 6 7 8 | CCCallFunc* func = [CCCallFunc actionWithTarget:self selector:@selector(onCallFunc)]; CCCallFuncN* funcN = [CCCallFuncN actionWithTarget:self selector:@selector(onCallFuncN:)]; CCCallFuncND* funcND = [CCCallFuncND actionWithTarget:self selector:@selector(onCallFuncND:data:) data:(void*)self]; CCSequence* seq = [CCSequence actions:tint1, func, tint2, funcN, tint3, funcND, nil]; [label runAction:seq]; |
对应以下3个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | -(void) onCallFunc { CCLOG(@"end of tint1!"); } -(void) onCallFuncN:(id)sender { CCLOG(@"end of tint2! sender: %@", sender); } -(void) onCallFuncND:(id)sender data:(void*)data { // be careful when casting pointers like this! // you have to be 100% sure the object is of this type! CCSprite* sprite = (CCSprite*)data; CCLOG(@"end of sequence! sender: %@ - data: %@", sender, sprite); } |
万恶的苹果的iphone模拟器上竟然不支持用accelerometer,但拿到一本cocos2d的书的例子却净是用这个操作游戏的
google了一下,有位牛人做了一个模拟器,原理是把模拟器安装在ios设备上,通过udp发送accelerometer数据给xcode上的模拟器以达到在模拟器上debug的效果。
可以参考这个http://code.google.com/p/accelerometer-simulator/wiki/Home
mbp上自带svn还是挺强大的。。