在COCOS2D-X里如何制作各种按钮

程序截图

注意:原文是基于Cocos2d-iphone的教程,现已改为Cocos2d-X-3.0Beta版本。

当你在使用cocos2d制作一个游戏的时候,你很可能会发现,你需要的第一个东西就是“按钮”。(比如游戏开始时的菜单选择界面等)这个教程将会一步步地教你如何使用cocos2d来创建按钮。刚开始创建简单的按钮,然后再介绍开关按钮和单选按钮。这篇教程假设你已经知道如何使用cocos2d来制作简单的游戏了,或者具备同等相关经验。

当我第一次想要在cocos2d里面添加一个按钮的时候,我是这样想的:创建一个精灵(sprite)来代表按钮,然后检测这个按钮什么时候被按下去。当然,这样做肯定是可行的。但是,在cocos2d里面,还有更简单的方法--通过使用cocos2d的菜单系统。

在cocos2d的菜单系统里面,包含一个Menu,在Menu里面又包含一系列的Menuitems。Menu items可以是文本或者图片,而且菜单系统里面还包含了一些非常有用的逻辑,比如:排列菜单项,高亮显示被按下去的菜单项,开关菜单项等等。好了,让我们实践一下,看看用cocos2d的方式如何创建一个简单的按钮!

创建一个简单的按钮

打开VS,创建一个新的工程并命名为:Buttons。接下来,你需要一些按钮的图片--你可以自己创建,或者下载一些我已经做好的图片。好了,现在你有图片了,把它们拖到resource文件夹下面。

打开Classes分组下面的HelloWorldscene.h文件,然后在HelloWorld类里面添加一个成员变量,后面将会用到它:

LabelTTF* label; 

好了,接下来就是重点了。在HelloWorldScene.cpp文件中,把下面的代码添加到init方法中:

// Create a label for display purposes

label = LabelTTF::create("Last button: None", "Marker Felt", 32);
label->setPosition(Point(visibleSize.width / 2 + origin.x, origin.y + visibleSize.height - 80));
label->setHorizontalAlignment(TextHAlignment::CENTER);
this->addChild(label);

// Standard method to create a button

auto starMenuItem = MenuItemImage::create(
    "ButtonStar.png",
    "ButtonStarSel.png", CC_CALLBACK_1(HelloWorld::starMenuCallback, this));
starMenuItem->setPosition(Point(160, 220));
auto starMenu = Menu::create(starMenuItem, NULL);
starMenu->setPosition(Point::ZERO);
this->addChild(starMenu, 1);

首先,为了调试方便,我们创建了一个label。这个看起来很熟悉对不对?---我们在之前的教程中有提到过。然而,这一次我们使用了一个新的创建函数,它可以让我们指定label的大小和文字对齐方式。在这里,我把label的位置设置在窗口中上方,而且文本设置为居中对齐。这是一种广为人知的技术了,特别是当你想实现一些左对齐或者右对齐的文本的时候。

接下来的代码是创建按钮。首先使用类 MenuItemImage 来创建一个菜单项,并为这个按钮指定一张被选中的图片和没有被选中的图片。(也就是单击时被显示的图片和没有被单击时被显示的图片)当创建完菜单项之后,我们为按钮的点击事件指定了一个回调函数(这个函数后面会给出代码)。最后一步,就是创建一个 Menu 来包含这个按钮(或者一系列的按钮,以NULL结尾)。

注意,我们在原点的位置创建了按钮。这里实际上指定了菜单的中心点的位置。然后,我们指定菜单项的位置相对于菜单的位置偏移(160,220)--这样的话,在屏幕上面显示的时候,菜单项就会显示在(160,220)的位置了。(因为菜单项的position是相对于菜单的中心点来的,把菜单的中心点(也就是锚点)设置为(0, 0),与屏幕坐标原点重合后,可以方便为每个菜单项指定坐标点,因为这时候,只要按钮实际屏幕出现的位置设置菜单项的坐标点就行了)。

好了,还有一些代码需要补充。在init方法后面,添加我们的按钮回调函数:

void HelloWorld::starMenuCallback(Object* pSender)
{
    label->setString("Last button: * ");
}

编译并运行,你会看到如下的运行结果:

开关按钮

另外一种在游戏里面常用的按钮类型就是--开关按钮。这种类型的按钮一次只有一个图片显示出来,当你单击它的时候,它就会切换到另外一张图片。这个可以用来制作一个控制面板的可见性的控制器,这样可以最大限度地利用设备上面有限的屏幕大小。

非常幸运的是,cocos2d里面就内置了一种特殊的menu item叫做**MenuItemToggle**,它可以使事情变得更加简单。让我们来体验一下吧!首先,在HelloWorldScene.h里面添加2个成员变量:

MenuItemImage *_plusItem;
MenuItemImage *_minusItem;

然后,在你为场景添加的StartMenu后面,再添加下面的代码:

    _plusItem = MenuItemImage::create(
        "ButtonPlus.png",
        "ButtonPlusSel.png");
    _minusItem = MenuItemImage::create(
        "ButtonMinus.png",
        "ButtonMinusSel.png");
    MenuItemToggle *toggleItem = MenuItemToggle::createWithCallback(CC_CALLBACK_1(HelloWorld::plusMinusButtonCallback, this), _plusItem, _minusItem, NULL);

    auto toggleMenu = Menu::create(toggleItem, NULL);
    toggleMenu->setPosition(Point(160, 290));
    this->addChild(toggleMenu, 1);

首先, 就像我们前一个例子中一样,创建两个 MenuItemImage 。这里有一点不同--我把它们都添加到了 MenuItemToggle 里面。这个类会管理当前应该显示的菜单项,同时会在开关元素之间进行一些切换。

注意,当创建 MenuItemImage 的时候,我没有设置回调函数(也可以把回调函数设置成了NULL),但是我为 MenuItemToggle 类设置了回调函数。这样,就会使得代码意途更加清晰:当 MenuItemImage 在 MenuItemToggle 中的时候,在 MenuItemImage 上的任何 selector 都不会被调用,而只有 MenuItemToggle的selector会被调用。当然,我们可以很容易地在回调函数里面区分,到底哪个菜单项是可见的。

接下来,让我们看看如何实现回调函数吧!在init方法后面添加下面的代码:

void HelloWorld::plusMinusButtonCallback(Object* pSender)
{
    MenuItemToggle *toggleItem = (CCMenuItemToggle *)pSender;
    if (toggleItem->selectedItem() == _plusItem){
        label->setString("Visible button: +");
    }
    else if (toggleItem->selectedItem() == _minusItem){
        label->setString("Visible button: -");
    }
}

因此,正如你所见,MenuItemToggle里有一个**selectedItem()**方法,它可以告诉我们它的哪一个子菜单项当前可见(注意,当前可见的不等于被单击的)

好了,让我们运行一下吧!你会看到如下结果:

单选按钮

第三种常用的按钮类型就是单选按钮(radio button)。我在做一个游戏的时候,发现自己需要一些单选按钮,但是,cocos2d的源代码里面并没有任何有关单选按钮的实现。因此,我们自己实现一个单选按钮。

但是,目前cocos2d里面还是没有,因此,在这期间,你可以免费地使用我上面提到的一些实现。这篇教程使用的是我自己写的单选按钮的实现。首先,下载RadioMenu.h和RadioMenu.cpp,然后把它们拖到你的Classes文件夹下。然后在HelloWorldScene.cpp的顶部添加下面代码:

#include "RadioMenu.h"

然后,在init方法后面,紧跟你添加开关按钮的代码,添加下面代码:

    auto *menuItem1 = MenuItemImage::create("Button1.png", "Button1Sel.png", this, menu_selector(HelloWorld::but1Callback));
    auto *menuItem2 = MenuItemImage::create("Button2.png", "Button2Sel.png", this, menu_selector(HelloWorld::but2Callback));
    auto *menuItem3 = MenuItemImage::create("Button3.png", "Button3Sel.png", this, menu_selector(HelloWorld::but3Callback));

    auto *radioMenu = RadioMenu::create(menuItem1, menuItem2, menuItem3, NULL);
    radioMenu->setPosition(Point(220, 360));
    radioMenu->alignItemsHorizontally();
    radioMenu->setSelectedItem_(menuItem1);
    menuItem1->selected();
    this->addChild(radioMenu, 10);

首先,像之前一样,创建MenuItemImage,但是我们不是把它加到CCMenu类中,而是把它们加到RadioMenu类中。这个类确保一次只有一个菜单项被选中。这里,我们设置默认情况下,第一个菜单项被选中。 这里有一个新的知识点:我们利用cocos2d里面的自己布局功能,调用menu的 alignItemsHorizontally() 来水平对齐menu中的所有菜单项。注意,菜单项是相对于菜单的中心点来布局的。因此,我们不再需要把菜单的中心点设置为(0,0)了--取而代之的是,我们需要把菜单往中间靠右挪动一些,这样我们就可以让菜单项都完整地显示出来。 最后一件事情--像之前一样添加回调函数:

void HelloWorld::but1Callback(Object* pSender)
{
    label->setString("Last button: button1 ");
}
void HelloWorld::but2Callback(Object* pSender)
{
    label->setString("Last button: button2 ");
}
void HelloWorld::but3Callback(Object* pSender)
{
    label->setString("Last button: button3 ");
}

编译并运行,你将会看到下面的结果:

背后的原理

如果你看一看菜单系统是如何实现的,你会注意到所有的菜单项都是Node的子类,但是Menu是Layer的子类。根据cocos2d最佳实践,你不应该创建非常大的层次结构,你应该让层次结构尽可能的小。 因此,这意味着,你可能需要把尽可能多的菜单项放到一个菜单里面。因为,Layer是从Node派生出来的,它也可以run action。就是说,你可以对menu run action。




实现单选框复选框其实很容易,使用cocostudio可以实现复选框的功能,单选框的话貌似是不可以的,今天我们实现这俩个功能不使用cocostudio,因为本人认为这个东西还不是太成熟,资料不多,最主要的是没有研究,所以我们就自己写代码实现一下吧,原理就是利用菜单去做这个单选框和复选框,菜单项有一个函数是selected,我们就是利用这个函数来完成我们要的功能,效果如图,代码so easy!

单选框和复选框的实现

1 bool HelloWorld::init()
2 {
3     //
4     // 1. super init first
5     if ( !CCLayer::init() )
6     {
7         return false;
8     }
9  
10     CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
11     //添加一张背景图片
12     CCSprite * background = CCSprite::create("background.png");
13     background->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
14     this->addChild(background);
15  
16     //以下是用来实现单选框的功能的
17     //添加文本信息
18     CCLabelTTF * text = CCLabelTTF::create("Sex:","",24);
19     text->setColor(ccc3(255,100,100));
20     text->setPosition(ccp(visibleSize.width/3,visibleSize.height/2));
21     this->addChild(text);
22  
23     //创建俩个菜单项,用来实现当选框的功能
24     male = CCMenuItemImage::create("single_choice_normal.png","single_choice_select.png",
25         this,menu_selector(HelloWorld::button1));
26     female = CCMenuItemImage::create("single_choice_normal.png",
27         "single_choice_select.png",
28         this,menu_selector(HelloWorld::button2));
29  
30     CCMenu * menu = CCMenu::create(male,female,NULL);
31     menu->alignItemsHorizontallyWithPadding(80);
32     menu->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
33     //关键处在这里,设置male菜单项为选中状态,单选框在任何时刻必须有一个为选中状态
34     male->selected();
35     this->addChild(menu);
36  
37     //以下代码用来实现复选框的功能
38     save = CCMenuItemImage::create("box_off.png","box_on.png",this,
39         menu_selector(HelloWorld::button3));
40     CCMenu * menu2 = CCMenu::create(save,NULL);
41     menu2->setPosition(ccp(visibleSize.width/2,visibleSize.height/3));
42     this->addChild(menu2);
43  
44     //添加文本信息
45     CCLabelTTF * text2 = CCLabelTTF::create("Save:","",24);
46     text2->setColor(ccc3(255,100,100));
47     text2->setPosition(ccp(visibleSize.width/3,visibleSize.height/3));
48     this->addChild(text2);
49  
50     return true;
51 }
52  
53 //在以下俩个事件回调函数中改变他们的选中状态
54 void HelloWorld::button1(CCObject * obj)
55 {
56     CCLog("male");
57     male->selected();
58     female->unselected();
59 }
60  
61 void HelloWorld::button2(CCObject * obj)
62 {
63     CCLog("female");
64     female->selected();
65     male->unselected();
66 }
67  
68 //实现复选框的功能
69 void HelloWorld::button3(CCObject * obj)
70 {
71     CCLog("save");
72     static bool b = false;
73     if(b == false)
74     {
75         save->selected();
76         b = true;
77     }
78     else
79     {
80         save->unselected();
81         b = false;
82     }
83 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值