原文链接地址:http://www.iphonegametutorials.com/2010/09/14/cocos2d-sprite-tutorial-part-3/

  我们在第2部分教程中已经介绍了如何让dragon沿着8个不同的方向移动,并且播放相应的动画,同时,移动过程可以由用户touch屏幕来控制。cocos2d很酷吧!好了,今天我们将多干点活,我们将创建一大批村民--实际上是N个村民。我们会使用我们已经学习过的技术,从spritesheet里面加载精灵,同时建立相应的精灵动画。

  这里有本教程的完整源代码。

  那么,我们到底要做成什么样子呢---看了下面的图就明白了:

上面加载了好多村民,但是,屏幕的帧速率仍然是60 fps。这是因为我们做了优化。那么,究竟是如何做的呢?让我们马上开始学习吧。

首先,我们要创建我们的adventurer (冒险者)类。---它里面存储了我们的移动和行走动画的精灵实例。在屏幕上每一个冒险家,我们都会为之创建一个adventurer 类的实例。

Adventurer.h

#import "cocos2d.h"

@interface Adventurer : CCNode {CCSprite *_charSprite;CCAction *_walkAction;CCAction *_moveAction;BOOL _moving;}

@property (nonatomic, retain) CCSprite *charSprite;@property (nonatomic, retain) CCAction *walkAction;@property (nonatomic, retain) CCAction *moveAction;

@end

如果你愿意的话,你也可以从CCSprite继承,然后我们可以调用initWithFile方法来初始化我们的Adventure 类。但是,我更喜欢从CCNode继承,然后包含一个CCSprite的实例。

Adventurer.m

#import "Adventurer.h"

@implementation Adventurer

@synthesize charSprite = _charSprite;@synthesize moveAction = _moveAction;@synthesize walkAction = _walkAction;

-(id) init{self = [super init];if (!self) {return nil;}

return self;}

- (void) dealloc{self.charSprite = nil;self.walkAction = nil;self.moveAction = nil;[super dealloc];}

@end

很简单的init函数,同时我们还定义了一个dealloc方法。(译者:大家一定要养成一个好习惯,定义init就马上定义dealloc,“创建-销毁”要成对,这个很重要,能减少很多内存问题。stackoverflow上面,有人直接把dealloc方法放在.m文件的最开头,作用不言而喻!ios内存有限啊!)---上面这段代码,我们再熟悉不过了。这里创建了一个非常简单的类,但是,也给我们一些提示,如何为游戏主角创建class。这里把所有的属性都定义了retain说明符,同时在dealloc方法里面调了self.xxx = nil来释放内存。这样就把内存管理与property关联起来了。objc的引用计数已经为我们程序员减少了对于内存管理的烦恼,因此,只需要养成良好的习惯,就可以减少大量与内存有关的问题发生。

现在,我们拥有角色了,让我们来使用之。。。先回到“PlayLayer.h” ,然后做下面一些变更:

#import "cocos2d.h"

#import "SceneManager.h"#import "Adventurer.h"

@interface PlayLayer : CCLayer {CCTexture2D *_texture;CCSpriteSheet *_spriteSheet;

NSMutableArray *_charArray;}

@property (nonatomic, assign) CCTexture2D *texture;@property (nonatomic, assign) CCSpriteSheet *spriteSheet;

@property (nonatomic, retain) NSMutableArray *charArray;

@end

我们先导入 “Adventurer.h”,然后定义了3个实例变量。第一个变量 “_texture”用来加载adventurer 精灵表单。第二变量 “_spritesheet”是把我们将要创建的精灵都进行“批处理”,使之提高效率。最后,我们想要追踪所有的adventurers,所以,我们定义了一个“_charArray”.数组。同时我们为每一个实例变量都声明了属性,这样我们就可以在PlayLayer.m间接使用了。(另一种方法是定义tag,在init方法里面指定tag,然后在其它方法里面就可以用self getChildByTag:tag来获得想要的孩子了)

OK,现在我们有一堆类了。不过别担心,我们会在后面把它逐步分开讲解--首先,先让我们实现PlayLayer.m:

PlayLayer.m

#import "PlayLayer.h"#import "Adventurer.h"

@implementation PlayLayer

@synthesize texture = _texture;@synthesize spriteSheet = _spriteSheet;

@synthesize charArray = _charArray;

enum {kTagSpriteSheet = 1,};

-(id) init{self = [super init];if (!self) {return nil;}

CCSprite *background = [CCSprite spriteWithFile:@"Terrain.png"];background.position = ccp(160, 240);[self addChild:background];

_texture = [[CCTextureCache sharedTextureCache] addImage:@"adventurer.png"];_spriteSheet = [CCSpriteSheet spriteSheetWithTexture:self.texture capacity:100];[self addChild:_spriteSheet z:0 tag:kTagSpriteSheet];

self.charArray = [[NSMutableArray alloc] init]; 

[self schedule:@selector(gameLogic:) interval:1.0f];

return self;}

-(void)addAdventurer {

NSLog(@"Add Adventurer");

NSMutableArray *animFrames = [NSMutableArray array];

[animFrames removeAllObjects];

for (int i = 0; i < 9; i++) {CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:self.texture rect:CGRectMake(i*16, 0, 16, 29) offset:CGPointZero];[animFrames addObject:frame];}

Adventurer * adventurer = [[[Adventurer alloc] init] autorelease];if (adventurer != nil) {CCSpriteFrame *frame1 = [CCSpriteFrame frameWithTexture:self.texture rect:CGRectMake(0, 0, 19, 29) offset:CGPointZero];adventurer.charSprite = [CCSprite spriteWithSpriteFrame:frame1];

CGSize s = [[CCDirector sharedDirector] winSize];

int minY = adventurer.charSprite.contentSize.height/2;int maxY = s.height - adventurer.charSprite.contentSize.height/2;int rangeY = maxY - minY;int actualY = (arc4random() % rangeY) + minY;

int minX = -300;int maxX = 0;int rangeX = maxX - minX;int actualX = (arc4random() % rangeX) + minX;

adventurer.charSprite.position = ccp(actualX, actualY);

CCAnimation *animation = [CCAnimation animationWithName:@"walk" delay:0.2f frames:animFrames];CCAnimate *animate = [CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO];CCSequence *seq = [CCSequence actions: animate,nil];adventurer.walkAction = [CCRepeatForever actionWithAction: seq ]; 

id actionMove = [CCMoveTo actionWithDuration:10.0f position:ccp(s.width + 200,actualY)];id actionMoveDone = [CCCallFuncND actionWithTarget:self selector:@selector(spriteMoveFinished:data:)data:adventurer];adventurer.moveAction = [CCSequence actions:actionMove, actionMoveDone, nil];

[adventurer.charSprite runAction:adventurer.walkAction];[adventurer.charSprite runAction:adventurer.moveAction];

[self addChild:adventurer.charSprite];[_charArray addObject:adventurer];}

}

-(void)gameLogic:(ccTime)dt {

[self addAdventurer];}

-(void)spriteMoveFinished:(id)sender data:adv{Adventurer * adventurer = (Adventurer*)adv;

CGSize s = [[CCDirector sharedDirector] winSize];

int minY = adventurer.charSprite.contentSize.height/2;int maxY = s.height - adventurer.charSprite.contentSize.height/2;int rangeY = maxY - minY;int actualY = (arc4random() % rangeY) + minY;

int minX = -300;int maxX = 0;int rangeX = maxX - minX;int actualX = (arc4random() % rangeX) + minX;

adventurer.charSprite.position = ccp(actualX, actualY);

[adventurer stopAction:adventurer.moveAction];[adventurer.charSprite runAction:adventurer.moveAction];

}

- (void) dealloc{[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];[self.charArray removeAllObjects];

[super dealloc];}

@end

OK,千万别头疼--接下来我会一点点分解:

-(id) init{self = [super init];if (!self) {return nil;}

CCSprite *background = [CCSprite spriteWithFile:@"Terrain.png"];background.position = ccp(160, 240);[self addChild:background];

_texture = [[CCTextureCache sharedTextureCache] addImage:@"adventurer.png"];_spriteSheet = [CCSpriteSheet spriteSheetWithTexture:self.texture capacity:100];[self addChild:_spriteSheet z:0 tag:kTagSpriteSheet];

self.charArray = [[NSMutableArray alloc] init]; 

[self schedule:@selector(gameLogic:) interval:1.0f];

return self;}

好,首先看到“init”函数,它和我们之前的adventurer 类一样,先调super init,如果失败的话,就直接返回nil。然后我们添加了一张背景图片叫做"Terrain.png"并把它放置在屏幕的中心(因为我们知道图片的默认中心点anchorPoint是0.5,0.5)。然后直接把它加到当前层中。

接下来,我们初始化纹理和spritesheet--首先把"adventurer.png"加载到CCTexture2D变量中,然后使用spriteSheetWithTexture来建立一个精灵表单。(我们也可以用spriteSheetWithFile这个函数来建立spritesheet,但是,我想向你展示另外一种方法)。然后,我们把spritesheet加到CCLayer中。之后,我们所有的精灵,如果作为孩子加到spritesheet中的话,就可以得到“批处理”。

最后,我们初始化NSMutableArray ,同时触发一个回调函数gamelogic,它会每隔1秒钟回调一次。函数如下所示:

-(void)gameLogic:(ccTime)dt {

[self addAdventurer];}

我们将使用这个函数,每隔一秒钟创建一个新的adventurer 对象。

接下来,看看AddAventurer函数。这个函数不仅仅创建一个新的角色,同时还会使之移动并播放相应方向行走的动画。

-(void)addAdventurer {

NSLog(@"Add Adventurer");

NSMutableArray *animFrames = [NSMutableArray array];

[animFrames removeAllObjects];

for (int i = 0; i < 9; i++) {CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:self.texture rect:CGRectMake(i*16, 0, 16, 29) offset:CGPointZero];[animFrames addObject:frame];}

上面的代码我们之前已经见过了,我们只是为walking动画存储了9个动画帧(CCSpriteFrames) 。

Adventurer * adventurer = [[[Adventurer alloc] init] autorelease];if (adventurer != nil) {CCSpriteFrame *frame1 = [CCSpriteFrame frameWithTexture:self.texture rect:CGRectMake(0, 0, 19, 29) offset:CGPointZero];adventurer.charSprite = [CCSprite spriteWithSpriteFrame:frame1];

接下来,我们创建一个新的adventurer 实例,然后把charSprite成员初始化为第一个动画帧,调用的方法是spriteWithSpriteFrame。

int minY = adventurer.charSprite.contentSize.height/2;int maxY = s.height - adventurer.charSprite.contentSize.height/2;int rangeY = maxY - minY;int actualY = (arc4random() % rangeY) + minY;

int minX = -300;int maxX = 0;int rangeX = maxX - minX;int actualX = (arc4random() % rangeX) + minX;

adventurer.charSprite.position = ccp(actualX, actualY);

好了,即使我们的精灵按照粒子数量去增加,所有的精灵刚开始的位置都是在左下角,除非我们人为改变它们的位置。因此,上面的代码就是产生一个随机坐标,同时又要保证这个随机坐标在屏幕范围之内。然后把这个随机坐标点赋值给adventurer。

CCAnimation *animation = [CCAnimation animationWithName:@"walk" delay:0.2f frames:animFrames];CCAnimate *animate = [CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO];CCSequence *seq = [CCSequence actions: animate,nil];adventurer.walkAction = [CCRepeatForever actionWithAction: seq ]; 

id actionMove = [CCMoveTo actionWithDuration:10.0f position:ccp(s.width + 200,actualY)];id actionMoveDone = [CCCallFuncND actionWithTarget:self selector:@selector(spriteMoveFinished:data:)data:adventurer];adventurer.moveAction = [CCSequence actions:actionMove, actionMoveDone, nil];

[adventurer.charSprite runAction:adventurer.walkAction];[adventurer.charSprite runAction:adventurer.moveAction];

[self addChild:adventurer.charSprite];[_charArray addObject:adventurer];}

addAdventurer方法的最后一个部分就是处理角色在屏幕上面的行走和移动。我们把之前存储CCSpriteFrame 的animFrames拿过来,然后把它转换成动画。(每个动画帧之间的间隔是0.2秒,整个动画差不多就要2秒的时间来运行完)。然后我们把这个动画放到一个sequence 动作中(使用CCSequence 类),最后,我们使用CCRepeatForever创建walkAction,并把它赋值给adventurer。

我们现在已经可以让角色有行走的动画了,但是,我们还想让它实际移动。所以,我们需要创建另外一个action,叫做CCMoveTo 。并且使用CCSequence类把它与一个回调函数关联起来。当CCMoveTo结束的时候,就运行CCCallFuncND指定的回调函数。

-------------------
Side Note:
    如果你想指定带一个参数的函数,那么就使用CCCallFuncN--它会把CCSprite本身传递进去,通过sender参数传递:

id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)];

如果你不想让任何参数传递的话,就使用CCCallFunc函数。
-------------------

现在,我们还剩下一件事情没有涉及了,就是之前CCMove结束的时候,通过CCCallFuncND指定的回调函数,如下所示:

-(void)spriteMoveFinished:(id)sender data:adv{Adventurer * adventurer = (Adventurer*)adv;

CGSize s = [[CCDirector sharedDirector] winSize];

int minY = adventurer.charSprite.contentSize.height/2;int maxY = s.height - adventurer.charSprite.contentSize.height/2;int rangeY = maxY - minY;int actualY = (arc4random() % rangeY) + minY;

int minX = -300;int maxX = 0;int rangeX = maxX - minX;int actualX = (arc4random() % rangeX) + minX;

adventurer.charSprite.position = ccp(actualX, actualY);

[adventurer stopAction:adventurer.moveAction];[adventurer.charSprite runAction:adventurer.moveAction];

}

这里再重复解释上面的代码的话,就有点烦人了。简言之,就是在CCMoveTo结束之后,随机再生成一个x,y坐标,然后让advertuere移动到这个位置去,再开始行走的动画。

最后,别忘了我们的dealloc方法:

- (void) dealloc{[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];[self.charArray removeAllObjects];

[super dealloc];}

上面把不再使用的纹理全部移除,并且把数组里面的对象移除掉。一定不要忘了调用super dealoc方法!

如果大家发现教程有什么问题或者笔误,请在下方留言,让我知道,谢谢!

  如果你觉得还缺少了些什么,也请在下方留言。

下篇教程见!~

转载于:https://www.cnblogs.com/pengyingh/articles/2395962.html

(译)cocos2d精灵教程:第三部分相关推荐

  1. cocos2d精灵教程(三篇)

    这里,我们将要征服cocos2d里面的精灵.这个过程并不会像你想像中那么难,接下来的教程,我就会证明给你看. cocos2d精灵教程:第一部分 http://www.1000phone.net/thr ...

  2. python基础教程第三版-《Python基础教程第三版》原版中英文PDF+代码+袁国忠(译)...

    <Python基础教程第3版>整本书的结构安排还是比较明显的.先是基础知识和python的基本数据类型和语言特性介绍,然后是面向对象的编程.之后介绍python的标准库以及相关主题的编程( ...

  3. python基础教程第三版电子版百度云-《python基础教程第三版》高清版PDF免费下载...

    下载地址1:http://t.cn/EGxO1sW Python基础教程 第3版Python简明教程书籍 Python编程从入门到实践 灵程序设计丛书 <python基础教程第三版>高清版 ...

  4. python基础教程第三版电子版-《python基础教程第三版》PDF高清完整版-免费下载...

    <python基础教程第3版>高清PDF下载地址:http://t.cn/EGxO1sW Python基础教程 第3版Python简明教程书籍 Python编程从入门到实践 灵程序设计丛书 ...

  5. AI火爆干货最全整理!五套深度学习和算法学习教程和三套Python学习视频!!!限时无套路免费领取!...

    点击蓝色"AI专栏"关注我哟 选择"星标",重磅干货,第一时间送达 这是站长第 31 期免费送丰富宝贵的干货资源与教程 本期绝对是满满的干货! 获取更多资源请关 ...

  6. [ISUX译]iOS 9人机界面指南(三):iOS 技术

    [ISUX译]iOS 9人机界面指南(三):iOS 技术 UI规范 summer 2015-11-29 3247浏览 0评论 专为0基础小白量身打造的UI设计入门课程(ps,ai软件+图标技巧),在线 ...

  7. 这可能是最好的性能优化教程(三)

    这可能是最好的性能优化教程系列专栏 这可能是最好的性能优化教程(一) 这可能是最好的性能优化教程(二) 这可能是最好的性能优化教程(三) 前言 内存泄漏从来都是我们老生常谈的话题,无论是 Androi ...

  8. Blend4精选案例图解教程(三):一键拖拽

    原文:Blend4精选案例图解教程(三):一键拖拽 拖拽效果,常规实现方法是定义MoveLeftDwon.MoveLeftUp.MouseMove事件,在Blend的世界里,实现对象的拖拽,可以不写一 ...

  9. iOS 11开发教程(三)运行第一个iOS 11程序

    iOS 11开发教程(三)运行第一个iOS 11程序 运行iOS11程序 创建好项目之后,就可以运行这个项目中的程序了.单击运行按钮,如果程序没有任何问题的话,会看到如图1.6和1.7的运行效果. 图 ...

  10. 网摘精灵教程:网摘自动提交工具。

    网摘精灵教程:网摘自动提交工具. 尊敬的站长:你好. 我们注意到您的网站放置了很多网摘提交代码. 你希望通过提交网摘来获取流量,对吗? 靠这种原始的手工提交,效率很低,对吗? 如果有一种网摘提交工具自 ...

最新文章

  1. mysql text index_MySQL 全文索引(fulltext index)
  2. java 8 lambda表达式
  3. 对textfield的键盘改造成滚动选择器而不是输入键盘
  4. 《数据库SQL实战》查找所有员工的last_name和first_name以及对应的dept_name
  5. p批处理替换目录下文本中的字符串
  6. object转成实体对象_面向对象的TypeScript-序列化与反序列化(1)
  7. 前景检测算法 (GMM)
  8. webots使用以及第三方模型导入装配、运动学仿真教程
  9. 解决微信小程序图片上传点击无反应问题
  10. python进销存系统代码_继续进销存系统
  11. PID参数自整定库之一:继电反馈整定算法
  12. java生成条形码~~使用barcode4j在线生成条形码
  13. java咖啡杯_一次性咖啡杯的简史
  14. 单机塔防游戏推荐_十大塔防单机手游2019 简单好玩的单机塔防游戏推荐
  15. uniapp px转rpx
  16. 5、特征选择(filter):方差分析(ANOVA)
  17. 计算机映像缺失磁盘如何修复,电脑映像损坏怎么修复_windows提示损坏的映像怎么处理...
  18. C4D骨骼动画绑定到像素
  19. 20种梦幻摄影调色luts预设
  20. SpringBoot+Vue项目快速入门

热门文章

  1. Black Salt Audio All Plug-Ins Mac 实用音频压缩插件套装
  2. DRmare Audio Converter Mac使用指南 - DRM音频清除转换
  3. 这些超好用的 Mac 系统自带软件,来试试
  4. 使用CrossOver的Wine配置修改容器WIndows系统版本
  5. Linux 4.21优化Zen 2架构
  6. /usr/bin/ld: cannot find -lstdc++ -lz问题
  7. 磁盘IO单线程顺序写时最快的,如果多线程写,磁盘的磁头要不断重新寻址,所以写入速度反而会慢...
  8. dede 鼠标移到标题处显示完整标题
  9. 三元操作符(即条件表达式)
  10. I/O多路复用之select