更难的怪物和更多的关卡:如何使用Cocos2D制作一款简单的iphone游戏第3部分

Ray Wenderlich

这篇文章还可以在这里找到 英语, 俄语, 西班牙语

Watch out for the green guy!

小心那个绿色的家伙!

到目前为止,我们制作的游戏如何使用Cocos2D制作一款简单的iphone游戏相当cool了。我们已经有了旋转的炮台,用来射击的鬼头靶子,还有很棒的音效。

但是,我们的游戏略显简单了。鬼头靶子只能挨一下飞镖,就挂了,而且游戏只有一关。我们甚至还没热身呢游戏就结束了。

在本篇教学中,我们将扩展我们的工程,加入不同难度的鬼头靶子,并在游戏中实现多关卡。

更难的鬼头靶子Tougher Monsters

为了有趣,我们创建两种类型的鬼头靶子:一种脆弱但是移动速度快的,和一种强壮但是速度慢的。为了让玩家很容易区分这两种,下载修改过的鬼头资源 并将其加入你的工程中。同时,下载我制作的爆炸音效并将其也加入到工程中去。

现在让我们创建Monster类。有很多方法可以模型化Monster类,但这里我们使用最简单的,就是作为CCSprite的子类。我们还将会创建两个Monster的子类:一个代表脆弱快速的鬼头靶子,另一种则代表强壮慢速的。

选择FileNew File,选择Cocoa Touch ClassObjective-C class,确保Subclass of NSObject是选中的,点击Next。把文件命名为Monster.m并确保”Also create Monster.h” 是选中的。

用以下内容替换Monster.h:

#import "cocos2d.h"
 
@interface Monster : CCSprite {
    int _curHp;
    int _minMoveDuration;
    int _maxMoveDuration;
}
 
@property (nonatomic, assign) int hp;
@property (nonatomic, assign) int minMoveDuration;
@property (nonatomic, assign) int maxMoveDuration;
 
@end
 
@interface WeakAndFastMonster : Monster {
}
+(id)monster;
@end
 
@interface StrongAndSlowMonster : Monster {
}
+(id)monster;
@end

这里直截了当:我们让Monster继承自CCSprite并加了几个成员变量,用来记录鬼头靶子的状态,然后又从Monster派生了两个类,用来代表两种不同类型的鬼头靶子。

打开Monster.m并在implementation块儿中加入如下内容:

#import "Monster.h"
 
@implementation Monster
 
@synthesize hp = _curHp;
@synthesize minMoveDuration = _minMoveDuration;
@synthesize maxMoveDuration = _maxMoveDuration;
 
@end
 
@implementation WeakAndFastMonster
 
+ (id)monster {
 
    WeakAndFastMonster *monster = nil;
    if ((monster = [[[super alloc] initWithFile:@"Target.png"] autorelease])) {
        monster.hp = 1;
        monster.minMoveDuration = 3;
        monster.maxMoveDuration = 5;
    }
    return monster;
 
}
 
@end
 
@implementation StrongAndSlowMonster
 
+ (id)monster {
 
    StrongAndSlowMonster *monster = nil;
    if ((monster = [[[super alloc] initWithFile:@"Target2.png"] autorelease])) {
        monster.hp = 3;
        monster.minMoveDuration = 6;
        monster.maxMoveDuration = 12;
    }
    return monster;
 
}
 
@end

这里实际有用的代码只是在两个类中分别添加了一个static方法,用来设置默认的HP和移动时间。

现在让我们把新的Monster类加入到除此之外的代码中去!首先导入新文件到HelloWorldScene.m的一开头:

#import "Monster.h"

然后,修改addTarget 方法,替换掉以前直接创建sprite的方法,使其构造我们新的类。用以下内容替换掉spriteWithFile这一行:

//CCSprite *target = [CCSprite spriteWithFile:@"Target.png" rect:CGRectMake(0, 0, 27, 40)]; 
Monster *target = nil;
if ((arc4random() % 2) == 0) {
    target = [WeakAndFastMonster monster];
} else {
    target = [StrongAndSlowMonster monster];
}

这里会有50%的几率会出现两种鬼头靶子中的一种。另外,因为我们把靶子移动速度这个变量移到了类中,所以要如下修改min/max duration这一行:

int minDuration = target.minMoveDuration; //2.0;
int maxDuration = target.maxMoveDuration; //4.0;

最后一步,要对update方法做些修改。首先在targetsToDelete声明之前加入一个boolean。

BOOL monsterHit = FALSE;

然后,在CGRectIntersectsRect判断块儿中,不要立刻就将对象加入到targetsToDelete,用以下内容替换之:

//[targetsToDelete addObject:target];	
monsterHit = TRUE;
Monster *monster = (Monster *)target;
monster.hp--;
if (monster.hp <= 0) {
    [targetsToDelete addObject:target];
}
break;

我们不再立刻杀掉鬼头靶子了,而是减少其一HP,当血量为0或比0小时,我们才摧毁它。另外注意一下,每当飞镖碰到一个鬼头靶子时我们就退出循环了,意味着飞镖每次只能打击一个鬼头靶子。

最后,如下修改projectilesToDelete判断块儿:

if (monsterHit) {
    [projectilesToDelete addObject:projectile];
    [[SimpleAudioEngine sharedEngine] playEffect:@"explosion.caf"];
}

编译并运行,如果一切顺利你会看到两种不同类型的鬼头靶子穿梭在屏幕中,我们的小炮台终于棋逢对手了!

Screenshot of different monster types

多个关卡Multiple Levels

为了实现多关卡,我们首先需要做一些代码重构。这些重构都很简单数量却很多,如果我在本篇里将这些重构一五一十的罗列出来,那么本篇将会非常冗长,无趣。

相反的,我会罗列此部分的概要,你可以在示例工程里找到所有你需要的细节。

抽象出一个关卡类。当前,HelloWorldScene里边有关“level”的内容全部是写死的,包括出现哪种和多块速度的鬼头靶子等等。所以第一步就是把以上相关内容剥离出来并加入到一个Level类中,这样我们就可以在HelloWorldScene的多关卡任务中重复使用它了。

重复利用scenes。 当前,每次切换场景的时候,我们都会新创建一个场景的实例。这样做有个缺点,由于没有做精心的内存管理,每当在init方法里加载资源的时候都会卡一下。

因为我们的游戏很简单,所以只需对每个scene创建一个实例,需要重置状态时只需调用reset方法即可(比如之前关卡中的鬼头靶子和飞镖)。

使用app delegate作为切换状态的控制台。 当前,我们并没有用来指示目前所在关卡的全局状态,而且每个场景应该切换到哪里去都是写死的。

我们修改下App Delegate,使其存储一个指示全局状态(比如关卡信息)的指针,因为App Delegate是一个容易被所有场景访问到的中心位置。我们还在其中加入了一些方法,用来集中切换场景的逻辑,同时减少场景内部和之间的依赖。

以上就是重构的主旨了,示例工程中可以查看到所有的细节。 注意这只是很多方法中的一种而已,如果你有更cool的组织场景和游戏对象的方法,请分享出来哦!

先别管别的,下载了代码试验一下再说!我们的游戏有了一个好的开始,包含一个旋转的小炮台,众多可以被射杀的有不同属性的敌人,多个关卡,胜利/失败场景,当然-还有绝妙的音效! ;]

就到这里!That’s A Wrap!

同样的,如果你还没下载,这里是到目前为止所有内容的示例工程

现在,你知道如何制作一款简单的游戏了,为什么不更进一步,学习如何使用Cocos2D制作一款基于tile的游戏! 毕竟,谁不喜欢忍者吃西瓜呢?

我希望你享受这个学习的过程,祝你好运!

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

用户评论

0 Comment

Other Items of Interest

Ray 的每月简报

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    加载中 ... 加载中 ...

我们的书

Our Team

教程组

  • Matt Galloway

... 49 total!

Update Team

Editorial Team

  • Matt Galloway

... 23 total!

Code Team

  • Orta Therox

... 3 total!

翻译团队

  • Jiyeon Seo
  • Przemysław Rembelski

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!