cocos2dx 3.3 如何为创建一个按钮
在使用 Cocos2d-x 开发游戏时,按钮往往是不可或缺的元素,例如游戏开始时的菜单选择界面就会用到按钮。本教程将逐步指导你如何使用 Cocos2d-x 创建不同类型的按钮,包括简单按钮、开关按钮和单选按钮。阅读本教程前,假设你已经掌握使用 Cocos2d-x 制作简单游戏的技能或具备同等相关经验。
一、使用 Cocos2d-x 菜单系统创建按钮的优势
最初,我尝试在 Cocos2d-x 中添加按钮时,考虑创建一个精灵(sprite)来代表按钮,并检测其何时被按下。这种方法可行,但 Cocos2d-x 提供了更简单的方式——使用菜单系统。
Cocos2d-x 的菜单系统包含一个 Menu 对象,其中又包含一系列的 MenuItem。MenuItem 可以是文本或图片,菜单系统还具备一些实用的逻辑,如排列菜单项、高亮显示被按下的菜单项、开关菜单项等。接下来,我们将实践如何使用 Cocos2d-x 的方式创建一个简单的按钮。
二、创建一个简单的按钮
1. 准备工作
打开 Visual Studio,创建一个新的工程并命名为 Buttons。你需要准备一些按钮的图片,可以自行创建或下载已有的图片,将这些图片拖到 resource 文件夹下。
2. 代码实现
2.1 添加成员变量
打开 Classes 分组下的 HelloWorldScene.h 文件,在 HelloWorld 类中添加一个成员变量:
LabelTTF* label;
2.2 在 init 方法中添加代码
在 HelloWorldScene.cpp 文件的 init 方法中添加以下代码:
// 创建一个用于显示的标签
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);
// 创建按钮的标准方法
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),与屏幕坐标原点重合后,为每个菜单项指定坐标点就更方便。
2.3 添加按钮回调函数
在 init 方法后面添加按钮回调函数:
void HelloWorld::starMenuCallback(Object* pSender)
{
label->setString("Last button: * ");
}
3. 编译运行
编译并运行程序,你将看到相应的运行结果。
三、创建开关按钮
1. 开关按钮简介
开关按钮是游戏中常用的按钮类型,一次仅显示一张图片,单击时会切换到另一张图片。可用于制作控制面板的可见性控制器,以充分利用设备有限的屏幕空间。
2. 代码实现
2.1 添加成员变量
在 HelloWorldScene.h 中添加两个成员变量:
MenuItemImage* _plusItem;
MenuItemImage* _minusItem;
2.2 添加开关按钮代码
在为场景添加 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 上的选择器不会被调用,只有 MenuItemToggle 的选择器会被调用。
2.3 实现回调函数
在 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() 方法可告知我们当前哪个子菜单项可见(注意,当前可见的不等于被单击的)。
3. 编译运行
编译并运行程序,查看运行结果。
四、创建单选按钮
1. 单选按钮实现背景
Cocos2d-x 的源代码中没有直接实现单选按钮,因此我们需要自行实现。本教程使用我自己编写的单选按钮实现。
2. 代码实现
2.1 下载并添加文件
下载 RadioMenu.h 和 RadioMenu.cpp,将它们拖到 Classes 文件夹下。在 HelloWorldScene.cpp 的顶部添加以下代码:
#include "RadioMenu.h"
2.2 添加单选按钮代码
在 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,但将它们添加到 RadioMenu 类中,该类确保一次只有一个菜单项被选中。这里设置默认情况下第一个菜单项被选中。
新的知识点是,利用 Cocos2d-x 的布局功能,调用 alignItemsHorizontally() 方法水平对齐菜单中的所有菜单项。注意,菜单项是相对于菜单的中心点布局的,因此无需将菜单的中心点设置为(0,0),而是将菜单往中间靠右挪动一些,以确保菜单项完整显示。
2.3 添加回调函数
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 ");
}
3. 编译运行
编译并运行程序,查看运行结果。
五、菜单系统背后的原理
查看菜单系统的实现,会发现所有菜单项都是 Node 的子类,而 Menu 是 Layer 的子类。根据 Cocos2d-x 最佳实践,应尽量减小层次结构,因此建议将尽可能多的菜单项放到一个菜单中。由于 Layer 派生自 Node,它可以运行动作,即可以对 Menu 执行动作。
六、总结
本教程提供了创建不同类型按钮的完整源代码,希望对你有所帮助。如果你在使用 Cocos2d-x 的按钮时有任何好的建议或想法,欢迎分享!