zoukankan      html  css  js  c++  java
  • Cocos2dxna : 横版战略游戏开发实验4 Layer构建丰富的交互

    本篇中主要介绍cocos2d-xna的CCLayer和CCMenu,在游戏开发中,只是使用前面提到的CCSprite和CCScene来完成丰富的交互行为是不够的,单体的界面元素操作只会增加UI开发的复杂程度,所以像CCLayer这样的成组操作可以为开发提供很好的便捷。这次拿关卡选择场景做操作实验,完成较为复杂的交互开发。

    巧用CCMenuItem

    拿出来咱们之前制作的资源文件,游戏设计中,将魏蜀吴三个国家分成不同的几个关卡,这样就要求我们在界面上需要对它们进行分开选择,先看看图片资源的准本情况,在GameUI01.plist里有这样的几个按钮文件:

    QQ截图20120922195802

    用亮红色的按钮表示为选定的,而暗红色的表示可以点击,在标签头上,被选定的一定是不能点击的,所以可以巧用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;

    用什么方式都行,现在运行一下看看效果

    QQ截图20120922203222

    当点击任何一个标签的时候就会呈现激活状态,而此时也会不能点击,好了,下面就要考虑如何实现对应的关卡内容了。

    CCLayer

    上一篇的代码中,只是用了for循环绘制了几个按钮然后让按钮触发跳转场景,很显然,现在有了分类的标签页,下面的对应的关卡也需要改变,如果使用很粗暴的方法(每个组单独写按钮,用是否可见管理)虽然能实现,但是管理起来不但麻烦,而且控制动画也就不方便了,下面我们用CCLayer图层来将这一大堆的按钮放在一起,关于层的概念如果玩过PS的朋友应该很容易理解,下面这张图当作简要的说明:

    QQ截图20120922203223

    魏蜀吴不同的标签对应一个不同的图层,当点击对应时,图层发生发生变换,所以我们抽象一个关卡的图层类代码如下:

    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());

    运行看看:

    QQ截图20120922203214

    有了关卡,图层显示,并且还有了关卡名字,其实这里你可以发挥一下,以后将来能自定义关卡名,但谨记汉字是显示不了的,这需要另外一个汉字解决方案,以后咱们再说。

    为了更好了和将来拓展,我加入了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;
    }

    现在再运行一下:

    QQ截图20120922203224

    当点击对应的标签时候就会发生变化,交互是否立即丰富了很多呢?现在你可以尝试自己做一些行为效果发挥想象力。

    本篇主要对CCLayer和CCAction结合应用达到关卡选择场景的交互开发,没有做太多的其他考虑,在正式开发中,关卡选择界面和关卡设计紧密相关,对应数据的构建和设计至关重要,该部分在后面我们一步一步的完成它。

    关于Action有很多的方法,有兴趣的朋友可以参看OpenXLive移植的cocos2d-xna里的test工程样本,里面展示了非常多的行为效果,例如放缩、切变、不透明等等,在这里就不再太多的讲解。

    本篇例子工程:https://github.com/Nowpaper/SanguoCommander_cocos2dxna_Sample
    本例工程名为:SanguoCommander4

  • 相关阅读:
    bzoj3622: 已经没有什么好害怕的了
    BSGS
    LOJ#2320 生成树计数
    ??? cliquers
    生成函数
    洛谷P5206 数树
    01分数规划
    差分约束系统
    51nod1238 最小公倍数之和 V3
    51nod1237 最大公约数之和 V3
  • 原文地址:https://www.cnblogs.com/nowpaper/p/2698465.html
Copyright © 2011-2022 走看看