(译)cocos2d精灵教程:第三部分
原文链接地址: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精灵教程:第三部分相关推荐
- cocos2d精灵教程(三篇)
这里,我们将要征服cocos2d里面的精灵.这个过程并不会像你想像中那么难,接下来的教程,我就会证明给你看. cocos2d精灵教程:第一部分 http://www.1000phone.net/thr ...
- python基础教程第三版-《Python基础教程第三版》原版中英文PDF+代码+袁国忠(译)...
<Python基础教程第3版>整本书的结构安排还是比较明显的.先是基础知识和python的基本数据类型和语言特性介绍,然后是面向对象的编程.之后介绍python的标准库以及相关主题的编程( ...
- python基础教程第三版电子版百度云-《python基础教程第三版》高清版PDF免费下载...
下载地址1:http://t.cn/EGxO1sW Python基础教程 第3版Python简明教程书籍 Python编程从入门到实践 灵程序设计丛书 <python基础教程第三版>高清版 ...
- python基础教程第三版电子版-《python基础教程第三版》PDF高清完整版-免费下载...
<python基础教程第3版>高清PDF下载地址:http://t.cn/EGxO1sW Python基础教程 第3版Python简明教程书籍 Python编程从入门到实践 灵程序设计丛书 ...
- AI火爆干货最全整理!五套深度学习和算法学习教程和三套Python学习视频!!!限时无套路免费领取!...
点击蓝色"AI专栏"关注我哟 选择"星标",重磅干货,第一时间送达 这是站长第 31 期免费送丰富宝贵的干货资源与教程 本期绝对是满满的干货! 获取更多资源请关 ...
- [ISUX译]iOS 9人机界面指南(三):iOS 技术
[ISUX译]iOS 9人机界面指南(三):iOS 技术 UI规范 summer 2015-11-29 3247浏览 0评论 专为0基础小白量身打造的UI设计入门课程(ps,ai软件+图标技巧),在线 ...
- 这可能是最好的性能优化教程(三)
这可能是最好的性能优化教程系列专栏 这可能是最好的性能优化教程(一) 这可能是最好的性能优化教程(二) 这可能是最好的性能优化教程(三) 前言 内存泄漏从来都是我们老生常谈的话题,无论是 Androi ...
- Blend4精选案例图解教程(三):一键拖拽
原文:Blend4精选案例图解教程(三):一键拖拽 拖拽效果,常规实现方法是定义MoveLeftDwon.MoveLeftUp.MouseMove事件,在Blend的世界里,实现对象的拖拽,可以不写一 ...
- iOS 11开发教程(三)运行第一个iOS 11程序
iOS 11开发教程(三)运行第一个iOS 11程序 运行iOS11程序 创建好项目之后,就可以运行这个项目中的程序了.单击运行按钮,如果程序没有任何问题的话,会看到如图1.6和1.7的运行效果. 图 ...
- 网摘精灵教程:网摘自动提交工具。
网摘精灵教程:网摘自动提交工具. 尊敬的站长:你好. 我们注意到您的网站放置了很多网摘提交代码. 你希望通过提交网摘来获取流量,对吗? 靠这种原始的手工提交,效率很低,对吗? 如果有一种网摘提交工具自 ...
最新文章
- mysql text index_MySQL 全文索引(fulltext index)
- java 8 lambda表达式
- 对textfield的键盘改造成滚动选择器而不是输入键盘
- 《数据库SQL实战》查找所有员工的last_name和first_name以及对应的dept_name
- p批处理替换目录下文本中的字符串
- object转成实体对象_面向对象的TypeScript-序列化与反序列化(1)
- 前景检测算法 (GMM)
- webots使用以及第三方模型导入装配、运动学仿真教程
- 解决微信小程序图片上传点击无反应问题
- python进销存系统代码_继续进销存系统
- PID参数自整定库之一:继电反馈整定算法
- java生成条形码~~使用barcode4j在线生成条形码
- java咖啡杯_一次性咖啡杯的简史
- 单机塔防游戏推荐_十大塔防单机手游2019 简单好玩的单机塔防游戏推荐
- uniapp px转rpx
- 5、特征选择(filter):方差分析(ANOVA)
- 计算机映像缺失磁盘如何修复,电脑映像损坏怎么修复_windows提示损坏的映像怎么处理...
- C4D骨骼动画绑定到像素
- 20种梦幻摄影调色luts预设
- SpringBoot+Vue项目快速入门
热门文章
- Black Salt Audio All Plug-Ins Mac 实用音频压缩插件套装
- DRmare Audio Converter Mac使用指南 - DRM音频清除转换
- 这些超好用的 Mac 系统自带软件,来试试
- 使用CrossOver的Wine配置修改容器WIndows系统版本
- Linux 4.21优化Zen 2架构
- /usr/bin/ld: cannot find -lstdc++ -lz问题
- 磁盘IO单线程顺序写时最快的,如果多线程写,磁盘的磁头要不断重新寻址,所以写入速度反而会慢...
- dede 鼠标移到标题处显示完整标题
- 三元操作符(即条件表达式)
- I/O多路复用之select