Cocos2d-xna : 横版战略游戏开发实验4 Layer构建丰富的交互

时间:2023-02-08 15:52:15

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

巧用CCMenuItem

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

Cocos2d-xna : 横版战略游戏开发实验4 Layer构建丰富的交互

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

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

Cocos2d-xna : 横版战略游戏开发实验4 Layer构建丰富的交互

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

CCLayer

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

Cocos2d-xna : 横版战略游戏开发实验4 Layer构建丰富的交互

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

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

运行看看:

Cocos2d-xna : 横版战略游戏开发实验4 Layer构建丰富的交互

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

为了更好了和将来拓展,我加入了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:开发一个打地鼠游戏(下)

Cocos2d-xna : 横版战略游戏开发实验4 Layer构建丰富的交互

我们将用上这个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;
}

现在再运行一下:

Cocos2d-xna : 横版战略游戏开发实验4 Layer构建丰富的交互

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

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

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

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