数字滚动由两个类实现,数字类UINumber和数字滚动类UIRollNum,都是继承的CCSprite,都是精灵。数字类实现单个数字的上下滚动,数字滚动类实现了界面分数数字的位置设置,分数变化时把分数拆解成单个数字,然后调用数字类赋值,由数字类判断滚动方向,并连续改变精灵帧坐标,实现数字滚动效果。
数字类详解
数字类成员变量:
NumStyle m_style; //滚动样式
int m_num; //显示的数字
int m_nPosCur; //当前的位置
int m_nPosEnd; //结束的位置
int m_nMoveLen; //每次移动的位置
CCTexture2D *m_texture; //数字的texture
数字类的实现方法:
-(id) initWithStyle:(NumStyle) style;//按照格式初始化
-(void) setNumber:(int) num;//设置分数数字,为一位数字
-(void) onRollDown:(ccTime) dt;//数字向上帧向下滚动
-(void) onRollUP:(ccTime) dt;//数字向下帧向上滚动
-(void) setup;//获取数字图片生成精灵帧
数字类初始化首先设置数字成员都为0,贴图为NULL,然后-(void) setup获取贴图,过程如下:
UIImage *image = [UIImage imageNamed:@"number.png"];
// 用UIImage加载图像的方法很多,最常用的是下面两种:
// 1、用imageNamed函数
// [UIImage imageNamed:ImageName];
// 2、用imageWithContentFile的方式加载,例如:
// 1. NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:extension];
// 2. UIImage *image = [UIImage imageWithContentOfFile:filePath];
// 由于第一种方式要写的代码比较少,可能比较多人利用imageNamed的方式加载图像。其实这两种加载方式都有各自的特点。
// 1)用imageNamed的方式加载时,系统会把图像Cache到内存。如果图像比较大,或者图像比较多,用这种方式会消耗很大的内存,而且释放图像的内存是一件相对来说比较麻烦的事情。例如:如果利用imageNamed的方式加载图像到一个动态数组NSMutableArray,然后将将数组赋予一个UIView的对象的animationImages进行逐帧动画,那么这将会很有可能造成内存泄露。并且释放图像所占据的内存也不会那么简单。但是利用imageNamed加载图像也有自己的优势。对于同一个图像系统只会把它Cache到内存一次,这对于图像的重复利用是非常有优势的。例如:你需要在一个TableView里重复加载同样一个图标,那么用imageNamed加载图像,系统会把那个图标Cache到内存,在Table里每次利用那个图像的时候,只会把图片指针指向同一块内存。这种情况使用imageNamed加载图像就会变得非常有效。
// 2)利用NSData方式加载时,图像会被系统以数据方式加载到程序。当你不需要重用该图像,或者你需要将图像以数据方式存储到数据库,又或者你要通过网络下载一个很大的图像时,请尽量使用imageWithData的方式加载图像。无论用哪种方式加载图像,图像使用结束后,一定要记得显示释放内存。
// + (UIImage *)imageNamed:(NSString *)name导致的内存问题
// 这种方法在application bundle的顶层文件夹寻找名字的图象 , 如果找到图片, 系统缓存图象。图片内容被加载到系统内存中,使用时直接引用到系统内存。
// 所以当图片比较大时,程序使用的内存会迅速上升导致内存警告并退出。
// 特别在使用Interface Builder建立界面时,如果直接拖动UIImageView 并设置image的图片名称。InterfaceBuilder 正是通过UIImage 类的imageName方法加载图片。图片被缓存,导致内存使用较大。且无法释放,即使release掉 UIImageView也无济于事。
m_texture = [[CCTexture2D alloc]initWithImage:image]; //CCTexture2D表示精灵包含的图片,范围是整张图片
CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:m_texture rect:CGRectMake(0, 0, NUM_WIDTH, NUM_HEIGHT)];
// CCSPriteFrame是帧,它是组成动画的一部 分,CCSPriteFrame的成员变量中有一个Texture,用帧创建精灵,其实就是用帧的Texture创建精灵,当精灵切换帧 时,Texture也会随之更换,精灵就会呈现不同的形态,以次达到动画的效果。这里大家可以理解为,Texture和Frame都可以构成一个精灵对象。
然后用setNumber 设置显示的数字
m_nPosCur = NUM_HEIGHT * m_num;//当前显示单个数字的位置为数值乘以高度
m_nPosEnd = NUM_HEIGHT * num;//需要更换的单个数字的位置为数值乘以高度
if (NumStyleNormal == m_style) {//根据格式判断移动速度。
m_nMoveLen = 4;//数字图片精灵每次移动距离,数值越大移动速度越快。
}
if (m_num > num) {//如果当前数字大于更换数字就向上滚动,例如当前数字5,更换为数字1,按图片的滚动方向为向下滚动,那怎么理解呢?图片向下滚动是通过帧向上移动实现的。
[self schedule:@selector(onRollUP:) interval:0.03]; //每0.03秒执行一次onRollUP方法
// Schedule是cocos2d中的一个很重要的机制,它的功能和CCAction有相似的地方,都是执行一种行为;不同之处在于,schedule是通过调用方法来执行逻辑的,而且他它内部还有个计时器,可以每隔一定的时间调用一次,反复循环,直到手动将其停止或者节点被删除(暂停)。这里的参数selector就是执行的方法,interval是间隔时间(秒),
// cocos2d中的schedule有两种作用
// 1)定时执行方法,例如每隔3秒钟执行一次方法fire
// 看例子:
// - (id) init{
// if((self = [super init])){
// [game addChild:self]
// [self schedule:@selector(fire) interval:3];
// }
// return self;
// }
// - (void) fire{
// NSLog(@"fire");
// }
// 2)延时执行方法,例如5秒种后执行方法destory
// 看例子:
// - (id) init{
// if((self = [super init])){
// [game addChild:self]
// [self schedule:@selector(destory) interval:5];
// }
// return self;
// }
// - (void) destory{
// NSLog(@"destory");
// [self unschedule:@selector(destory)];//解除调度
// [self.parent removeChild:self cleanup:YES];
// }
}
else {
[self schedule:@selector(onRollDown:) interval:0.03];//每0.03秒执行一次onRollDown方法
// @selector是什么
// 一种类型 SEL
// 代表你要发送的消息(方法), 跟字符串有点像, 也可以互转.
// 是能让Objective-C动态调用方法的玩意.
// 可以理解为类似函数指针的东西
// 其实就是消息响应函数
// 选一个消息响应的函数地址给你的action
// @selector(function_name) 即取得一个function的id
// 函数指针 本质上讲, 是 object-c 的动态后绑定技术 可以通过字符串 访问的函数指针
// @selector(xxxx)的作用是找到名字为xxxx的方法。
// 一般用于[a performSelector:@selector(b)];就是说去调用a对象的b方法,和[a b];的意思一样,但是这样更加动态一些。
// 返回的类型是SEL,看方法说明的时候如果参数类型是SEL,那么就是要接受@selector(xxxx)返回的值的那种了。
// 可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取.
onRollDown 帧向下滚动图片向上滚动
m_nPosCur += m_nMoveLen;//当前y坐标加每次移动长度
if (m_nPosCur >= m_nPosEnd) {//如果移动到新位置就结束
m_nPosCur = m_nPosEnd;//更改当前位置为新位置
[self unschedule:@selector(onRollDown:)];//解除选择器方法
CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:m_texture rect:CGRectMake(0, m_nPosCur, NUM_WIDTH, NUM_HEIGHT)];//使帧坐标下移则图片上移
// 精灵在创建之后,它的contentSize便不会再发生变化。CCAnimate的实现通过setDisplayFrame来实现,setDisplayFrame通过setTexture来实现的, 仅仅是添加了修改rect的相应操作。所以,通过CCAnimate和setDisplayFrame可以修改精灵的contentSize,通过setTexture不能修改对应的contentSize
}
onRollUP 帧向上滚动图片向下滚动
m_nPosCur -= m_nMoveLen;//当前y坐标减每次移动长度
if (m_nPosCur <= m_nPosEnd) {//如果移动到新位置就结束
m_nPosCur = m_nPosEnd;//更改当前位置为新位置
[self unschedule:@selector(onRollUP:)];
CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:m_texture rect:CGRectMake(0, m_nPosCur, NUM_WIDTH, NUM_HEIGHT)];
数字滚动类主要实现数字在界面的位置布局,及改变总分数的数值。
成员变量:
int m_nNumber; //显示的数字
int m_maxCol; //最大显示位数
// NSMutableArray可变对象数组
// [NSMutableArray arrayWithCapacity:6] :初始化可变数组对象的长度,如果后面代码继续添加数组超过长度6以后NSMutableArray的长度会自动扩充,6是自己可以设置的颗粒度。
// [array addObject:...] : 向可变数组尾部添加数据对象。
// [array addObjectsFromArray:..] :向可变数组尾部添加一个数组对象。
// NSArray 不可变数组
// [array count] : 数组的长度。
// [array objectAtIndex 0]: 传入数组脚标的id 得到数据对象。
// [arrayWithObjects; ...] :向数组对象初始化赋值。这里可以写任意对象的指针,结尾必须使用nil。
NSMutableArray *numArray; //存放每个数字的数组
CGPoint m_point; //坐标
bool zeroFill; //是否开启0填充
NumStyle style; //滚动样式
实现方法如下:
-(void) rebuildEffect;//更新分数数字
-(void) clearEffect;//初始化分数数字,全部为0
-(int) getNumber;//获得当前显示分数数字
-(void) setNumber:(int)num;//设置新分数数字
rebuildEffect 重新设置每位数字
// while语句的原型是while(表达式)语句,当表达式为非0值时,执行while语句中的嵌套语句。
// 那么while(1)其中1代表一个常量表达式,他永远不会等于0。所以,循环会一直执行下去。除非你设置break等类似的跳出循环语句循环才会中止
while (1) {
if (num<=0) {//如果现实的数字是0
if(m_maxCol<=i && zeroFill)//判断是否是最后数字,如果是就退出while()循环
break;
}
int showNum = num%10;//num%10则取出当前十进制位数的值,如9987,则获取的数字为7.
UINumber* pNumber = [numArray objectAtIndex:i];
[pNumber setNumber:showNum];//设置数字产生滚动
i++;
num = num/10;//当前数字除以10,把已经设置完毕的数字去掉,继续循环设置下一位数字。
clearEffect初始化每位数字
[self removeChild:pNumber cleanup:YES];//如果有精灵,就循环全部删除
[numArray removeAllObjects];//把精灵数组清空
for (int i=0; i< m_maxCol; i++) {//生成6个图片精灵
UINumber* pNumber = [[UINumber alloc]initWithStyle:style];
[numArray addObject:pNumber];
[pNumber setNumber:0];
[pNumber setPosition:CGPointMake(m_point.x-i*NUM_WIDTH, m_point.y)];//依次向左边显示
[pNumber setAnchorPoint:CGPointMake(1, 0.5)];//图片setPosition时的点在图片上所处的位置.取值范围0-1,0,0表示左下角,1,1表示右上角,0.5,0.5表示中心点.
[self addChild:pNumber z:100];//z:是指添加的ZOrder值,ZOrder是指该成员的层级(也可以说深度),z值大的成员在z值小的成员的上面;
}