3.1菜单按钮
菜单CCMenu只是提供了一个可以点击的层,本质就是一个容器,用来装菜单项,CCMenuItem是所有菜单项的父类,有下面5个子类
3.1.1CCMenuItemFont
CCMenuItem*font=CCMenuItemFont::create("Start",this,menu_selector(T11Menu::menuCallBack));
3.1.2CCMenuItemImage
CCMenuItem*imageItem=CCMenuItemImage::create("CloseNormal.png","CloseSelected.png");
3.1.3CCMenuItemLabel
CCMenuItem*labelItem=CCMenuItemLabel::create(bm,menu_selector(T11Menu::menuCallBack));
3.1.4CCMenuItemSprite
CCMenuItemSprite*spriteItem=CCMenuItemSprite::create(normal,selected,menu_selector(T11Menu::menuCallBack));
3.1.5CCMenuItemToggle
CCMenuItemToggle*toggle=CCMenuItemToggle::createWithTarget(this,menu_selector(T11Menu::menuCallBack),t1,t2,t3,t4,NULL);
intidx=((CCMenuItemToggle*)item)->getSelectedIndex();//获取哪个菜单被选中
CCMenu和CCMenuItem的锚点都是(0.5,0.5),menu的坐标原点是中心点。为了和其余的一致,应该将坐标原点设置为左下角,menu在层上面封装了很多函数。菜单项的原点是坐标的窗口的中心,多个菜单项时,使用这个方法alignItemsVertically让菜单项自动对齐,也可以自己来设置item的位置,alignItemsVerticallyWithPadding也可以自己设置中间的大小。另外一种menu是CCMenuIteToggle,点击的时候实现菜单项的转换,比如开关,在构造之前需要先构造一个CCArray数组。
将在菜单项添加到menu层上,点击一下字会变大,是CCMenu内部实现的。CCMenu的父类是CCLayer,本身也是一个层,并且定义了触摸功能,并且优先级是-128,最高的优先级,其他的层不会捕获消息。当menu层的优先级一样的时候,最上层捕获消息。
菜单可以设置回调函数setTarget,就是当鼠标点击的时候如何处理,回调函数在转换的时候用到了一个宏menu_selector,使代码简洁。菜单的响应是在鼠标按下之后弹起的时候调用的,在内部调用active来执行。如果希望在按下的时候就调用,这个时候可以重定义一个类,继承自CCMenu,然后让这个类,重写一个触摸函数,在CCTouchBegan的时候就处理触摸,就是在里面添加active方法即可。
3.2课程菜单的制作
增加一个菜单Menu类,在这里类里面增加多个菜单项,在回调函数中处理,切换进入不同的场景。实现拖拽功能,首先注册触摸,在CCTouchmoved函数中添加下面的代码,其实就是设置y轴坐标
menu->setPositionY(menu->getPositionY()+pTouch->getDelta().y);
但是这样的话可以无限的拖拽,应该在最上端和最下端menu进入窗口的时候,停止拖拽,CCMenuItem*)menu->getChildren()->objectAtIndex(0)。实现返回按钮,可以添加一个Back类,然后让每个类来继承使用,这个类只有一个menu,返回到初始位置。但是,这样改起来很麻烦,可以在切换场景的时候,在场景上面添加一个menu,这样就不需要每个都改了。
replaceScene切换的时候,旧的scene内存会释放。
教学程序的课程更改,加一个新的类,首先将其余的头文件加载过来,然后创建一个字符串数组,包含这些头文件,然后创建一个menu,在这个menu上面添加多个menuitem。然后给这个menu实现触摸的功能,这样就可以拖动了。在moved的时候移动菜单,getDelta获取偏移的距离,然后重新设置menu的位置即可。不希望无限向上向下触摸。第一个菜单项位于了窗口就不再向上触摸,最后一个菜单位于窗口也就不再向下触摸。取出第一个的boundingBox,然后比较Y轴的坐标是否在窗口内。这个时候注意坐标体系的转化,用的谁的坐标体系就使用谁来调转化函数。出来的时候显示最上面的是T01,而不是放在中间,将菜单下移。在初始化的时候,找到偏移量,然后重新设置menu就可以了。下拉的时候有时候会漏出一些黑色,是因为帧率比较卡。
这个时候就设好了,应该给每个menuitem设置一个回调函数,在这里面切换场景。获得node的tag之后,使用switch来处理,创建一个layer,然后加到场景中去。使用replaceScene来切换,启动runwithScene。pushScene和popScene是一对,用栈来维护,切换的时候不会释放内存,而且场景状态也会保留。这两个函数不好操作,因为耗费内存。很少使用,帮助和音量偶尔用到。这个时候给每一scene场景加一个按钮,一种方法是创建一个类,这个类具有返回的功能,让每一个类都继承它,但是加起来很复杂,需要处理每个添加的类。给这个item注册一个函数。将其设置在右下角。这个时候有个同学提了一个意见,在之前创建的scene上面加一个层即可,这样处理起来就简单多了。
切换的特效,CCTransition类都是特效类,第一个参数是时间,第二个参数是切换到那个场景。切换的特效类大概有38个。CCTransitionFade淡入淡出,CCTransitionPageTurn翻页,给场景添加一个背景会清楚一点。CCTransition的原理是在场景切换的时候,旧的场景不能一下丢掉,添加进去的场景也不能一点显示出来,就将旧场景和新的场景组合成为一个新的场景,然后设置特效,等到结束的时候再去释放旧的场景。
3.3输出控件
输出控件lable,输出在游戏中很少用到,但是显示分数的时候也会用到。文字输出主要用到CCLableTTF,CCLableAtlas和CCLableBMFont这三个类。CCLableTTF父类是sprite,如果内容经常发生变化,需要渲染,效果不好,适合不变的显示内容,而且跨平台不适合,因为需要在显示的时候设置字体。可以使用这个做一个秒表。
CCLabelTTF*label=CCLabelTTF::create("Label","Arial",24);
CCLableAtlas用到字符映射文件,字符要等宽等高,编码是ASCII,并且显示的字符在这个图片中。砖块,父类是node,对渲染做了优化,对于不断变化的内容有利。对于不太规则的,有空白的文件,需要自己来设置字符的大小。如果显示的内容不存在,就会显示一个空隙。一次加载,可以多吃取用。
CCLabelAtlas*label2=CCLabelAtlas::create("100/.01","fonts/labelatlas2.png",16,30,'.');
CCLableBMFont也依赖ASCII表,是一个批处理精灵,里面的内容是一个精灵,可以对每个精灵进行处理,使用objectAtIndex来获取,有一个fnt文件来描述那个图片文件,创建的时候更加自由。CCARRAY_FOREACH来遍历CCArray数组,然后可以设置让全部的精灵运动。
CCLabelBMFont*label3=CCLabelBMFont::create("ab","bitmap.fnt");
输入控件CCEditBox,在手机上才可以看到,需要用到头文件cocos-ext.h,创建CCEditBox的时候需要用到九妹图。手机上是弹出软键盘,但是win下是弹出一个对话框。可以设置setPlaceHolder提示文字,也可以设置文件的显示颜色,用ccColor3B和ccColor4B(rgb,透明度),全是255变为白色。使用getText获取输入的字符串。
3.4UI控件
UI控件来自cocos2dx的扩展库,完善了UI方面的元素,使cocos2dx更加丰富多彩。
3.4.1CCControlSlider滑动条
CCControlSlider*slider=CCControlSlider::create("sliderTrack.png","sliderProgress.png","sliderThumb.png");
slider->addTargetWithActionForControlEvents(this,cccontrol_selector(T12UI::controlCallback),CCControlEventValueChanged);//注册响应函数
CCControlSlider,也需要提供三个精灵,需要设置最大值和最小值,因为默认是0.用来调节音量,也是设置一个回调函数来处理,在回调函数中调用的成员变量,之前需要创建好,否则会有bug。v=slider->getValue()获取活动条的值。在Windows下是设置音量无效的,因为没有写代码。
3.4.2CCControlSwitch开关按钮
CCControlSwtich,需要提供四个精灵,实现开关的转换。设置一个回调函数,来确定是开还是关,addTargetWithActionForControlEvents,用来监听一些事件,数值发生变化的时候调用。在回调函数中,判断是isOn,还是关闭,setOn设置默认值。可以用来打开和关闭音乐。
3.2.3 CCScale9Sprite九妹图
背景是使用一小块图片,通过拉伸,但是比例不一定,使得可以当做大的图片来使用。九妹图是将图片分为9部分,只扩展中间的部分,其余的部分不变,正常的显示在图片的边缘。正常的图片拉伸之后,边角比较模糊,但是九妹图没有那个情况,缩小的时候就达不到效果。九妹图创建的时候,设置如何切割,有的时候效果不是很好,最好是默认的。
3.2.4CCControlButton
CCControlButton*button=CCControlButton::create(titlebutton,bgbutton);
button->setBackgroundSpriteForState(bgbuttonlighted,CCControlStateHighlighted);//设置背景高亮
3.5帧循环-定时器
游戏的本质就是不断的绘图,游戏的主逻辑就是循环(循环的绘图,定时处理事件和处理用户输入),主循环在CCDirector类的mainLoop函数,这是一个虚函数,在子类CCDisplayLinkDirctor类中重写。在这个函数中调用drawScene函数,之后调用visit遍历每个node,还有一个pool内存池管理。只要是CCNode派生的子类都可以设置定时器。
(1)帧循环定时器:scheduUpdate函数启动帧定时器,启动之后调用update回调函数,update是CCNode定义的虚函数,使用unschedualUpdate停止,停止也可以通过设置一个标志flag来停止,当触摸的时候flag变为true,然后在update的函数中判断。对实时性要求高的,比如碰撞检测会用到帧循环定时器。
(2)自定义定时器:schedule参数是一个回调函数,可以设计隔多久调用,单位是秒,处理函数在回调函数中处理,回调函数是自定义的。unschedule停止某个回调函数。schedule还有一个参数较多的构造函数,功能更加强大。回调函数的参数,是这次和上次调用定时器的时间差,因为存在误差,比如一些跑酷游戏,会越来越偏移,也会卡。尽量一个定时器对应一个回调函数。unscheduleAllSelectors停止所有的定时器。
(3)一次性的定时器:scheduleOnce只用一次的定时器,用的比较少。schedule_selector是一个宏,经过了两次包装,得到的是函数的入口地址。
动画的本质就是定时器+属性的改变。帧循环实现来回巡逻,在帧循环提供一个速度,由于帧循环提供一个时间,通过setPositionX来设置X轴的位置,增加一个flag,当为true的时候,X轴增加,到了一定距离之后改变flag;当为false的时候,X轴减少。
3.6碰撞检测
精灵之间简化的碰撞检测:为了防止游戏变卡,简化算法
外切矩形的碰撞检测:intersectsRect
点和矩形之间的检测:containsPoint
圆和圆之间的碰撞:getDistance、getDistanceSq(不用开根,效率高)
圆和点之间的碰撞:getDistanceSq
线段相交:ccpSegmentIntersect
直线检测:ccpLineIntersect
穿透:预测下一个时刻会不会撞墙,如果游戏变卡的时候,就会穿透过去。解决方案:如果dt很大,就丢弃,就不移动,帧循环丢弃;也可以不是检测目标地点有没有墙,而是检测下一时刻移动的时候路途上有没有墙。
3.7数据结构
CCString万能类型,可以转换为任意类型,在cocos3.x中是value。
CCArray动态数组(二级指针),只能存储CCObject及子类,如果保存CCPoint就会报错。CCArray是一个面向对象包装类继承至CCObject。(insetObject效率低(会发生移动),addObject插入后面,removeObject删除;遍历的时候,可以使用objectAtIndex(i),遍历的时候从后面遍历到前面更加安全,因为即使删除也不会发生越界。CCArray一般不会被增加到其他类中,所以他的引用计数是1,并且设置为autorelease对象。创建CCArray对象并且retain,然后在这个类中的析构函数中调用release方法来释放内存。在cocos3.x中是vector。
CCDictionary字典,很少用来插入数据,一般用来查找valueForKey,返回CCString类型,遍历的时候,首先获得allKeys,返回的是CCArray指针,也可以removeObjectForKey删除数据。setObject和objectForKey获取数据,key的类型必须一致。中文显示问题,支持中文显示。在cocos3.x中是map。
3.8背景音乐和音效
需要包含SimpleAudioEngine头文件,音效游离于场景之外,是一个全局的,切换场景的时候要注意。播放音乐是一个单例,与音乐相关的头文件位于CocosDenshion文件中。playBackgroudMusic音乐,playEffect音效,应该在播放之前将音乐缓冲下来preloadBackgroudMusic和preloadEffect,只有在手机下面预加载才会显现,电脑上显示不出来。stopBackgroundMusic关闭音乐只会有一个,多个会发生混乱。stopEffect(id)关闭音效,id是由playEffect返回。pauseBackgroudMusic暂停,resumeBackgroudMusic恢复setBackgroudMusicVolume设置音量,unloadEffect卸载。
切换场景replaceScene。错误案例:在一个场景init的时候播放音效,在切换到另一个场景的时候打开音乐,在OnExit函数中关闭,然后在新场景的init函数中播放音乐。但是切换之后竟然没有继续播放。这是因为在切换函数中播放下一个场景的音乐,但是其实这个时候这个场景并没有结束,于是在onExit的时候调用关闭音乐的时候,将刚刚打开的音乐关闭了。所以在工作中,播放音乐在onEnter中播放,在onExit停止,这样就会继续播放,否则出现错误。new->init->onEnter->onExit->delete步骤来表示生命周期。
引申:场景之间使用的全局变量,都要在onEnter中申请资源,在onExit中释放资源。
3.9string类实现
实现简单的string类,包含拷贝构造函数和默认构造函数,析构函数,赋值运算符重载。在构造函数中,当参数为空的时候,同样需要开辟内存_str=newchar[1];
*_str='\0'。在赋值重载的时候,首先需要判断是否和自己一样,不相同的话先删除自己,然后再重新创建。
3.10cast类型转换
基本类型在隐式转换的时候,有可能溢出或者截断。如果希望转换为什么类型,必须自己制定。为了避免浪费总线,一般定义为int类型,这样也避免转换出错。如果对象A的空间比对象B的空间大,给B赋值的时候出现,踩内存,有可能崩溃。这种强制转换在编译的时候无法发现。
static_cast静态类型转换,在编译的时候就出错。如果a到b可以进行隐式转换,那么b到a就可以进行static_cast转换。经常用到结构体指针转为void类型,父类指针转换为子类指针。
dynamic_cast没有虚函数的指针不可以进行转换,只用于父子之间转换,转换失败变为NULL。
const_cast去掉变量的const属性。constinta=10;相当于在编译的时候就确定a是多少,即使强制转换之后,a也是那个值。类型转换的坑很多,最好不要使用。
3.11ZOrder
默认是0。设置两个精灵,重叠在一起,看哪个显示,如果没有设置的话,先进入的先渲染,看到的是后渲染的。setOrderOfArrival是系统自动调用的,是在create或者addChild的时候设置的,有一个静态变量,每次调用就会加1,所以越到后面越大,这个是逻辑上的,其实ZOrder还都是0。也可以setZOrder自己设置,必须是相同父节点才有意义。和settag的值是一样的,必须是相同父节点才有意义,不同父节点之间tag的值相同也没有关系。
原文链接:https://www.f2er.com/cocos2dx/343104.html