第一次用cocos2d做小游戏,初步了解使用cocos2d这个游戏引擎来做一个简单的游戏。
1)首先都是创建项目,加入场景;
2)然后添加player sprite ;
3)加入加速计;
4)控制玩家速度;
5)添加障碍物;
6)碰撞检测;
7)标签和位图字体;
8)播放音乐;
9)移植到ipad。
我逐步来讲:
1、创建项目,加入场景:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface GameScene : CCLayer
{
}
+(id) scene;
@end
@implementation GameScene
+(id) scene
{
CCScene* scene = [ CCScenenode ];
CCLayer* layer = [ GameScenenode ];
[ scene addChild: layer ];
return scene;
}
@end
2、然后添加player sprite :
@interface GameScene : CCLayer
{
CCSprite* player;
}
在方法
-(id) init
中设置 player 的初始位置
3、加入加速计:4、控制玩家速度:
在方法
-(id) init
中设置
[ selfsetAccelerometerEnabled: YES ];
然后就自动调用此方法来控制玩家速度
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
还有在方法
-(id) init中加入
[ selfscheduleUpdate ];
就会调用方法#pragma mark update
- (void) update:(ccTime)delta
5、添加障碍物:障碍物设置为蜘蛛
@interface GameScene :CCLayer
{
CCSprite* player;
CGPoint playerVelocity;
// 添加障碍物
CCArray* spiders;
float spiderMoveDuration;
int numSpidersMoved;
}
#pragma mark Spiders
//初始化障碍物的方法 蜘蛛开始的位置和蜘蛛的个数
- (void) initSpiders
// resetSpiders 方法 里面会调用 蜘蛛落下的方法
- (void) resetSpiders
// 让蜘蛛频繁落下的方法
- (void) spidersUpdate: (ccTime) delta
// 控制蜘蛛的运动
- (void) runSpiderMoveSequence: (CCSprite*)spider
// 控制蜘蛛蠕动
- (void) runSpiderWiggleSequence:(CCSprite *)spider
// 重设蜘蛛位置使之可以从屏幕上方重新落下
- (void) spiderDidDrop: (id) sender
6、碰撞检测:有蜘蛛落下,就必须考虑碰撞的问题
#pragma mark Collision Checks
// 碰撞检测
- (void) checkForCollision
当碰撞了,游戏结束,gameover#pragma mark Reset Game
// The game is played only using the accelerometer. The screen may go dark while playing because the player
// won't touch the screen. This method allows the screensaver to be disabled during gameplay.
- (void) setScreenSaverEnabled:(bool)enabled
- (void) showGameOver
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
点击画面
"tap screen to play again "时重新开始游戏
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[ self resetGame ];
startBOOL = NO;
}
- (void) resetGame
7、标签和位图字体:
@interface GameScene :CCLayer
{
CCSprite* player;
CGPoint playerVelocity;
// 添加障碍物
CCArray* spiders;
float spiderMoveDuration;
int numSpidersMoved;
// 标签
// CCLabelTTF* scoreLabel;
CCLabelBMFont* scoreLabel;
ccTime totalTime;
int score;
}
需要用到Hiero来创建 .fnt文件,自己做出自己喜欢的艺术字8、播放音乐:
#import "SimpleAudioEngine.h"
//背景音乐
[ [ SimpleAudioEnginesharedEngine ] playBackgroundMusic:@"GameLoop.mp3" loop:YES ];
// Preload the sound effect into memory so there's no delay when playing it the first time.
[ [ SimpleAudioEnginesharedEngine ] preloadEffect: @"alien-sfx.caf" ];
9、移植到ipad:
代码不能够写死,所有可视化的控件的位置都不能够写死,它们适应苹果的所有机型,每个控件放置的位置都要根据不同机型而变。由此可以减少程序员重新改代码来适应另一台机型。
程序源代码:
//
// GameScene.h
// DoodleDrop
//
// Created by apple on 13-3-14.
// Copyright 2013年 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface GameScene : CCLayer
{
CCSprite* player;
CGPoint playerVelocity;
// 添加障碍物
CCArray* spiders;
float spiderMoveDuration;
int numSpidersMoved;
// 标签
// CCLabelTTF* scoreLabel;
CCLabelBMFont* scoreLabel;
ccTime totalTime;
int score;
// 是否刚开始
BOOL startBOOL;
}
+(id) scene;
@end
//
// GameScene.m
// DoodleDrop
//
// Created by apple on 13-3-14.
// Copyright 2013年 __MyCompanyName__. All rights reserved.
//
#import "GameScene.h"
#import "SimpleAudioEngine.h"
@interface GameScene (PrivateMethods)
- (void) initSpiders;
- (void) resetSpiders;
- (void) spidersUpdate:(ccTime)delta;
- (void) runSpiderMoveSequence:(CCSprite*)spider;
- (void) runSpiderWiggleSequence:(CCSprite*)spider;
- (void) spiderDidDrop:(id)sender;
- (void) checkForCollision;
- (void) showGameOver;
- (void) resetGame;
@end
@implementation GameScene
+(id) scene
{
CCScene* scene = [ CCScenenode ];
CCLayer* layer = [ GameScenenode ];
[ scene addChild: layer ];
return scene;
}
-(id) init
{
if ( ( self = [super init ] ) )
{
CCLOG( @"%@: %@",NSStringFromSelector( _cmd ), self );
[ selfsetAccelerometerEnabled: YES ];
player = [CCSprite spriteWithFile:@"Ability_Druid_PredatoryInstincts.png" ];
[ self addChild:player z: 0tag: 1 ];
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
float imageHeight = [ player texture ].contentSize.height;
player.position =CGPointMake( screenSize.width / 2 , imageHeight / 2 );
[ self scheduleUpdate ];
[ self initSpiders ];
// scoreLabel = [ CCLabelTTF labelWithString: @"0" fontName: @"Cochin" fontSize: 48 ];
scoreLabel = [ CCLabelBMFont labelWithString:@"0" fntFile:@"bitmapfont3.fnt" ];
scoreLabel.position =CGPointMake( screenSize.width / 2 , screenSize.height );
// ajust the label's anchorPoint's y position to make it align with the top.
scoreLabel.anchorPoint =CGPointMake( 0.5f ,1.0f );
// add the score label with z value of -1 so it's drawn below everything else
[ self addChild:scoreLabel z: -1 ];
//背景音乐
[ [ SimpleAudioEnginesharedEngine ] playBackgroundMusic:@"GameLoop.mp3" loop:YES ];
// Preload the sound effect into memory so there's no delay when playing it the first time.
[ [ SimpleAudioEnginesharedEngine ] preloadEffect: @"alien-sfx.caf" ];
// Seed the randomizer once with current time. This way the game doesn't start with the same sequence
// of spiders dropping every time it is started.
srandom( time(NULL ) );
// start with game over first
startBOOL = YES;
[ self showGameOver ];
}
return self;
}
-(void) dealloc
{
CCLOG(@"%@: %@", NSStringFromSelector(_cmd ) , self );
// 障碍物
[ spiders release ];
// // 标签
// [ scoreLabel release ];
//never forget to call [ super dealloc ]
[ super dealloc ];
}
#pragma mark Spiders
//初始化障碍物
- (void) initSpiders
{
// CCLOG( @"蜘蛛" );
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
// using a temporary spider sprite is the easiest way to get the image's size
CCSprite* tempSprider = [ CCSprite spriteWithFile: @"spider.png" ];
float imageWidth = [ tempSprider texture ].contentSize.width;
// use as many spiders as can fit next to each other over the whole screen width.
int numSpiders = screenSize.width / imageWidth;
// Initialize the spiders array. Make sure it hasn't been initialized before.
NSAssert(spiders == nil ,@"%@: spiders array is already initialized!" , NSStringFromSelector(_cmd ) );
spiders = [ [ CCArrayalloc ] initWithCapacity: numSpiders ];
for ( int i = 0 ; i < numSpiders ; i++ )
{
CCSprite* spider = [ CCSprite spriteWithFile: @"spider.png" ];
[ self addChild: spiderz: 0 tag:2 ];
// CCLOG( @"%@" , spiders );
// also add the spider to the spiders array.
[ spiders addObject: spider ];
}
// call the method to reposition all spiders
[ selfresetSpiders ];
}
// resetSpiders 方法
- (void) resetSpiders
{
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
int numSpiders = [ spiderscount ];
if ( numSpiders > 0 )
{
// get any spider to get its image width
CCSprite* tempSpider = [ spiders lastObject ];
CGSize size = [ tempSpider texture ].contentSize;
for ( int i =0 ; i < numSpiders ; i++ )
{
// put each spider at its designated position outside the screen
CCSprite* spider = [ spiders objectAtIndex: i ];
spider.position = CGPointMake( size.width * i + size.width *0.5f , screenSize.height + size.height );
[ spider stopAllActions ];
}
}
// 让蜘蛛频繁落下的方法
// unschedule the selector just in case. if it isn't scheduled it won't do anything.
[ self unschedule:@selector( spidersUpdate:) ];
// schedule the spiders update logic to run at the given interval.
[ self schedule:@selector( spidersUpdate:) interval:0.7f ];
// reset the move spiders counter and spider move duration ( affects speed )
numSpidersMoved = 0;
spiderMoveDuration = 4.0f;
}
// 让蜘蛛频繁落下的方法
- (void) spidersUpdate: (ccTime) delta
{
// CCLOG( @"999" );
// try to find a spider which isn't currently moving.
for ( int i = 0 ; i < 10 ; i++ )
{
int randomSpiderIndex = CCRANDOM_0_1() * [ spiders count ];
// CCLOG( @"%d" , randomSpiderIndex );
CCSprite* spider = [ spiders objectAtIndex: randomSpiderIndex ];
// if the spider isn't moving it won't have any running actions.
if ( [ spider numberOfRunningActions ] ==0 )
{
// If you're curious how often the for i < 10 loop is actually run ...
if ( i > 0 )
{
CCLOG(@"Dropping a Spider after %i retries." , i );
}
// This is the sequence which controls the spiders' movement
[ self runSpiderMoveSequence: spider ];
// only one spider should start moving at a time.
break;
}
}
}
// 控制蜘蛛的运动
- (void) runSpiderMoveSequence: (CCSprite*)spider
{
// slowly increase the spider speed over time
numSpidersMoved++;
if (numSpidersMoved % 8 ==0 && spiderMoveDuration >2.0f )
{
spiderMoveDuration -=0.1f;
}
// this is the sequence which controls the spiders' movement. A CCCallFuncN is used to reset the
// spider once it has moved outside the lower border of the screen, which is when it can be re-used.
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
CGPoint hangInTherePosition = CGPointMake( spider.position.x , screenSize.height -3 * [ spider texture ].contentSize.height );
CGPoint belowScreenPosition = CGPointMake( spider.position.x , - (3 * [ spider texture ].contentSize.height ) );
CCMoveTo* moveHang = [ CCMoveTo actionWithDuration: 4position: hangInTherePosition ];
CCEaseElasticOut* easeHang = [ CCEaseElasticOut actionWithAction: moveHang period: 0.8f ];
CCMoveTo* moveEnd = [ CCMoveTo actionWithDuration: spiderMoveDurationposition: belowScreenPosition ];
CCEaseBackInOut* easeEnd = [ CCEaseBackInOut actionWithAction: moveEnd ];
CCCallFuncN* callDidDrop = [ CCCallFuncN actionWithTarget: self
selector: @selector(spiderDidDrop:) ];
CCSequence* sequence = [ CCSequence actions: easeHang , easeEnd , callDidDrop ,nil ];
[ spider runAction: sequence ];
}
// 控制蜘蛛蠕动
- (void) runSpiderWiggleSequence:(CCSprite *)spider
{
// Do something icky with the spiders ...
CCScaleTo* scaleUp = [ CCScaleTo actionWithDuration: CCRANDOM_0_1() *2 + 1 scale:1.05f ];
CCEaseBackInOut* easeUp = [ CCEaseBackInOut actionWithAction: scaleUp ];
CCScaleTo* scaleDown = [ CCScaleTo actionWithDuration: CCRANDOM_0_1() *2 + 1 scale:0.95f ];
CCEaseBackInOut* easeDown = [ CCEaseBackInOut actionWithAction: scaleDown ];
CCSequence* scaleSequence = [ CCSequence actions: easeUp , easeDown , nil ];
CCRepeatForever* repeatScale = [ CCRepeatForever actionWithAction: scaleSequence ];
[ spider runAction: repeatScale ];
}
// 重设蜘蛛位置使之可以从屏幕上方重新落下
- (void) spiderDidDrop: (id) sender
{
// make sure sender is actually of the right class.
NSAssert( [ sender isKindOfClass: [ CCSprite class ] ], @"sender is not a CCSprite!" );
CCSprite* spider = ( CCSprite* )sender;
// move the spider back up outside the top of the screen
CGPoint pos = spider.position;
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
pos.y = screenSize.height + [ spidertexture ].contentSize.height;
spider.position = pos;
}
#pragma mark Accelerometer Input
//加速计输入
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
// controls how quickly velocity decelerates (lower = quicker to change direction )
float deceleration = 0.4f;
// determines how sensitive the accelerometer reacts ( higher = more sensitive )
float sensitivity = 6.0f;
// how fast the velocity can be at most
float maxVelocity = 100;
// adjust velocity based on current accelerometer acceleration
playerVelocity.x =playerVelocity.x * deceleration + acceleration.x * sensitivity;
// we must limit the maximum velocity of the player sprites , in both directions
if ( playerVelocity.x > maxVelocity )
{
playerVelocity.x = maxVelocity;
}
else if ( playerVelocity.x < - maxVelocity )
{
playerVelocity.x = - maxVelocity;
}
}
#pragma mark update
- (void) update:(ccTime)delta
{
// Keep adding up the playerVelocity to the player's position
CGPoint pos =player.position;
pos.x += playerVelocity.x;
// The Player should also be stopped from going outside the screen
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
float imageWidthHalved = [ player texture ].contentSize.width *0.5f;
float leftBorderLimit = imageWidthHalved;
float rightBorderLimit = screenSize.width - imageWidthHalved;
// preventing the player sprite from moving outside the screen
if ( pos.x < leftBorderLimit )
{
pos.x = leftBorderLimit;
playerVelocity = CGPointZero;
}
else if ( pos.x > rightBorderLimit )
{
pos.x = rightBorderLimit;
playerVelocity = CGPointZero;
}
// assigning the modified position back
player.position = pos;
[ selfcheckForCollision ];
// Update the Score ( Timer ) once per second. If you'd do it more often, especially every frame, this
// will easily drag the framerate down. Updating a CCLabel's strings is slow!
totalTime += delta;
int currentTime = (int)totalTime;
if ( score < currentTime )
{
score = currentTime;
[ scoreLabel setString: [NSString stringWithFormat: @"%i" , score ] ];
}
}
#pragma mark Collision Checks
// 碰撞检测
- (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 = [ spiderscount ];
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 )
{
[ [ SimpleAudioEngine sharedEngine ] playEffect: @"alien-sfx.caf" ];
// // game Over ( just restart the game for now )
// [ self resetGame ];
[ self showGameOver ];
break;
}
}
}
#pragma mark Reset Game
// The game is played only using the accelerometer. The screen may go dark while playing because the player
// won't touch the screen. This method allows the screensaver to be disabled during gameplay.
- (void) setScreenSaverEnabled:(bool)enabled
{
UIApplication* thisApp = [UIApplication sharedApplication ];
thisApp.idleTimerDisabled = !enabled;
}
- (void) showGameOver
{
// Re-enable screensaver , to prevent battary drain in case the user puts the device aside without turning it off.
[ selfsetScreenSaverEnabled: YES ];
// have everything stop
CCNode* node;
CCARRAY_FOREACH( [ self children ] , node )
{
[ node stopAllActions ];
}
// I do want the spiders to keep wiggling so I simply restart this here
CCSprite* spider;
CCARRAY_FOREACH( spiders , spider )
{
[ selfrunSpiderWiggleSequence: spider ];
}
// disable accelerometer input for the time being
[ selfsetAccelerometerEnabled: NO ];
// but allow touch input now
[ selfsetTouchEnabled: YES ];
// stop the scheduled selectors
[ selfunscheduleAllSelectors ];
// add the labels shown during game over
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
// CCLabelTTF* gameOver = [ CCLabelTTF labelWithString: @"GAME OVER!" fontName: @"Marker Felt" fontSize: 60 ];
CCLabelTTF* gameOver = [ CCLabelTTF labelWithString: @"GAME OVER!"fontName: @"TrebuchetMS" fontSize: 60 ];
// CCLOG( @"gameOver %@" , gameOver );
gameOver.position = CGPointMake( screenSize.width /2 , screenSize.height / 3 );
[ self addChild: gameOverz: 100 tag:100 ];
// 一开始隐藏 game over label
gameOver.visible = !startBOOL ;
// game over label runs 3 different actions at the same time to create the combined effect
// 1) color tinting
CCTintTo* tint1 = [ CCTintToactionWithDuration: 2 red: 255 green: 0 blue: 0 ];
CCTintTo* tint2 = [ CCTintToactionWithDuration: 2 red: 255 green: 255 blue: 0 ];
CCTintTo* tint3 = [ CCTintToactionWithDuration: 2 red: 0 green: 255 blue: 0 ];
CCTintTo* tint4 = [ CCTintToactionWithDuration: 2 red: 0 green: 255 blue: 255 ];
CCTintTo* tint5 = [ CCTintToactionWithDuration: 2 red: 0 green: 0 blue: 255 ];
CCTintTo* tint6 = [ CCTintToactionWithDuration: 2 red: 255 green: 0 blue: 255 ];
CCSequence* tintSequence = [ CCSequence actions: tint1 , tint2 , tint3 , tint4 , tint5 , tint6 ,nil ];
CCRepeatForever* repeatTint = [ CCRepeatForever actionWithAction: tintSequence ];
[ gameOver runAction: repeatTint ];
// 2) rotation with ease 旋转自如
CCRotateTo* rotate1 = [ CCRotateTo actionWithDuration: 2angle: 3 ];
CCEaseBounceInOut* bounce1 = [CCEaseBounceInOut actionWithAction: rotate1 ];
CCRotateTo* rotate2 = [ CCRotateTo actionWithDuration: 2angle: -3 ];
CCEaseBounceInOut* bounce2 = [CCEaseBounceInOut actionWithAction: rotate2 ];
CCSequence* rotateSequence = [ CCSequence actions: bounce1 , bounce2 , nil ];
CCRepeatForever* repeatBounce = [ CCRepeatForever actionWithAction: rotateSequence ];
[ gameOver runAction: repeatBounce ];
// 3) jumping
CCJumpBy* jump = [ CCJumpByactionWithDuration: 3 position: CGPointZero height: screenSize.height /3 jumps: 1 ];
CCRepeatForever* repeatJump = [ CCRepeatForever actionWithAction: jump ];
[ gameOver runAction: repeatJump ];
// touch to continue label
// MarkerFelt-Thin TrebuchetMS
CCLabelTTF* touch = [CCLabelTTF labelWithString:@"tap screen to play again " fontName:@"Verdana-Italic" fontSize:20 ];
touch.position = CGPointMake( screenSize.width /2 , screenSize.height / 4 );
[ self addChild: touchz: 100 tag:101 ];
// did you try turning it off and on again?你尝试将其关闭,重新打开吗?
CCBlink* blink = [ CCBlinkactionWithDuration: 10 blinks: 20 ];
CCRepeatForever* repeatBlink = [ CCRepeatForever actionWithAction: blink ];
[ touch runAction: repeatBlink ];
}
//- (void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
//{
// [ self resetGame ];
//}
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[ self resetGame ];
startBOOL = NO;
}
- (void) resetGame
{
// prevent screensaver from darkening the screen while the game is played
[ selfsetScreenSaverEnabled: NO ];
// remove game over label & touch to continue label
[ selfremoveChildByTag: 100cleanup: YES ];
[ selfremoveChildByTag: 101cleanup: YES ];
// re-enable accelerometer
self.accelerometerEnabled =YES;
self.touchEnabled =NO;
// put all spiders back to top
[ selfresetSpiders ];
// re-schedule update
[ selfscheduleUpdate ];
// reset score
score = 0;
totalTime = 0;
[ scoreLabel setString:@"0" ];
}
// Only draw this debugging information in , well , debug builds.
- (void) draw
{
#if DEBUG
// Iterate through all nodes of the layer.
CCNode* node;
CCARRAY_FOREACH( [ self children ] , node )
{
// Make sure the node is a CCSprite and has the right tags.
if ( [ node isKindOfClass: [CCSprite class ] ] && node.tag ==2 )
{
// The sprite's collision radius is a percentage of its image width. Use that to draw a circle
// which represents the sprite's collision radius.
CCSprite* sprite = (CCSprite*) node;
float radius = [ sprite texture ].contentSize.width * 0.4f;
float angle = 0;
int numSegments = 10;
bool drawLineToCenter = NO;
ccDrawCircle( sprite.position , radius , angle , numSegments , drawLineToCenter );
}
}
#endif
CGSize screenSize = [ [ CCDirector sharedDirector ] winSize ];
// always keep variables you have to calculate only outside the loop
float threadCutPosition = screenSize.height *0.75f;
// Draw a spider thread using OpenGL
// CCARRAY_FOREACH is a bit faster than regular for loop
CCSprite* spider;
CCARRAY_FOREACH( spiders , spider )
{
// only draw thread up to a certain point
if ( spider.position.y > threadCutPosition )
{
// vary thread position a little so it looks a bit more dynamic
float threadX = spider.position.x + (CCRANDOM_0_1() * 2.0f - 1.0f );
// glColorMask( 0.5f , 0.5f , 0.5f , 1.0f );
// ccDrawLine( spider.position , CGPointMake( threadX , screenSize.height ) );
// glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
// ccColor4F( 0.5f , 0.5f , 0.5f , 1.0f );
// ccc4f( 0.0f , 0.0f , 0.5f , 1.0f );
ccDrawLine(spider.position,CGPointMake(threadX, screenSize.height));
}
}
}
@end