Archive for the '技术' Category
Grand Central Dispatch (GCD) Reference
星期四, 六月 30th, 2011Blocks Programming Topics
星期四, 六月 30th, 2011Cocos2d notes(7) physics engines
星期五, 六月 17th, 2011Chipmunk 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, 2011http://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, 2011Cocos2d notes(6) Tiled Map
星期三, 六月 15th, 20111 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, 2011https://github.com/sneakyness/SneakyInput
Cocos2D notes(4)
星期一, 六月 13th, 2011CCSpriteBatchNode
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]; |
