首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Cocos2D开发教程:Caterpillar游戏的移动和飞弹

Cocos2D开发教程:Caterpillar游戏的移动和飞弹

Tut7做了一利用Cocos2D引擎来制作毛虫游戏的个系列教程,并提供了详细的代码。我们节选了其中关于移动和导弹的教程,希望能够为开发者带来帮助。Cocos2D是一个开源2D游戏框架,分别有Python版本和Objective-C版本。其衍生出一个跨平台版本叫做Cocos2D-x。如果你也在用Cocos2D-x开发的话,也可以到CSDN发起的跨平台开发工具调查中,投上一票,或者吐上一槽。


第一步:操控游戏角色
加入以下代码到GameLayer.m的init方法里:
  • [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
这行代码允许GmaiLayer陈为一个触控代理去接受触控通知。现在,我们家两个方法进去:
  • // 1
  • - (BOOL)ccTouchBeganUITouch *)touch withEventUIEvent *)event {
  •     return YES;
  • }

  • // 2
  • - (void)ccTouchMovedUITouch *)touch withEventUIEvent *)event {
  •     // 3
  •     CGPoint touchLocation = [self convertTouchToNodeSpace:touch];

  •     CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
  •     oldTouchLocation = [[CCDirector sharedDirector] convertToGLldTouchLocation];
  •     oldTouchLocation = [self convertToNodeSpaceldTouchLocation];

  •     // 4
  •     int xChange = touchLocation.x - oldTouchLocation.x;
  •     int yChange = touchLocation.y - oldTouchLocation.y;

  •     int newX = self.player.position.x + xChange;
  •     int newY = self.player.position.y + yChange;

  •     // 5
  •     іf(newX <
    kGameAreaStartX + kGameAreaWidth - kGridCellSize &&
  •        newX > kGameAreaStartX &&
  •        newY > kGameAreaStartY + kGridCellSize / 2 &&
  •        newY <
    kGameAreaStartY + (kGridCellSize * 3)) {

  •         __block BOOL collide = NO;
  •         CGPoint oldPosition = self.player.position;
  •         // 6
  •         self.player.position = ccp(newX,newY);

  •         // 7
  •         [self.sprouts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *ѕtοр) {
  •             Sprout *sprout = (Sprout *)obj;
  •             CGRect sproutRect = [sprout getBounds];
  •             CGRect playerRect = [self.player getBounds];

  •             іf(CGRectIntersectsRect(sproutRect, playerRect)) {
  •                 collide = YES;
  •                 *ѕtοр = YES;
  •             }

  •         }];

  •         // 8
  •         іf(collide) {
  •             self.player.position = oldPosition;
  •         }
  •     }
  • }
让我们对这些代码做一些注释:
  • 首先我们需要实现ccTouchesBegan方法,使得调用器知道我们正在响应触控操作。一旦遗漏程序就会崩溃。
  • 接下来,用户手指在屏幕上滑动时候调用ccTouchesMoved方法方法。
  • 获取当前触摸位置的坐标引用,和之前的坐标。
  • 获取新旧位置的变化,用来定义下一步游戏角色应该怎走多远。
  • 至此我们得到了一串方格来确保角色不会约出边界。
  • 更新角色的位置。
  • 检查是否角色会碰上障碍。我们需要严格限定角色的走位。
  • 最后,如果有碰撞,则回到上一个比较安全的位置。
第二步:飞弹
为了让角色和其他目标进行互动,游戏角色会以一个恒定的速率发射当前级别的飞弹。我们得先需要几个常数,在GameConfig.h里加上以下代码:
  • #define kMissileSpeed 1.0
  • #define kMissileMaxSpeed 10.0
  • #define kMissilesTotal 20
  • #define kMissileFrequency .6 //seconds
  • #define kMinMissileFrequency .2
现在创建一个GameObject subclass,命名为Missile,然后把以下代码加到Missile.h里:
  • #import "cocos2d.h"
  • #import "GameObject.h"

  • @interface Missile : GameObject
  • @property (nonatomic, assign) BOOL dirty;
  • - (void)updateccTime)dt;
  • @end
其中dirty属性会在将来代表飞散无法使用。可能由于游戏角色越出边界或者和其他游戏物体碰撞。因为飞弹永久失效,需要一阿update方法来做出一个动画效果。
现在往Missile.m里面加入以下代码:
  • #import "Missile.h"
  • #import "GameLayer.h"
  • #import "GameConfig.h"

  • @implementation Missile

  • @synthesize dirty = _dirty;

  • // 1
  • - (id)initWithGameLayerGameLayer *)layer {

  •     if(self == [super initWithGameLayer:layer]) {
  •         self.sprite = [CCSprite spriteWithSpriteFrameName"missile.png"];
  •     }

  •     return self;
  • }

  • - (void)updateccTime)dt {
  •     // 2
  •     int inc = kMissileSpeed * (self.gameLayer.level + 1.5);

  •     // 3
  •     if(inc > kMissileMaxSpeed) {
  •         inc = kMissileMaxSpeed;
  •     }

  •     // 4
  •     int y = self.position.y + inc;
  •     self.position = ccp(self.position.x,y);

  •     // 5
  •     if(self.position.y > kGameAreaStartY + kGameAreaHeight) {
  •         self.dirty = YES;
  •     }
  • }

  • @end
  • 这里的init方法和之相似,之负责创建飞弹。
  • 飞弹的移动速度。会根据当前级别进行加速。
  • 有时候飞弹的速度会失控,我们需要限制一个最大速度。
  • 这两行代码实际上是让飞弹超前运动,我们计算一个新飞弹的y值,同时更新位置。
  • 最后,如果飞弹碰撞到顶部边框(没有碰到障碍物),就设置成_dirty属性为真。我们会在GmaeLayer里使用垃圾回收机制将这些dirty的飞弹回收掉。
第三步:开火
通常当你有一大票的对象,比如飞弹这种,你绝对不想每次都重新分配。因此我们需要创建两个数组,一个用来hold住所有发出的飞弹(in play),另一个hold住还未发出的飞弹(pool)。最后我们把所有dirty属性的飞弹都放到pool数组里面。
将以下属性添加到GameLayer.h:
  • @property (nonatomic, retain) NSMutableArray *missilesWaiting;
  • @property (nonatomic, retain) NSMutableArray *missilesFiring;

  • 然后在GameLayer.m中import Missile.h,在init方法中加入以下代码:

  • // 1
  • _missilesWaiting = [[NSMutableArray alloc] initWithCapacity:kMissilesTotal];
  • _missilesFiring = [[NSMutableArray alloc] initWithCapacity:kMissilesTotal];
  • // 2
  • for(int x = 0; x < kMissilesTotal; x++) {
  •     Missile *missile = [[Missile alloc] initWithGameLayer:self];
  •     [self.missilesWaiting addObject:missile];
  •     [missile release];
  • }
  • 初始化每个飞弹数组。
  • 循环kMissilesTotal几次,创建足够多的飞弹。然后将它们添加到missilesWaiting数组。
然后跳转到update方法,添加以下代码:
  • // 1
  • static
    float missleFireCount = 0;

  • - (void)updateccTime)dt {

  •     // ...Caterpillar code...

  •     // 2
  •     float frequency = kMinMissileFrequency;
  •     if(kMissileFrequency / (self.level * 1.25) > kMinMissileFrequency) {
  •         frequency = kMissileFrequency / self.level;
  •     }

  •     // 3
  •     if(missleFireCount < frequency) {
  •         missleFireCount += dt;
  •     } else {
  •         missleFireCount = 0;
  •         // 4
  •         if([self.missilesWaiting count] > 0) {
  •             Missile *missile = [self.missilesWaiting objectAtIndex:0];
  •             [self.missilesFiring addObject:missile];
  •             [self.missilesWaiting removeObjectAtIndex:0];
  •             missile.position = self.player.position;
  •             [self.spritesBatchNode addChild:missile.sprite];

  •         }
  •     }

  •     // 5
  •     __block Missile *dirty = nil;
  •     [self.missilesFiring enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  •         Missile *missile = (Missile *)obj;
  •         [missile update:dt];
  •         if(missile.dirty) {
  •             dirty = missile;
  •             *stop = YES;
  •         }
  •     }];

  •     // 6
  •     if(dirty) {
  •         dirty.dirty = NO;
  •         [self.missilesWaiting addObject:dirty];
  •         [self.missilesFiring removeObject:dirty];
  •         [self.spritesBatchNode removeChild:dirty.sprite cleanup:NO];
  •     }
  • }
  • 我们需要创建一个静态计数器。
  • 计算飞弹开火频率,不同级别有不同的开火频率。
  • 如果频率更快,或者达到的级别,就释放一种新的飞弹。
  • 把一个飞弹拉出pool的数组,把它加到正在开火的数组中。
  • 枚举所有的飞弹,检查是否有dirty属性的飞弹。一旦发现,标记它以便移回pool的数组里。
  • 如果有dirty属性的飞弹,从pool数组里移动到正在开火的数组同时从batch node中移除sprite(精灵)
最后实现的效果。

返回列表