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