Archive for the '技术' Category

ios图表

星期四, 六月 30th, 2011

Core Plot
教程1
教程2

Grand Central Dispatch (GCD) Reference

星期四, 六月 30th, 2011

Grand Central Dispatch (GCD) Reference

Blocks Programming Topics

星期四, 六月 30th, 2011

read

Cocos2d notes(7) physics engines

星期五, 六月 17th, 2011

Chipmunk SpaceManager
Chipmunk SpaceManager Documentation
Chipmunk Physics
Chipmunk Documentation
Box2D Documents
Box2D API Reference

Box2D

header定义,几乎没变化,只是增加了Box2D的头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"

enum
{
    kTagBatchNode,
};

@interface HelloWorld : CCLayer
{
    b2World* world;
}

// returns a Scene that contains the HelloWorld as the only child
+(id) scene;

@end

初始化Box2D世界
b2Vec2 gravity = b2Vec2(0.0f, -10.0f);
bool allowBodiesToSleep = true;
world = new b2World(gravity, allowBodiesToSleep);

销毁时

1
2
3
4
5
-(void) dealloc
{
    delete world;
    [super dealloc];
}

关于什么是Sleeping body

It’s a trick that allows the physics simulation to quickly skip over
objects that do not need processing. A dynamic body goes to sleep when the forces
applied to it have been below a threshold for a certain amount of time. In other words, if
268 CHAPTER 12: Physics Engines
the dynamic body is moving and rotating very slowly or not at all, the physics engine will
flag it as sleeping and won’t apply forces to it anymore—that is, unless an impulse or force
applied to the body is strong enough to make the body move or rotate again. This trick
allows the physics engine to save time by not processing the bodies that are at rest.
Unless all of your game’s dynamic bodies are in constant motion, you should enable this
feature by setting the allowBodiesToSleep variable to true,

限制对象运动的屏幕
(Box2D的单位全是公制单位,即长度用米,重量用千克,时间用秒)

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
// Define the static container body, which will provide the collisions at screen borders.
b2BodyDef containerBodyDef;
b2Body* containerBody = world->CreateBody(&containerBodyDef);

// for the ground body we'll need these values
// PTM pixel转换成公制长度的产量
CGSize screenSize = [CCDirector sharedDirector].winSize;
float widthInMeters = screenSize.width / PTM_RATIO;
float heightInMeters = screenSize.height / PTM_RATIO;
b2Vec2 lowerLeftCorner = b2Vec2(0, 0);
b2Vec2 lowerRightCorner = b2Vec2(widthInMeters, 0);
b2Vec2 upperLeftCorner = b2Vec2(0, heightInMeters);
b2Vec2 upperRightCorner = b2Vec2(widthInMeters, heightInMeters);

// Create the screen box' sides by using a polygon assigning each side individually.
b2PolygonShape screenBoxShape;
int density = 0;

// bottom
screenBoxShape.SetAsEdge(lowerLeftCorner, lowerRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);

// top
screenBoxShape.SetAsEdge(upperLeftCorner, upperRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);

// left side
screenBoxShape.SetAsEdge(upperLeftCorner, lowerLeftCorner);
containerBody->CreateFixture(&screenBoxShape, density);

// right side
screenBoxShape.SetAsEdge(upperRightCorner, lowerRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);

#define PTM_RATIO 32
代表32像素代表Box2D中的1米

转换

1
2
3
4
5
6
7
8
-(b2Vec2) toMeters:(CGPoint)point
{
    return b2Vec2(point.x / PTM_RATIO, point.y / PTM_RATIO);
}
-(CGPoint) toPixels:(b2Vec2)vec
{
    return ccpMult(CGPointMake(vec.x, vec.y), PTM_RATIO);
}

创建Box2D Sprite

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
// Use the orthogonal tileset for the little boxes
CCSpriteBatchNode* batch = [CCSpriteBatchNode batchNodeWithFile:@"dg_grounds32.png" capacity:TILESET_ROWS * TILESET_COLUMNS];
[self addChild:batch z:0 tag:kTagBatchNode];

// Add a few objects initially
for (int i = 0; i < 11; i++)
{
    [self addNewSpriteAt:CGPointMake(screenSize.width / 2, screenSize.height / 2)];
}

[self scheduleUpdate];

self.isTouchEnabled = YES;

-(void) addNewSpriteAt:(CGPoint)pos
{
    CCSpriteBatchNode* batch = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];
   
    int idx = CCRANDOM_0_1() * TILESET_COLUMNS;
    int idy = CCRANDOM_0_1() * TILESET_ROWS;
    CGRect tileRect = CGRectMake(TILESIZE * idx, TILESIZE * idy, TILESIZE, TILESIZE);
    CCSprite* sprite = [CCSprite spriteWithBatchNode:batch rect:tileRect];
    sprite.position = pos;
    [batch addChild:sprite];
   
    // Create a body definition and set it to be a dynamic body
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;

    // position must be converted to meters
    bodyDef.position = [self toMeters:pos];
   
    // assign the sprite as userdata so it's easy to get to the sprite when working with the body
    bodyDef.userData = sprite;
    b2Body* body = world->CreateBody(&bodyDef);
   
    // Define another box shape for our dynamic body.
    b2PolygonShape dynamicBox;
    float tileInMeters = TILESIZE / PTM_RATIO;
    dynamicBox.SetAsBox(tileInMeters * 0.5f, tileInMeters * 0.5f);
   
    // Define the dynamic body fixture.
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 0.3f;
    fixtureDef.friction = 0.5f;
    fixtureDef.restitution = 0.6f;
    body->CreateFixture(&fixtureDef);
}

让sprite动起来
事实上,通过以上代码还不能让每个sprite运动起来,还是需要通过sheduleUpdate触发每次计算world里存在的各个body,计算一定timeStep时间内他们的运动情况,然后给sprite设置属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-(void) update:(ccTime)delta
{
    // The number of iterations influence the accuracy of the physics simulation. With higher values the
    // body's velocity and position are more accurately tracked but at the cost of speed.
    // Usually for games only 1 position iteration is necessary to achieve good results.
    float timeStep = 0.03f;
    int32 velocityIterations = 8;
    int32 positionIterations = 1;
    world->Step(timeStep, velocityIterations, positionIterations);
   
    // for each body, get its assigned sprite and update the sprite's position
    for (b2Body* body = world->GetBodyList(); body != nil; body = body->GetNext())
    {
        CCSprite* sprite = (CCSprite*)body->GetUserData();
        if (sprite != NULL)
        {
            // update the sprite's position to where their physics bodies are
            sprite.position = [self toPixels:body->GetPosition()];
            float angle = body->GetAngle();
            sprite.rotation = CC_RADIANS_TO_DEGREES(angle) * -1;
        }  
    }
}

碰撞检测
* 如果把.m文件后缀改成.mm则可以在其中同时使用objc和c++语法,box2d是c++写的,所以。。

b2ContactListener类支持两个事件,注意一下都是c++代码了
ContactListener.h

1
2
3
4
5
6
7
8
#import "Box2D.h"

class ContactListener : public b2ContactListener
{
private:
    void BeginContact(b2Contact* contact);
    void EndContact(b2Contact* contact);
};

.mm

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
#import "ContactListener.h"
#import "cocos2d.h"

void ContactListener::BeginContact(b2Contact* contact)
{
    b2Body* bodyA = contact->GetFixtureA()->GetBody();
    b2Body* bodyB = contact->GetFixtureB()->GetBody();
    CCSprite* spriteA = (CCSprite*)bodyA->GetUserData();
    CCSprite* spriteB = (CCSprite*)bodyB->GetUserData();
   
    if (spriteA != NULL && spriteB != NULL)
    {
        spriteA.color = ccMAGENTA;
        spriteB.color = ccMAGENTA;
    }
}


void ContactListener::EndContact(b2Contact* contact)
{
    b2Body* bodyA = contact->GetFixtureA()->GetBody();
    b2Body* bodyB = contact->GetFixtureB()->GetBody();
    CCSprite* spriteA = (CCSprite*)bodyA->GetUserData();
    CCSprite* spriteB = (CCSprite*)bodyB->GetUserData();
   
    if (spriteA != NULL && spriteB != NULL)
    {
        spriteA.color = ccWHITE;
        spriteB.color = ccWHITE;
    }
}

在layer中加入listener

1
2
contactListener = new ContactListener();
world->SetContactListener(contactListener);

Joint

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
// Create a body definition and set it to be a dynamic body
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;

// position must be converted to meters
bodyDef.position = [self toMeters:pos];
bodyDef.position = bodyDef.position + b2Vec2(-1, -1);
bodyDef.userData = [self addRandomSpriteAt:pos];
b2Body* bodyA = world->CreateBody(&bodyDef);
[self bodyCreateFixture:bodyA];

bodyDef.position = [self toMeters:pos];
bodyDef.userData = [self addRandomSpriteAt:pos];
b2Body* bodyB = world->CreateBody(&bodyDef);
[self bodyCreateFixture:bodyB];

bodyDef.position = [self toMeters:pos];
bodyDef.position = bodyDef.position + b2Vec2(1, 1);
bodyDef.userData = [self addRandomSpriteAt:pos];
b2Body* bodyC = world->CreateBody(&bodyDef);
[self bodyCreateFixture:bodyC];

b2RevoluteJointDef jointDef;
jointDef.Initialize(bodyA, bodyB, bodyB->GetWorldCenter());
bodyA->GetWorld()->CreateJoint(&jointDef);

jointDef.Initialize(bodyB, bodyC, bodyC->GetWorldCenter());
bodyA->GetWorld()->CreateJoint(&jointDef);

// create an invisible static body to attach to
bodyDef.type = b2_staticBody;
bodyDef.position = [self toMeters:pos];
b2Body* staticBody = world->CreateBody(&bodyDef);
jointDef.Initialize(staticBody, bodyA, bodyA->GetWorldCenter());
bodyA->GetWorld()->CreateJoint(&jointDef);

Chipmunk

.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import "cocos2d.h"
#import "chipmunk.h"

enum
{
    kTagBatchNode = 1,
};

@interface HelloWorld : CCLayer
{
    cpSpace* space;
}

+(id) scene;

@end

space等同于box2d中的world

初始化

1
2
3
4
5
cpInitChipmunk();

space = cpSpaceNew();
space->iterations = 8;
space->gravity = CGPointMake(0, -100);

销毁

1
2
3
4
5
-(void) dealloc
{
    cpSpaceFree(space);
    [super dealloc];
}

设置ground body(容器)

1
2
3
4
5
6
7
8
9
10
11
// for the ground body we'll need these values
CGSize screenSize = [CCDirector sharedDirector].winSize;
CGPoint lowerLeftCorner = CGPointMake(0, 0);
CGPoint lowerRightCorner = CGPointMake(screenSize.width, 0);
CGPoint upperLeftCorner = CGPointMake(0, screenSize.height);
CGPoint upperRightCorner = CGPointMake(screenSize.width, screenSize.height);

// Create the static body that keeps objects within the screen area
float mass = INFINITY;
float inertia = INFINITY;
cpBody* staticBody = cpBodyNew(mass, inertia);

创建容器边缘

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
cpShape* shape;
float elasticity = 1.0f;
float friction = 1.0f;
float radius = 0.0f;

// bottom
shape = cpSegmentShapeNew(staticBody, lowerLeftCorner, lowerRightCorner, radius);
shape->e = elasticity;
shape->u = friction;
cpSpaceAddStaticShape(space, shape);

// top
shape = cpSegmentShapeNew(staticBody, upperLeftCorner, upperRightCorner, radius);
shape->e = elasticity;
shape->u = friction;
cpSpaceAddStaticShape(space, shape);

// left
shape = cpSegmentShapeNew(staticBody, lowerLeftCorner, upperLeftCorner, radius);
shape->e = elasticity;
shape->u = friction;
cpSpaceAddStaticShape(space, shape);

// right
shape = cpSegmentShapeNew(staticBody, lowerRightCorner, upperRightCorner, radius);
shape->e = elasticity;
shape->u = friction;
cpSpaceAddStaticShape(space, shape);

添加Sprite

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
-(CCSprite*) addRandomSpriteAt:(CGPoint)pos
{
    CCSpriteBatchNode* batch = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];
   
    int idx = CCRANDOM_0_1() * TILESET_COLUMNS;
    int idy = CCRANDOM_0_1() * TILESET_ROWS;
    CGRect tileRect = CGRectMake(TILESIZE * idx, TILESIZE * idy, TILESIZE, TILESIZE);
    CCSprite* sprite = [CCSprite spriteWithBatchNode:batch rect:tileRect];
    sprite.position = pos;
    [batch addChild:sprite];
   
    return sprite;
}

-(void) addNewSpriteAt:(CGPoint)pos
{
    float mass = 0.5f;
    float moment = cpMomentForBox(mass, TILESIZE, TILESIZE);
    cpBody* body = cpBodyNew(mass, moment);
   
    body->p = pos;
    cpSpaceAddBody(space, body);
   
    float halfTileSize = TILESIZE * 0.5f;
    int numVertices = 4;
    CGPoint vertices[] =
    {
        CGPointMake(-halfTileSize, -halfTileSize),
        CGPointMake(-halfTileSize, halfTileSize),
        CGPointMake(halfTileSize, halfTileSize),
        CGPointMake(halfTileSize, -halfTileSize),
    };

    CGPoint offset = CGPointZero;
    float elasticity = 0.3f;
    float friction = 0.7f;
   
    cpShape* shape = cpPolyShapeNew(body, numVertices, vertices, offset);
    shape->e = elasticity;
    shape->u = friction;
    shape->data = [self addRandomSpriteAt:pos];
    cpSpaceAddShape(space, shape);
}

update

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// C method that updates sprite position and rotation:
static void forEachShape(void* shapePointer, void* data)
{
    cpShape* shape = (cpShape*)shapePointer;
    CCSprite* sprite = (CCSprite*)shape->data;
    if (sprite != nil)
    {
        cpBody* body = shape->body;
        sprite.position = body->p;
        sprite.rotation = CC_RADIANS_TO_DEGREES(body->a) * -1;
    }
}

-(void) update:(ccTime)delta
{
    float timeStep = 0.03f;
    cpSpaceStep(space, timeStep);
   
    // call forEachShape C method to update sprite positions
    cpSpaceHashEach(space->activeShapes, &forEachShape, nil);
    cpSpaceHashEach(space->staticShapes, &forEachShape, nil);
}

检测碰撞

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
// C callback methods for collision handling
static int contactBegin(cpArbiter* arbiter, struct cpSpace* space, void* data)
{
    bool processCollision = YES;
   
    cpShape* shapeA;
    cpShape* shapeB;
    cpArbiterGetShapes(arbiter, &shapeA, &shapeB);
   
    CCSprite* spriteA = (CCSprite*)shapeA->data;
    CCSprite* spriteB = (CCSprite*)shapeB->data;
    if (spriteA != nil && spriteB != nil)
    {
        spriteA.color = ccMAGENTA;
        spriteB.color = ccMAGENTA;
    }
   
    return processCollision;
}

static void contactEnd(cpArbiter* arbiter, cpSpace* space, void* data)
{
    cpShape* shapeA;
    cpShape* shapeB;
    cpArbiterGetShapes(arbiter, &shapeA, &shapeB);
   
    CCSprite* spriteA = (CCSprite*)shapeA->data;
    CCSprite* spriteB = (CCSprite*)shapeB->data;
    if (spriteA != nil && spriteB != nil)
    {
        spriteA.color = ccWHITE;
        spriteB.color = ccWHITE;
    }
}

添加listener

1
2
3
4
        // Add the collision handlers
        unsigned int defaultCollisionType = 0;
        cpSpaceAddCollisionHandler(space, defaultCollisionType, defaultCollisionType,
                                   &contactBegin, NULL, NULL, &contactEnd, NULL);

How to Deploy an Xcode App on an iPhone

星期四, 六月 16th, 2011

http://www.ehow.com/how_8409934_deploy-xcode-app-iphone.html

1
Sign up for the iOS Developer Program. The cost is $99 and you can enroll at the Apple Developers’ website. Indicate whether you wish to enroll as an individual or a company. If you enroll as an individual, your name will appear as the creator of the apps you distribute in the Apple App Store. Enroll as a company if you plan to add additional developers to your team or you want your company name to appear as the app creator. To enroll as a company, you need a legal company name, official company creation documents and the authority to sign legal documents for your company.

2
Obtain an iPhone Development Certificate from the iPhone Developer Program Portal to test your iPhone apps on your device. You’ll need a certificate for each device you plan to test on. You will need the unique identifier (UUID) for each device. Connect the device to your Mac and start Xcode to get the UUID. Select “Window” and then “Organizer” on the menu. The Organizer will display the UUID for your iPhone.

3
Generate a certificate-signing request for each device you want to use for testing. Go to the “Keychain Access” application in the Applications/Utilities folder. Once there, select the “Certificate Assistant” menu and choose the option to “Request a Certificate From a Certificate Authority.” Enter your email address. Select “Save to disk” followed by “Let me specify key pair information.” Click “Continue.” Choose a 2048-bit key size and RSA algorithm and save to a file.

4
Log in to the iOS Dev Center. When you reach the iPhone Developer Program Portal page, click “Launch Assistant.” Create an App ID for your app and click “Continue.” Assign a development device by entering its UUID. Submit the certificate-signing request to Apple.

5
Provide a description for your provisioning profile and click “Generate.” Once your provisioning profile has been created, you can download and install it on your device by dragging the provisioning profile and dropping it on the Xcode icon.

6
Navigate back to the iOS Developer Program Portal to download the development certificate and install it on your iPhone. Download and double-click the file to install it into a keychain. In the “Keychain Access” application on your Mac, select the login keychain and you’ll see a certificate for “iPhone Developer.” Click “Continue” in the Development Provisioning Assistant at the iOS Developer Program Portal to install your iPhone application with Xcode. In Xcode, choose “Active SDK” and select the OS version number of your device.

Deploy your app to your iPhone. With the app’s project open in Xcode, run the app. You will be prompted for permission to access your certificate. Click “Allow.” Your application should now be running on your iPhone.

一个TiledMap素材网站

星期四, 六月 16th, 2011

http://pousse.rapiere.free.fr/tome/index.htm

Cocos2d notes(6) Tiled Map

星期三, 六月 15th, 2011

相关工具

1
2
3
4
5
6
7
8
9
        CCTMXTiledMap* tileMap = [CCTMXTiledMap tiledMapWithTMXFile:@"orthogonal.tmx"];
        [self addChild:tileMap z:-1 tag:TileMapNode];
       
        // Use a negative offset to set the tilemap's start position
        //tileMap.position = CGPointMake(-160, -120);

        // hide the event layer, we only need this information for code, not to display it
        CCTMXLayer* eventLayer = [tileMap layerNamed:@"GameEventLayer"];
        eventLayer.visible = NO;

判断区域–1
获得map上的坐标

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
-(CGPoint) locationFromTouch:(UITouch*)touch
{
    CGPoint touchLocation = [touch locationInView: [touch view]];
    return [[CCDirector sharedDirector] convertToGL:touchLocation];
}

-(CGPoint) locationFromTouches:(NSSet*)touches
{
    return [self locationFromTouch:[touches anyObject]];
}

-(CGPoint) tilePosFromLocation:(CGPoint)location tileMap:(CCTMXTiledMap*)tileMap
{
    // Tilemap position must be subtracted, in case the tilemap position is not at 0,0 due to scrolling
    CGPoint pos = ccpSub(location, tileMap.position);
   
    // Cast to int makes sure that result is in whole numbers, tile coordinates will be used as array indices
    pos.x = (int)(pos.x / tileMap.tileSize.width);
    pos.y = (int)((tileMap.mapSize.height * tileMap.tileSize.height - pos.y) / tileMap.tileSize.height);
   
    CCLOG(@"touch at (%.0f, %.0f) is at tileCoord (%i, %i)", location.x, location.y, (int)pos.x, (int)pos.y);
    NSAssert(pos.x >= 0 && pos.y >= 0 && pos.x < tileMap.mapSize.width && pos.y < tileMap.mapSize.height,
             @"%@: coordinates (%i, %i) out of bounds!", NSStringFromSelector(_cmd), (int)pos.x, (int)pos.y);
   
    return pos;
}

-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    CCNode* node = [self getChildByTag:TileMapNode];
    NSAssert([node isKindOfClass:[CCTMXTiledMap class]], @"not a CCTMXTiledMap");  
    CCTMXTiledMap* tileMap = (CCTMXTiledMap*)node;

    // get the position in tile coordinates from the touch location
    CGPoint touchLocation = [self locationFromTouches:touches];
    CGPoint tilePos = [self tilePosFromLocation:touchLocation tileMap:tileMap];
   
    // Check if the touch was on water (eg. tiles with isWater property drawn in GameEventLayer)
    bool isTouchOnWater = NO;
    CCTMXLayer* eventLayer = [tileMap layerNamed:@"GameEventLayer"];
    int tileGID = [eventLayer tileGIDAt:tilePos];
   
    if (tileGID != 0)
    {
        NSDictionary* properties = [tileMap propertiesForGID:tileGID];
        if (properties)
        {
            CCLOG(@"NSDictionary 'properties' contains:\n%@", properties);
            NSString* isWaterProperty = [properties valueForKey:@"isWater"];
            isTouchOnWater = ([isWaterProperty boolValue] == YES);
        }
    }
   
    // decide what to do depending on where the touch was ...
    if (isTouchOnWater)
    {
        [[SimpleAudioEngine sharedEngine] playEffect:@"alien-sfx.caf"];
    }
    else
    {
        // get the winter layer and toggle its visibility
        CCTMXLayer* winterLayer = [tileMap layerNamed:@"WinterLayer"];
        winterLayer.visible = !winterLayer.visible;
       
        // remove the touched tile
        //[winterLayer removeTileAt:tilePos];
       
        // adds a given tile
        //tileGID = [winterLayer tileGIDAt:CGPointMake(0, 19)];
        //[winterLayer setTileGID:tileGID at:tilePos];
    }
}

判断区域–2

1
2
3
4
5
6
7
8
9
10
-(CGRect) getRectFromObjectProperties:(NSDictionary*)dict tileMap:(CCTMXTiledMap*)tileMap
{
    float x, y, width, height;
    x = [[dict valueForKey:@"x"] floatValue] + tileMap.position.x;
    y = [[dict valueForKey:@"y"] floatValue] + tileMap.position.y;
    width = [[dict valueForKey:@"width"] floatValue];
    height = [[dict valueForKey:@"height"] floatValue];
   
    return CGRectMake(x, y, width, height);
}
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
    // Check if the touch was within one of the rectangle objects
    CCTMXObjectGroup* objectLayer = [tileMap objectGroupNamed:@"ObjectLayer"];
    NSAssert([objectLayer isKindOfClass:[CCTMXObjectGroup class]], @"ObjectLayer not found or not a CCTMXObjectGroup");

    bool isTouchInRectangle = NO;
    int numObjects = [objectLayer.objects count];
    for (int i = 0; i < numObjects; i++)
    {
        NSDictionary* properties = [objectLayer.objects objectAtIndex:i];
        CGRect rect = [self getRectFromObjectProperties:properties tileMap:tileMap];

        if (CGRectContainsPoint(rect, touchLocation))
        {
            isTouchInRectangle = YES;
            break;
        }
    }
   
    // decide what to do depending on where the touch was ...
    if (isTouchOnWater)
    {
        [[SimpleAudioEngine sharedEngine] playEffect:@"alien-sfx.caf"];
    }
    else if (isTouchInRectangle)
    {
        CCParticleSystem* system = [CCQuadParticleSystem particleWithFile:@"fx-explosion.plist"];
        system.autoRemoveOnFinish = YES;
        system.position = touchLocation;
        [self addChild:system z:1];
    }
    else
    {
        // get the winter layer and toggle its visibility
        CCTMXLayer* winterLayer = [tileMap layerNamed:@"WinterLayer"];
        winterLayer.visible = !winterLayer.visible;
       
        // remove the touched tile
        //[winterLayer removeTileAt:tilePos];
       
        // adds a given tile
        //tileGID = [winterLayer tileGIDAt:CGPointMake(0, 19)];
        //[winterLayer setTileGID:tileGID at:tilePos];
    }

移动视点到点击位置

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
// move tilemap so that touched tiles is at center of screen
[self centerTileMapOnTileCoord:tilePos tileMap:tileMap];

-(void) centerTileMapOnTileCoord:(CGPoint)tilePos tileMap:(CCTMXTiledMap*)tileMap
{
    // center tilemap on the given tile pos
    CGSize screenSize = [[CCDirector sharedDirector] winSize];
    CGPoint screenCenter = CGPointMake(screenSize.width * 0.5f, screenSize.height * 0.5f);
   
    // tile coordinates are counted from upper left corner, this maps coordinates to lower left corner
    tilePos.y = (tileMap.mapSize.height - 1) - tilePos.y;
   
    // point is now at lower left corner of the screen
    CGPoint scrollPosition = CGPointMake(-(tilePos.x * tileMap.tileSize.width),
                                          -(tilePos.y * tileMap.tileSize.height));
   
    // offset point to center of screen and center of tile
    scrollPosition.x += screenCenter.x - tileMap.tileSize.width * 0.5f;
    scrollPosition.y += screenCenter.y - tileMap.tileSize.height * 0.5f;

    // make sure tilemap scrolling stops at the tilemap borders
    scrollPosition.x = MIN(scrollPosition.x, 0);
    scrollPosition.x = MAX(scrollPosition.x, -screenSize.width);
    scrollPosition.y = MIN(scrollPosition.y, 0);
    scrollPosition.y = MAX(scrollPosition.y, -screenSize.height);
   
    CCLOG(@"tilePos: (%i, %i) moveTo: (%.0f, %.0f)", (int)tilePos.x, (int)tilePos.y, scrollPosition.x, scrollPosition.y);
   
    CCAction* move = [CCMoveTo actionWithDuration:0.2f position:scrollPosition];
    [tileMap stopAllActions];
    [tileMap runAction:move];
}

45度角地图
不能使用CC_DIRECTOR_INIT();
创建部分重写

1
2
3
4
5
6
7
8
9
10
11
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    if ([CCDirector setDirectorType:kCCDirectorTypeDisplayLink] == NO)
        [CCDirector setDirectorType:kCCDirectorTypeNSTimer];
   
    CCDirector *director = [CCDirector sharedDirector];                                    
    [director setAnimationInterval:1.0/60];                                                
    EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
                                     pixelFormat:kEAGLColorFormatRGB565
                                     depthFormat:GL_DEPTH_COMPONENT24_OES
                              preserveBackbuffer:NO];
    [director setProjection:kCCDirectorProjection2D];

45度角地图获取点击位置

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
-(CGPoint) tilePosFromLocation:(CGPoint)location tileMap:(CCTMXTiledMap*)tileMap
{
    // Tilemap position must be subtracted, in case the tilemap position is not at 0,0 due to scrolling
    CGPoint pos = ccpSub(location, tileMap.position);
   
    float halfMapWidth = tileMap.mapSize.width * 0.5f;
    float mapHeight = tileMap.mapSize.height;
    float tileWidth = tileMap.tileSize.width;
    float tileHeight = tileMap.tileSize.height;
   
    CGPoint tilePosDiv = CGPointMake(pos.x / tileWidth, pos.y / tileHeight);
    float inverseTileY = mapHeight - tilePosDiv.y;

    // Cast to int makes sure that result is in whole numbers, tile coordinates will be used as array indices
    float posX = (int)(inverseTileY + tilePosDiv.x - halfMapWidth);
    float posY = (int)(inverseTileY - tilePosDiv.x + halfMapWidth);

    // make sure coordinates are within isomap bounds
    posX = MAX(0, posX);
    posX = MIN(tileMap.mapSize.width - 1, posX);
    posY = MAX(0, posY);
    posY = MIN(tileMap.mapSize.height - 1, posY);
   
    pos = CGPointMake(posX, posY);

    CCLOG(@"touch at (%.0f, %.0f) is at tileCoord (%i, %i)", location.x, location.y, (int)pos.x, (int)pos.y);
    //CCLOG(@"\tinverseY: %.2f -- tilePosDiv: (%.2f, %.2f) -- halfMapWidth: %.0f\n", inverseTileY, tilePosDiv.x, tilePosDiv.y, halfMapWidth);
   
    return pos;
}

滚动地图

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
-(void) centerTileMapOnTileCoord:(CGPoint)tilePos tileMap:(CCTMXTiledMap*)tileMap
{
    // center tilemap on the given tile pos
    CGSize screenSize = [[CCDirector sharedDirector] winSize];
    CGPoint screenCenter = CGPointMake(screenSize.width * 0.5f, screenSize.height * 0.5f);
   
    // get the ground layer
    CCTMXLayer* layer = [tileMap layerNamed:@"Ground"];
    NSAssert(layer != nil, @"Ground layer not found!");
   
    // internally tile Y coordinates are off by 1, this fixes the returned pixel coordinates
    tilePos.y -= 1;
   
    // get the pixel coordinates for a tile at these coordinates
    CGPoint scrollPosition = [layer positionAt:tilePos];
    // negate the position for scrolling
    scrollPosition = ccpMult(scrollPosition, -1);
    // add offset to screen center
    scrollPosition = ccpAdd(scrollPosition, screenCenter);
   
    CCLOG(@"tilePos: (%i, %i) moveTo: (%.0f, %.0f)", (int)tilePos.x, (int)tilePos.y, scrollPosition.x, scrollPosition.y);
   
    CCAction* move = [CCMoveTo actionWithDuration:0.2f position:scrollPosition];
    [tileMap stopAllActions];
    [tileMap runAction:move];
}

给地图添加一个宽度10的边框后修正的位置

1
2
3
4
5
6
7
8
9
    const int borderSize = 10;
    playableAreaMin = CGPointMake(borderSize, borderSize);
    playableAreaMax = CGPointMake(tileMap.mapSize.width - 1 - borderSize, tileMap.mapSize.height - 1 - borderSize);

// make sure coordinates are within bounds of the playable area
    posX = MAX(playableAreaMin.x, posX);
    posX = MIN(playableAreaMax.x, posX);
    posY = MAX(playableAreaMin.y, posY);
    posY = MIN(playableAreaMax.y, posY);

添加player

1
2
3
4
5
6
7
#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface Player : CCSprite
{
}
+(id) player;
@end
1
2
3
4
5
6
// Create the player and add it
player = [Player player];
player.position = CGPointMake(screenSize.width / 2, screenSize.height / 2);
// approximately position player's texture to best match the tile center position
player.anchorPoint = CGPointMake(0.3f, 0.1f);
[self addChild:player];

player和遮挡的关系

1
2
3
4
5
6
-(void) updateVertexZ:(CGPoint)tilePos tileMap:(CCTMXTiledMap*)tileMap
{
    float lowestZ = -(tileMap.mapSize.width + tileMap.mapSize.height);
    float currentZ = tilePos.x + tilePos.y;
    self.vertexZ = lowestZ + currentZ - 1;
}

cocos2d notes(5)

星期三, 六月 15th, 2011

粒子效果

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
    [self removeChildByTag:1 cleanup:YES];
   
    CCParticleSystem* system;
   
    switch (particleType)
    {
        case ParticleTypeExplosion:
            system = [CCParticleExplosion node];
            break;
        case ParticleTypeFire:
            system = [CCParticleFire node];
            break;
        case ParticleTypeFireworks:
            system = [CCParticleFireworks node];
            break;
        case ParticleTypeFlower:
            system = [CCParticleFlower node];
            break;
        case ParticleTypeGalaxy:
            system = [CCParticleGalaxy node];
            break;
        case ParticleTypeMeteor:
            system = [CCParticleMeteor node];
            break;
        case ParticleTypeRain:
            system = [CCParticleRain node];
            break;
        case ParticleTypeSmoke:
            system = [CCParticleSmoke node];
            break;
        case ParticleTypeSnow:
            system = [CCParticleSnow node];
            break;
        case ParticleTypeSpiral:
            system = [CCParticleSpiral node];
            break;
        case ParticleTypeSun:
            system = [CCParticleSun node];
            break;
           
        default:
            // do nothing
            break;
    }

    [self addChild:system z:1 tag:1];

自定义粒子效果

1
2
3
4
5
6
7
8
#import <Foundation/Foundation.h>
#import "cocos2d.h"
// Depending on the targeted device the ParticleEffectSelfMade class will either derive
// from CCPointParticleSystem or CCQuadParticleSystem
@interface ParticleEffectSelfMade : ARCH_OPTIMAL_PARTICLE_SYSTEM
{
}
@end

ARCH_OPTIMAL_PARTICLE_SYSTEM宏将自动判断使用效率较高的父类(CCPointParticleSystem or CCQuadParticleSystem)

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#import "ParticleEffectSelfMade.h"


@implementation ParticleEffectSelfMade

-(id) init
{
    return [self initWithTotalParticles:250];
}

-(id) initWithTotalParticles:(int)numParticles
{
    if ((self = [super initWithTotalParticles:numParticles]))
    {
        // DURATION
        // most effects use infinite duration
        self.duration = kCCParticleDurationInfinity;
        // for timed effects use a number in seconds how long particles should be emitted
        //self.duration = 2.0f;
        // If the particle system runs for a fixed time, this will remove the particle system node from
        // its parent once all particles have died. Has no effect for infinite particle systems.
        self.autoRemoveOnFinish = YES;

        // MODE
        // particles are affected by gravity
        self.emitterMode = kCCParticleModeGravity;
        // particles move in a circle instead
        //self.emitterMode = kCCParticleModeRadius;
       
        // some properties must only be used with a specific emitterMode!
        if (self.emitterMode == kCCParticleModeGravity)
        {
            // centerOfGravity is misleading as it determines the offset where particles appear. The actual
            // center of gravity is the node's position.
            self.centerOfGravity = CGPointMake(-15, 0);
            // gravity determines the particle's speed in the x and y directions
            self.gravity = CGPointMake(-50, -90);
            // radial acceleration affects how fast particles move depending on their distance to the emitter
            // positive radialAccel means particles speed up as they move away, negative means they slow down
            self.radialAccel = -90;
            self.radialAccelVar = 20;
            // tangential acceleration lets particles rotate around the emitter position,
            // and they speed up as they rotate around (slingshot effect)
            self.tangentialAccel = 120;
            self.tangentialAccelVar = 10;
            // speed is of course how fast particles move in general
            self.speed = 15;
            self.speedVar = 4;
        }
        else if (self.emitterMode == kCCParticleModeRadius)
        {
            // the distance from the emitter position that particles will be spawned and sent out
            // in a radial (circular) fashion
            self.startRadius = 100;
            self.startRadiusVar = 0;
            // the end radius the particles move towards, if less than startRadius particles will move
            // inwards, if greater than startRadius particles will move outward
            // you can use the keyword kCCParticleStartRadiusEqualToEndRadius to create a perfectly circular rotation
            self.endRadius = 10;
            self.endRadiusVar = 0;
            // how fast the particles rotate around
            self.rotatePerSecond = 180;
            self.rotatePerSecondVar = 0;
        }

        // EMITTER POSITION
        // emitter position is at the center of the node (default)
        // this is where new particles will appear
        self.position = CGPointZero;
        self.posVar = CGPointZero;
        // The positionType determines if existing particles should be repositioned when the node is moving
        // (kCCPositionTypeGrouped) or if the particles should remain where they are (kCCPositionTypeFree).
        self.positionType = kCCPositionTypeFree;
       
        // PARTICLE SIZE
        // size of individual particles in pixels
        self.startSize = 40.0f;
        self.startSizeVar = 0.0f;
        self.endSize = kCCParticleStartSizeEqualToEndSize;
        self.endSizeVar = 0;

        // ANGLE (DIRECTION)
        // the direction in which particles are emitted, 0 means upwards
        self.angle = 0;
        self.angleVar = 0;
       
        // PARTICLE LIFETIME
        // how long each individual particle will "life" (eg. stay on screen)
        self.life = 5.0f;
        self.lifeVar = 0.0f;
       
        // PARTICLE EMISSION RATE
        // how many particles per second are created (emitted)
        // particle creation stops if self.particleCount >= self.totalParticles
        // you can use this to create short burst effects with pauses between each burst
        self.emissionRate = 30;
        // normally set with initWithTotalParticles but you can change that number
        self.totalParticles = 250;

        // PARTICLE COLOR
        // A valid startColor must be set! Otherwise the particles may be invisible. The other colors are optional.
        // These colors determine the color of the particle at the start and the end of its lifetime.
        startColor.r = 1.0f;
        startColor.g = 0.25f;
        startColor.b = 0.12f;
        startColor.a = 1.0f;

        startColorVar.r = 0.0f;
        startColorVar.g = 0.0f;
        startColorVar.b = 0.0f;
        startColorVar.a = 0.0f;
       
        endColor.r = 0.0f;
        endColor.g = 0.0f;
        endColor.b = 0.0f;
        endColor.a = 1.0f;
       
        endColorVar.r = 0.0f;
        endColorVar.g = 0.0f;
        endColorVar.b = 1.0f;
        endColorVar.a = 0.0f;
       
        // BLEND FUNC
        // blend func influences how transparent colors are calculated
        // the first parameter is for the source, the second for the target
        // available blend func parameters are:
        // GL_ZERO   GL_ONE   GL_SRC_COLOR   GL_ONE_MINUS_SRC_COLOR   GL_SRC_ALPHA
        // GL_ONE_MINUS_SRC_ALPHA   GL_DST_ALPHA   GL_ONE_MINUS_DST_ALPHA
        self.blendFunc = (ccBlendFunc){GL_SRC_ALPHA, GL_DST_ALPHA};
        // shortcut to set the blend func to: GL_SRC_ALPHA, GL_ONE
        //self.blendAdditive = YES;
       
        // PARTICLE TEXTURE
        self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
    }
   
    return self;
}

@end

粒子设计工具
http://particledesigner.71squared.com/ 下载ParticleDesigner

使用ParticleDesigner生成的效果

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
-(void) runEffect
{
    // remove any previous particle FX
    [self removeChildByTag:1 cleanup:YES];
   
    CCParticleSystem* system;
   
    switch (particleType)
    {
        case ParticleTypeDesignedFX:
            // by using ARCH_OPTIMAL_PARTICLE_SYSTEM either the CCQuadParticleSystem or CCPointParticleSystem class is
            // used depending on the current target.
            system = [ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"designed-fx.plist"];
            break;
        case ParticleTypeDesignedFX2:
            // uses a plist with the texture already embedded
            system = [CCQuadParticleSystem particleWithFile:@"designed-fx2.plist"];
            system.positionType = kCCPositionTypeFree;
            break;
        case ParticleTypeDesignedFX3:
            // same effect but different texture (scaled down by Particle Designer)
            system = [CCQuadParticleSystem particleWithFile:@"designed-fx3.plist"];
            system.positionType = kCCPositionTypeFree;
            break;
        case ParticleTypeSelfMade:
            system = [ParticleEffectSelfMade node];
            break;
       
        default:
            // do nothing
            break;
    }

    CGSize winSize = [[CCDirector sharedDirector] winSize];
    system.position = CGPointMake(winSize.width / 2, winSize.height / 2);
    [self addChild:system z:1 tag:1];
   
    [label setString:NSStringFromClass([system class])];
}

在之前的射击游戏中加入爆炸粒子效果

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) gotHit
{
    hitPoints--;
    if (hitPoints <= 0)
    {
        self.visible = NO;
       
        // Play a particle effect when the enemy was destroyed
        CCParticleSystem* system;
        if (type == EnemyTypeBoss)
        {
            system = [ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"fx-explosion2.plist"];
        }
        else
        {
            system = [ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"fx-explosion.plist"];
        }
       
        // Set some parameters that can't be set in Particle Designer
        system.positionType = kCCPositionTypeFree;
        system.autoRemoveOnFinish = YES;
        system.position = self.position;
       
        // Add the particle effect to the GameScene, for these reasons:
        // - self is a sprite added to a spritebatch and will only allow CCSprite nodes (it crashes if you try)
        // - self is now invisible which might affect rendering of the particle effect
        // - since the particle effects are short lived, there is no harm done by adding them directly to the GameScene
        [[GameScene sharedGameScene] addChild:system];
    }
}

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