本篇中主要介绍cocos2d-xna的CCLayer和CCMenu,在游戏开发中,只是使用前面提到的CCSprite和CCScene来完成丰富的交互行为是不够的,单体的界面元素操作只会增加UI开发的复杂程度,所以像CCLayer这样的成组操作可以为开发提供很好的便捷。这次拿关卡选择场景做操作实验,完成较为复杂的交互开发。
巧用CCMenuItem |
拿出来咱们之前制作的资源文件,游戏设计中,将魏蜀吴三个国家分成不同的几个关卡,这样就要求我们在界面上需要对它们进行分开选择,先看看图片资源的准本情况,在GameUI01.plist里有这样的几个按钮文件:
用亮红色的按钮表示为选定的,而暗红色的表示可以点击,在标签头上,被选定的一定是不能点击的,所以可以巧用CCMenuItem的Enabled属性组合达到效果,打开SceneSelect.cs类,在构造中创建Tab按钮的地方把原有的代码修改一下,原来的只是用CCSpirte贴了一张展示图,详细的可以看上一篇,
首先创建该类的一个成员:
CCMenu story_tabs;
CCMenu story_tabs一会儿就能用的上了,下面再构造函数里写代码如下:
//上面的Tab按钮创建 CCMenuItemSprite tab1 = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("tab_shu2.png"), CCSprite.spriteWithSpriteFrameName("tab_shu1.png"), CCSprite.spriteWithSpriteFrameName("tab_shu1.png"), this, click_story_tab); CCMenuItemSprite tab2 = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("tab_wu2.png"), CCSprite.spriteWithSpriteFrameName("tab_wu1.png"), CCSprite.spriteWithSpriteFrameName("tab_wu1.png"), this, click_story_tab); CCMenuItemSprite tab3 = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("tab_wei2.png"), CCSprite.spriteWithSpriteFrameName("tab_wei1.png"), CCSprite.spriteWithSpriteFrameName("tab_wei1.png"), this, click_story_tab); story_tabs = CCMenu.menuWithItems(tab1, tab2, tab3); //按照水平方向10个像素间隔分割 story_tabs.alignItemsHorizontallyWithPadding(10); //转换为界面UI的坐标 story_tabs.position = CCDirector.sharedDirector().convertToUI(new CCPoint(300, 72)); //将第一个设置为不可点击 tab1.Enabled = false; this.addChild(story_tabs);
该代码是创建了几个按钮并且组合到了一个CCMenu当中,这里需要特别说明的是,CCDirector.sharedDirector().convertToUI()方法,这个方法是将坐标转换成为UI坐标,要知道,在cocos2d里面坐标是用左下角向上的,而不是一般的左上角,用这个方法可以方便的转换。
现在运行不了,因为没有实现click_story_tab方法,下面在该类中实现方法:
private void click_story_tab(CCObject sender) { //遍历story_tabs foreach (var item in story_tabs.children) { //判断是否为CCMenuItem,如果是的话,排除sender以外的全部设定为true if(item is CCMenuItem) (item as CCMenuItem).Enabled = item != sender; } }
也许初步看起来不太容易理解,其实这里做一下逻辑判断就行,用if else实现就是这样的:
if(item != sender) (item as CCMenuItem).Enabled = false; else (item as CCMenuItem).Enabled = true;
用什么方式都行,现在运行一下看看效果
当点击任何一个标签的时候就会呈现激活状态,而此时也会不能点击,好了,下面就要考虑如何实现对应的关卡内容了。
CCLayer |
上一篇的代码中,只是用了for循环绘制了几个按钮然后让按钮触发跳转场景,很显然,现在有了分类的标签页,下面的对应的关卡也需要改变,如果使用很粗暴的方法(每个组单独写按钮,用是否可见管理)虽然能实现,但是管理起来不但麻烦,而且控制动画也就不方便了,下面我们用CCLayer图层来将这一大堆的按钮放在一起,关于层的概念如果玩过PS的朋友应该很容易理解,下面这张图当作简要的说明:
魏蜀吴不同的标签对应一个不同的图层,当点击对应时,图层发生发生变换,所以我们抽象一个关卡的图层类代码如下:
public class LayerLevels : CCLayer { public LayerLevels() { //关卡选择层 CCPoint offset = new CCPoint(170, 180); for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { //关卡的按钮 CCMenuItemSprite level = CCMenuItemSprite.itemFromNormalSprite( CCSprite.spriteWithSpriteFrameName("btn_level1.png"), CCSprite.spriteWithSpriteFrameName("btn_level2.png"), this, click_level); CCMenu menu = CCMenu.menuWithItems(level); //位置相对于左上的UI界面 menu.position = CCDirector.sharedDirector().convertToUI(new CCPoint(offset.x + 160 * i, offset.y + 85 * j)); this.addChild(menu); //创建一个MenuItem,用作文本内容 CCMenuItem menuitem = new CCMenuItem(); //指定Arial的字体描述,保证fonts里有Arial.spritefont var text = CCLabelTTF.labelWithString((j * 4 + i + 1).ToString(), "Arial", 12); //将颜色指定为黑色 text.Color = new ccColor3B(); menuitem.addChild(text); menu.addChild(menuitem); } } } private void click_level(CCObject sender) { //当关卡点击的时候会自动跳转到游戏场景 CCDirector.sharedDirector().pushScene(GameRoot.pSceneGame); } public void Show() { this.visible = true; } public void Hide() { this.visible = false; } }
好了,现在回到SceneSelect.cs,将之前的按钮for循环全部去掉,只需要一行代码:
this.addChild(new LayerLevels());
运行看看:
有了关卡,图层显示,并且还有了关卡名字,其实这里你可以发挥一下,以后将来能自定义关卡名,但谨记汉字是显示不了的,这需要另外一个汉字解决方案,以后咱们再说。
为了更好了和将来拓展,我加入了Show()和Hide()两个方法,用来控制是否显示,下面我们马上就用的上啦。
但是点击对应的魏蜀吴还不能实现切换,下面考虑如何实现页面的切换,将标签都对应起来,建立一个字典:
Dictionary<CCMenuItem, LayerLevels> dictLayerLevels = new Dictionary<CCMenuItem, LayerLevels>(); LayerLevels currentlayerlevers = null;
在SceneSelect构造函数中:
//添加测试层 //this.addChild(new LayerLevels()); //将标签对应到不同的LyerLevers 类中 dictLayerLevels.Add(tab1, new LayerLevels() { visible = false }); dictLayerLevels.Add(tab2, new LayerLevels() { visible = false }); dictLayerLevels.Add(tab3, new LayerLevels() { visible = false }); //将tab1显示为当前的关卡层 showLayerLevels(dictLayerLevels[tab1]); //遍历并添加到界面中 foreach (var item in dictLayerLevels.Values) { this.addChild(item); }
注意将之前的注释掉,现在这个没用,好了,然后实现showLayerLevels方法和在标签点击的事件中加入代码,变成这个样子:
private void click_story_tab(CCObject sender) { //遍历story_tabs foreach (var item in story_tabs.children) { //判断是否为CCMenuItem,如果是的话,排除sender以外的全部设定为true if(item is CCMenuItem) (item as CCMenuItem).Enabled = item != sender; } showLayerLevels(dictLayerLevels[sender as CCMenuItem]); } private void showLayerLevels(LayerLevels layer) { if (currentlayerlevers != null) currentlayerlevers.Hide(); layer.Show(); currentlayerlevers = layer; }
showLayerLevels可以保证之前的关卡层隐蔽掉,将新的显示出来。
但是现在点击上面的标签是没用的,因为没有任何的变化,只是可见和不可见,所以为了更清晰,下面将用CCAction来实现动画效果。
CCMoveTo |
CCMoveTo意思很简单,让一个元素从当前的位置移动到指定的新位置,它的静态方法可以通过时间和坐标产生一个移动的CCAction,下面用它来实现图层的移动变化,这样当标签切换的时候,对应的更换也就可以直接看得到。那么打开LayerLevels类,把Show方法做一下修改:
public void Show() { //将其显示出来 this.visible = true; //把位置设置到最右边出屏幕外 this.position = new CCPoint(CCDirector.sharedDirector().getWinSize().width,0); //指定移动到0,0点 CCMoveTo move = CCMoveTo.actionWithDuration(0.5f,new CCPoint(0,0)); //运行这个Action this.runAction(move); }
好了,运行一下,看到了滑动效果了吗?这个效果,当然了,这个效果还不能通过手势来控制,只能点击上面的魏蜀吴。
更进一步,我们现在看这个效果还是有点别扭,就是之前页面是直接消失掉的,能不能跟滑动滚动走呢,这里就需要另外一个行为CCSequence队列行为来实现,队列行为在之前的一篇中有详细的说明:
Cocos2d-x for WindowsPhone:开发一个打地鼠游戏(下):
我们将用上这个Action来实现Hide方法,当隐蔽动画完成之后将本图层隐蔽,改造Hide方法并添加回调方法:
public void Hide() { //指定移动到最左边并超出屏幕 CCMoveTo move = CCMoveTo.actionWithDuration(0.5f,new CCPoint(-CCDirector.sharedDirector().getWinSize().width,0)); //执行一个队列行为,当移动完成后就会调用HideAniCompled this.runAction(CCSequence.actionOneTwo(move, CCCallFunc.actionWithTarget(this, HideAniCompled))); } void HideAniCompled() { this.visible = false; }
现在再运行一下:
当点击对应的标签时候就会发生变化,交互是否立即丰富了很多呢?现在你可以尝试自己做一些行为效果发挥想象力。
本篇主要对CCLayer和CCAction结合应用达到关卡选择场景的交互开发,没有做太多的其他考虑,在正式开发中,关卡选择界面和关卡设计紧密相关,对应数据的构建和设计至关重要,该部分在后面我们一步一步的完成它。
关于Action有很多的方法,有兴趣的朋友可以参看OpenXLive移植的cocos2d-xna里的test工程样本,里面展示了非常多的行为效果,例如放缩、切变、不透明等等,在这里就不再太多的讲解。
本篇例子工程:https://github.com/Nowpaper/SanguoCommander_cocos2dxna_Sample
本例工程名为:SanguoCommander4