cocos2D捕鱼达人源代码初学者详解4数字滚动

    数字滚动由两个类实现,数字类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值小的成员的上面;

    }

 

你可能感兴趣的:(cocos2d)