cocos2d 学习 第四章
#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface GameScene : CCLayer
{
CCSprite* player;
}
+(id) scene;
@end
启用加速计输入,创建并定位玩家精灵
-(id) init
{
if ((self = [super init]))
{
self.isAccelerometerEnabled = YES; // 支持重力加速
player = [CCSprite spriteWithFile:@"alien.png"]; // 由图片文件生成一个CCSprite对象作为主角
[self addChild:player z:0 tag:1]; // 将主角添加到当前场景中
CGSize screenSize = [[CCDirector sharedDirector] winSize]; // 通过CCDirector对象得到设备尺寸
float imageHeight = [player texture].contentSize.height; // 通过CCSprite对象得到主角图片的高度
player.position = CGPointMake(screenSize.width/2, imageHeight/2); // 将主角的位置设置为屏幕下方中心
[self initSpiders]; // 初始化蜘蛛对象
[selfscheduleUpdate]; // 是一个CCLayer对象定义的方法,它将在每桢调用update方法
}
return self;
}
注意,我没有保留玩家精灵。因为我们已将其添加为层 的子节点,所以 cocos2d 会保留它。另外由于玩家精灵永远不会从层中移除,因而现在这 样做就足够了,不需要特意对它进行保留。不保留一个内存被其他类或对象管理的对象称 为“保持弱引用”。
通过调用[player texture].contentSize.height 返回精灵纹理的内容尺寸。 我讲到 iOS 中纹理尺寸的大小只能是 2 的方幂。但是实际的图像尺寸可能会比纹理尺寸小。
-(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
CGPoint pos = player.position;
pos.x += acceleration.x * 10;
player.position = pos;
}
注意到奇怪之处了吗?上面的三行代码用一行就能写好:
// ERROR: lvalue required as left operand of assignment
player.position.x += acceleration.x * 10;
position 属性的类型是 CGPoint,这是一个 普通的 C 结构体。Objective-C 中的属性不能直接向结构体的域赋值。
注意到加速计输入哪里不正常了吗?是的,它反应迟缓,移动不畅。这是因为玩家精灵并没执行真实的加速和减速。
实现加速与减速的概念不在于直接改变玩家的位置值,而是使用单独的 CGPoint 变量 作为速度矢量。每次接收到加速计事件时,速度矢量就加上从加速计得到的输入。当然, 这意味着我们得把速度限制在一个任意的最大值内,否则减速时就要花点时间了。不管有没有接收到加速计输入,在每一帧都把速度加到玩家位置上。
为什么不使用动作来移动玩家精灵呢?无论何时你需要频繁地——如每秒数次——改 变对象的速度或方向,使用 move 动作都不是一个好的选择。动作适用于相对使用期较长 的对象,所以频繁创建新对象在分配和释放内存上增加了额外开销,这会使游戏性能大幅 下降。
更糟糕的是,如果不为动作留出一点时间,动作是不会执行的。这就是在每帧添加新 动作来替换前一个却没有任何效果的原因。
-(void) accelerometer:(UIAccelerometer *) acceleromete didAccelerate:(UIAcceleration *)acceleration
{
float deceleration = 0.4f; // 减速值
float sensitivity = 6.0f; // 明感度
float maxVeloctiy = 100; // 最大速度
playerVelocity.x = playerVelocity.x * deceleration + acceleration.x *sensitivity;
// 新的玩家速度=原有速度*减速值+加速计*明感度值
CCLOG(@"playVelocity.x = %f",playerVelocity.x);
if(playerVelocity.x > maxVeloctiy)
{
playerVelocity.x = maxVeloctiy;
}
else if(playerVelocity.x < -maxVeloctiy)
{
playerVelocity.x = maxVeloctiy;
}
}
现在 playerVelocity 能够改变了,但如何把速度加到玩家精灵的位置值上呢?可以在 GameScene 的 init 方法中指定如下 update 方法:
–(void) update:(ccTime)delta
method to be called every frame [self scheduleUpdate];
同样需要添加“–(void) update:(ccTime)delta”方法
- (void)update:(ccTime)delta
{
CGPoint pos = player.position; // 得到主角当前位置pos
pos.x += playerVelocity.x; // 将pos的X值加上速度playerVelocity的值
CGSize screenSize = [[CCDirector sharedDirector] winSize]; // 得到设备尺寸
float imageWidthHalved = [player texture].contentSize.width * 0.5f; // 得到主角图片宽度的一半
float leftBorderLimit = imageWidthHalved; // 左边界
float rightBorderLimit = screenSize.width - imageWidthHalved; // 右边界
if(pos.x <leftBorderLimit) // 如果新位置小于左边界
{
pos.x = leftBorderLimit; // 新位置等于左边界
playerVelocity = CGPointZero; // 速度重置为0
}
else if(pos.x >rightBorderLimit) // 如果新位置大于右边界
{
pos.x = rightBorderLimit; // 新位置等于右边界
playerVelocity = CGPointZero; // 速度重置为0
}
player.position = pos; // 如果新位置没有超出范围,则将移动主角到新位置
}
添加障碍物
CCArray *spiders; // 蜘蛛
float spiderMoveDuration; //
int numSpidersMoved; // 已经移动过的蜘蛛数量
#pragma mark 初始化所有蜘蛛对象
- (void)initSpiders
{
CGSize screenSize = [[CCDirectorsharedDirector] winSize];
CCSprite *tempSpider = [CCSpritespriteWithFile:@"spider.png"]; // 由图片文件生产CCSprite蜘蛛对象
float imageWidth = [tempSpider texture].contentSize.width; // 得到蜘蛛对象图片宽度
int numSpiders = screenSize.width/imageWidth; // 由设备宽度/图片宽带得到可以容纳蜘蛛的数量
spiders = [[CCArray alloc] initWithCapacity:numSpiders]; // 生成相应的数组
for (int i = 0; i<numSpiders; i++)
{
CCSprite *spider = [CCSpritespriteWithFile:@"spider.png"]; // 循环生成每个蜘蛛的CCSprite
[self addChild:spider z:0 tag:2]; // 将蜘蛛放到场景中
[spiders addObject:spider]; // 将蜘蛛对象放到数组中
}
[self resetSpiders]; // 重置所有蜘蛛的位置
}
#pragma mark 重置所有蜘蛛对象的位置
- (void)resetSpiders
{
CGSize screenSize = [[CCDirectorsharedDirector] winSize];
int numSpiders = [spiders count]; // 得到现有蜘蛛数组中的个数
if(numSpiders>0)
{
CCSprite *tempSplider = [spiders lastObject]; // 取得最后一个蜘蛛对象
CGSize size = [tempSplider texture].contentSize; // 得到蜘蛛对象的尺寸
for (int i = 0; i<numSpiders; i++)
{
CCSprite *spider = [spiders objectAtIndex:i]; // 循环每个蜘蛛对象
spider.position = CGPointMake(size.width * i + size.width * 0.5f
, screenSize.height+size.height); // 将蜘蛛下移一个图片高度
[spider stopAllActions]; // 停止蜘蛛对象的所有动作
}
}
[self unschedule:@selector(spidersUpdate:)]; // 停止所有循环动作
[self schedule:@selector(spidersUpdate:) interval:0.7f]; // 重现启动所有循环动作,每隔0.7秒循环蜘蛛下落动作
numSpidersMoved = 0; // 重置移动过的蜘蛛数量为0
spiderMoveDuration = 4.0f; // ??????
}
#pragma mark 每隔0.7秒,随机挑选一个蜘蛛,检查它是否有空,如果是则更新它的位置
- (void)spidersUpdate:(ccTime)delta
{
for (int i = 0; i<10; i++)
{
// CCRANDOM_0_1() == ((random() / (float)0x7fffffff ))
float rf = CCRANDOM_0_1(); // 生成0~1之间的某个随机数
int randomSpiderIndex = rf*[spiders count];
CCLOG(@"random = %f,spiderCount = %d,randomSpiderIndex = %d",rf,[spiderscount],randomSpiderIndex);
CCSprite *spider = [spiders objectAtIndex:randomSpiderIndex]; // 根据随机数选取某只蜘蛛
if([spider numberOfRunningActions] == 0) // 判断这只蜘蛛是否在执行动作
{
[self runSpiderMoveSequence:spider]; // 如果这只蜘蛛没有动作,则让它执行动作
break; // 退出循环
}
}
}
#pragma mark 通过动作序列控制某个蜘蛛的运动
- (void)runSpiderMoveSequence:(CCSprite *)spider
{
numSpidersMoved ++; // 下落蜘蛛数加1
if(numSpidersMoved %8 == 0 && spiderMoveDuration > 2.0f)
// 每落下 8 个蜘蛛,spiderMove - Duration 就降低,从而增加所有蜘蛛的速度。
{
spiderMoveDuration -= 0.1f;
}
CGPoint belowScreenPosition = CGPointMake(spider.position.x
, -[spider texture].contentSize.height); // 下落一个蜘蛛位置
CCMoveTo *move = [CCMoveToactionWithDuration:spiderMoveDuration
position:belowScreenPosition]; // 下落动作
CCCallFuncN *call = [CCCallFuncNactionWithTarget:self
selector:@selector(spiderDidDrop:)];
CCSequence *sequence = [CCSequence actions:move,call,nil]; // 动作序列
[spider runAction:sequence]; // 执行动作序列
}
- (void)spiderDidDrop:(id)sender
{
NSAssert([sender isKindOfClass:[CCSpriteclass]],@"sender is not a CCSprite");
CCSprite *spider = (CCSprite *)sender;
CGPoint pos = spider.position;
CGSize screenSize = [[CCDirectorsharedDirector] winSize];
pos.y = screenSize.height + [spider texture].contentSize.height;
spider.position = pos;
}
#pragma mark 碰撞测试
-(void) checkForCollision
{
float playerImageSize = [player texture].contentSize.width; // 主角宽度
float spiderImageSize = [[spiders lastObject] texture].contentSize.width; // 蜘蛛宽度
float playerCollisionRadius = playerImageSize * 0.4f; // 主角半径
float spiderCollisionRadius = spiderImageSize * 0.4f; // 蜘蛛半径
float maxCollisionDistance = playerCollisionRadius + spiderCollisionRadius;
// 主角与蜘蛛之间的最大的安全距离
int numSpiders = [spiders count];
for (int i = 0; i < numSpiders; i++)
{
CCSprite* spider = [spidersobjectAtIndex:i];
if ([spider numberOfRunningActions] == 0) // 如果蜘蛛没有发生动作
{
continue;
}
float actualDistance = ccpDistance(player.position, spider.position); // 主角与蜘蛛之间的距离
if (actualDistance < maxCollisionDistance) // 如果主角与蜘蛛之间的距离小于安全距离,说明他们碰撞了
{
[self resetSpiders];
totalTime = 0;
score = 0;
[scoreLabelsetString:@"0"];
break;
}
}
}
CCLabelBMFont 简介
CCLabelBMFont 的特色就是以占用更多内存为代价加快标签的更新,这与其他任何 CCSprite 类相似。