Archive for 六月, 2011

cocos2d的虚拟摇杆

星期二, 六月 14th, 2011

https://github.com/sneakyness/SneakyInput

Cocos2D notes(4)

星期一, 六月 13th, 2011

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];

Axure的iPhone Widgets和触摸屏设备手势Widgets

星期一, 六月 13th, 2011

iPhone-Axure-Widgets
Touch_Gestures

Axure的iPad Widgets

星期一, 六月 13th, 2011

http://www.kreativr.de/ipad-axure-widget-library/

axure_ipad_widget_1.01

Cocos2d notes(3)

星期日, 六月 12th, 2011

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];
}

Cocos2D notes(2)

星期日, 六月 12th, 2011

获取真实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 notes (1)

星期五, 六月 10th, 2011

决定还是把cocos2d研究一下,虽然不打算在ios上主攻游戏开发,但2d引擎还是很多地方会用到。

安装cocos2d

google code下载最新的稳定版本,下载完成后解压,用shell cd到解压路径,执行
./install-templates.sh -f -u

因为无论是书还是网上的教程,基本都用的是cocos2d 0.99和xcode3,所以我不知道是为什么,这样安装后不能在xcode4中看到工程模板,可以参见在xcode4中添加cocos2d的工程模板 的解决办法。

CCDirector

首先接触到的是CCDirector类,顾名思义是2d动画的导演类,负责

  1. 创建或更改场景(scenes)
  2. 设置cocos2d的configuration细节
  3. 获取视图(OpenGL, UIView, UIWindow)
  4. 暂停,继续或结束游戏
  5. 转换UIKit和OpenGL坐标

获取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

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];

CCAction

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

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.

CCLayer

一个场景(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);
}

CCSprite

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];

CCLabel

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);

CCMenu

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];

Actions

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);
}

How To – 在iphone模拟器中使用accelerometer

星期四, 六月 9th, 2011

万恶的苹果的iphone模拟器上竟然不支持用accelerometer,但拿到一本cocos2d的书的例子却净是用这个操作游戏的

google了一下,有位牛人做了一个模拟器,原理是把模拟器安装在ios设备上,通过udp发送accelerometer数据给xcode上的模拟器以达到在模拟器上debug的效果。
可以参考这个http://code.google.com/p/accelerometer-simulator/wiki/Home

mbp上自带svn还是挺强大的。。

在xcode4中添加cocos2d的工程模板

星期三, 六月 8th, 2011

http://www.cocos2d-iphone.org/archives/1412

用第一种方法可行

Tiled Map Editor

星期三, 六月 8th, 2011

http://www.mapeditor.org/

tiled-0.6.2-win32-setup