《保卫萝卜2》关卡选择页面开发
我们已经实现完《保卫萝卜2》主页面上解锁天天向上玩法(链接:https://blog.csdn.net/qq135595696/article/details/123012891),接下来我们实现关卡选择页面的开发。同样,在开发之前,我们先看一下完成后的效果图,如下如所示。
分析《保卫萝卜2》的关卡选择页面,可以明显得到,我们应该把这个场景逻辑上分为两个层,一个是背景层,一个UI层。其中,背景层里是一个滚动视图,滚动整个地图。UI层存放一些简单的按钮和图片等节点。
我们在项目中新建一个筛选器ChooseLevel,用来存放关卡选择场景的相关文件,如下图所示。
其中,ChooseLevel.h、ChooseLevel.cpp用来管理关卡选择页面场景。 在该场景之上又加载了一个UI层。根据分析得出,该场景中首先需要一个ScrollView类来滚动整个背景地图,接下来我们先创建ScrollView对象,代码如下:
//背景滚动界面
auto scrollView = ScrollView::create();
//设置滚动方向
scrollView->setDirection(ScrollView::Direction::HORIZONTAL);
//开启触摸事件
scrollView->setTouchEnabled(true);
//设置内容大小
scrollView->setContentSize(Director::getInstance()->getVisibleSize());
//隐藏滚动条
scrollView->setScrollBarEnabled(false);
this->addChild(scrollView);
//创建背景地图
Sprite* imageView = nullptr;
string filePath;
float nextPosX = 0.f;
for (int i = 0; i < 14; i++)
{
filePath = "ChooseLevel/Map/stage_map_" + to_string(i) + ".png";
imageView = Sprite::create(filePath);
//更改锚点位置是因为第14张地图跟前面的13张地图宽度不一致
imageView->setAnchorPoint(Vec2(0, 0.5));
imageView->setPosition(nextPosX, Director::getInstance()->getVisibleSize().height / 2);
nextPosX += imageView->getContentSize().width;
scrollView->addChild(imageView);
}
//设置ScrollView的布局容器大小
scrollView->setInnerContainerSize(Size(nextPosX, Director::getInstance()->getVisibleSize().height));
上述代码创建了ScrollView对象,同时规定其滚动方向为水平方向,同时开启其触摸事件、设置其内容大小为整个屏幕的大小。
紧接着,创建背景地图。在ScrollView中,子节点的坐标需要我们自己设定,这里通过一个for循环干净利索地台添加了14张背景图。最后设定了ScrollView的布局容器大小,这里nextPosX的值等于14张背景图宽度的总和。
最后只需要在ChooseLevel类中的init()函数调用即可。接下来,实现UI层的开发。
显然,UI层需要实现左上角的三个按钮、中间的打折信息按钮以及右上角的的生命星显示,如下图所示:
可以明显看出,左上角的三个按钮的后面有一张背景图片,这张背景图始终位于当前屏幕的左上角,所以可以将它的锚点设置为(0,1),坐标设置为(0, Director::getInstance()->getVisibleSize().height),代码如下(首先我们创建UI层的Layer):
//UI按钮控件
auto layerUI = Layer::create();
this->addChild(layerUI);
auto leftPanelPinfo = AutoPolygon::generatePolygon("ChooseLevel/stagemap_toolbar_leftbg.png");
//该背景图始终在屏幕的左上角
auto leftPanel = Sprite::create(leftPanelPinfo);
leftPanel->setAnchorPoint(Vec2(0, 1));
leftPanel->setPosition(0, Director::getInstance()->getVisibleSize().height);
leftPanel->setScale(0.7);
layerUI->addChild(leftPanel);
上面的代码保证了三个按钮的背景始终在屏幕的左上角,那么剩下的这3个按钮我们应该作为背景图片的子节点,使得三个按钮和按钮背景图片“绑”成了一个整体,这样的相对布局保证了3个按钮的坐标不会随屏幕大小的不一致而产生位置错乱,代码如下:
//创建背景图精灵的子节点(三个按钮)
string toolbarHomeFilePath = "ChooseLevel/stagemap_toolbar_home.png";
_toolbarHome = Button::create(toolbarHomeFilePath, toolbarHomeFilePath, "");
_toolbarHome->setAnchorPoint(Vec2(0, 0));
_toolbarHome->setPosition(Vec2(10, 10));
//启用缩放动画
_toolbarHome->setPressedActionEnabled(true);
leftPanel->addChild(_toolbarHome);
string toolbarShopFilePath = "ChooseLevel/stagemap_toolbar_shop.png";
_toolbarShop = Button::create(toolbarShopFilePath, toolbarShopFilePath, "");
_toolbarShop->setAnchorPoint(Vec2(0, 0));
_toolbarShop->setPosition(Vec2(125, 10));
_toolbarShop->setPressedActionEnabled(true);
leftPanel->addChild(_toolbarShop);
//初始化首页按钮、商店按钮、排行榜按钮的三个事件
_toolbarHome->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
auto mainScene = MainScene::createScene();
Director::getInstance()->replaceScene(mainScene);
}
});
_toolbarShop->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("open the shop");
}
});
_toolbarLeaderboard->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("open the leaderboard");
}
});
在代码中,我们可以发现“首页”按钮仅采用一张图片,所以我们应该给按钮启用被按下时的缩放操作。最后,给“首页”按钮添加触摸事件监听器,实现了住页面场景MainScene的切换。另外,在代码中用于判断时间是否在触摸结束时才响应实现逻辑代码,在开发中更多的按钮事件响应都应该放在触摸事件结束之后,而不是触摸释放之前。另外两个按钮同理。参考上述代码即可。
在中间促销按钮中,需要添加一个BMFont来表示当前打多少折扣。定义私有变量cocos2d::Label* _discountText;来表示当前打多少折扣,这样就可以通过该变量来控制显示的折扣,代码如下:
//创建中间的促销按钮
string discountTagStoneFilePath = "ChooseLevel/zh/discount_tag_stone.png";
_discountTagStone = Button::create(discountTagStoneFilePath, discountTagStoneFilePath, "");
_discountTagStone->setAnchorPoint(Vec2(0.5, 1));
_discountTagStone->setPosition(Vec2(Director::getInstance()->getVisibleSize().width / 2, Director::getInstance()->getVisibleSize().height));
_discountTagStone->setScale(0.7);
_discountText = Label::createWithBMFont("ChooseLevel/discount.fnt", "8");
_discountTagStone->addChild(_discountText);
_discountText->setAnchorPoint(Vec2(0, 0));
_discountText->setPosition(160, 65);
layerUI->addChild(_discountTagStone);
//初始化中间的促销按钮
_discountTagStone->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("open the discountTagStone");
}
});
右上角的生命星组成与中间限时折扣按钮发类似,最底层为一张背景图,顶层为生命星数量文本,实现起来比较简单,代码如下:
//创建右上角 生命星按钮
string toolbarRightbgFilePath = "ChooseLevel/stagemap_toolbar_rightbg.png";
_toolbarRightbg = Button::create(toolbarRightbgFilePath, toolbarRightbgFilePath, "");
_toolbarRightbg->setAnchorPoint(Vec2(1, 1));
_toolbarRightbg->setPosition(Director::getInstance()->getVisibleSize());
_toolbarRightbg->setScale(0.7);
layerUI->addChild(_toolbarRightbg);
//创建生命星星的图片背景
auto starImage = Sprite::create("ChooseLevel/zh/stagemap_toolbar_overten.png");
starImage->setAnchorPoint(Vec2(1, 1));
starImage->setPosition(Director::getInstance()->getVisibleSize());
starImage->setScale(0.7);
layerUI->addChild(starImage);
auto text = Label::createWithSystemFont("010", "Arial", 24);
text->setAnchorPoint(Vec2(0, 0));
text->setPosition(Vec2(220, 73));
starImage->addChild(text);
//初始化右上角生命星星事件
_toolbarRightbg->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("click the toolbarRightbg");
}
});
最后,在ChooseLevel类中的init()函数中调用这些方法,在运行该场景就可以得到文章开头的场景效果。
至此,我们已经开发了主页面场景和关卡选择场景ChooseLevel。虽然ChooseLevel还未开发完,但是已经可以将两个场景串联起来,从而实现场景的切换。点击主页面中的“开始冒险”菜单按钮,可以切换到关卡选择场景。此事件可以通过“开始冒险”的事件监听器进行实现,代码如下:
_startGameBtn->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
switch (type)
{
case ui::Widget::TouchEventType::BEGAN:
{
SimpleAudioEngine::getInstance()->playEffect("Sound/MainMenu/Select.mp3", false, 1.0f, 1.0f, 1.0f);
break;
}
case ui::Widget::TouchEventType::ENDED:
{
//log("_startGameBtn ENDERD");
//跳转场景
auto chooseLevel = ChooseLevel::createScene();
Director::getInstance()->replaceScene(chooseLevel);
break;
}
default:
break;
}
});
TIPS:注意需要#include “ChooseLevel.h”。
源代码
最后,附上ChooseLevel.h、ChooseLevel.cpp的源代码。
ChooseLevel.h
#pragma once
#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include <string.h>
class ChooseLevel :public cocos2d::Scene
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(ChooseLevel);
private:
void InitUI();
void InitEvent();
private:
cocos2d::ui::Button* _toolbarHome;
cocos2d::ui::Button* _toolbarShop;
cocos2d::ui::Button* _toolbarLeaderboard;
cocos2d::ui::Button* _discountTagStone;
cocos2d::Label* _discountText;
cocos2d::ui::Button* _toolbarRightbg;
static float discountText;
};
ChooseLevel.cpp
#include "ChooseLevel.h"
#include "MainScene.h"
USING_NS_CC;
using namespace cocos2d::ui;
using namespace std;
float ChooseLevel::discountText = 7.5;
cocos2d::Scene* ChooseLevel::createScene()
{
return ChooseLevel::create();
}
bool ChooseLevel::init()
{
if (!Scene::init())
return false;
InitUI();
InitEvent();
return true;
}
void ChooseLevel::InitUI()
{
//背景滚动界面
auto scrollView = ScrollView::create();
scrollView->setDirection(ScrollView::Direction::HORIZONTAL);
scrollView->setTouchEnabled(true);
scrollView->setContentSize(Director::getInstance()->getVisibleSize());
scrollView->setScrollBarEnabled(false);
this->addChild(scrollView);
Sprite* imageView = nullptr;
string filePath;
float nextPosX = 0.f;
for (int i = 0; i < 14; i++)
{
filePath = "ChooseLevel/Map/stage_map_" + to_string(i) + ".png";
imageView = Sprite::create(filePath);
//更改锚点位置是因为第14张地图跟前面的13张地图宽度不一致
imageView->setAnchorPoint(Vec2(0, 0.5));
imageView->setPosition(nextPosX, Director::getInstance()->getVisibleSize().height / 2);
nextPosX += imageView->getContentSize().width;
scrollView->addChild(imageView);
}
scrollView->setInnerContainerSize(Size(nextPosX, Director::getInstance()->getVisibleSize().height));
//UI按钮控件
auto layerUI = Layer::create();
this->addChild(layerUI);
auto leftPanelPinfo = AutoPolygon::generatePolygon("ChooseLevel/stagemap_toolbar_leftbg.png");
//该背景图始终在屏幕的左上角
auto leftPanel = Sprite::create(leftPanelPinfo);
leftPanel->setAnchorPoint(Vec2(0, 1));
leftPanel->setPosition(0, Director::getInstance()->getVisibleSize().height);
leftPanel->setScale(0.7);
layerUI->addChild(leftPanel);
//创建背景图精灵的子节点(三个按钮)
string toolbarHomeFilePath = "ChooseLevel/stagemap_toolbar_home.png";
_toolbarHome = Button::create(toolbarHomeFilePath, toolbarHomeFilePath, "");
_toolbarHome->setAnchorPoint(Vec2(0, 0));
_toolbarHome->setPosition(Vec2(10, 10));
_toolbarHome->setPressedActionEnabled(true);
leftPanel->addChild(_toolbarHome);
string toolbarShopFilePath = "ChooseLevel/stagemap_toolbar_shop.png";
_toolbarShop = Button::create(toolbarShopFilePath, toolbarShopFilePath, "");
_toolbarShop->setAnchorPoint(Vec2(0, 0));
_toolbarShop->setPosition(Vec2(125, 10));
_toolbarShop->setPressedActionEnabled(true);
leftPanel->addChild(_toolbarShop);
string toolbarLeaderboardFilePath = "ChooseLevel/stagemap_toolbar_leaderboard.png";
_toolbarLeaderboard = Button::create(toolbarLeaderboardFilePath, toolbarLeaderboardFilePath, "");
_toolbarLeaderboard->setAnchorPoint(Vec2(0, 0));
_toolbarLeaderboard->setPosition(Vec2(235, 10));
_toolbarLeaderboard->setPressedActionEnabled(true);
leftPanel->addChild(_toolbarLeaderboard);
//创建中间的促销按钮
string discountTagStoneFilePath = "ChooseLevel/zh/discount_tag_stone.png";
_discountTagStone = Button::create(discountTagStoneFilePath, discountTagStoneFilePath, "");
_discountTagStone->setAnchorPoint(Vec2(0.5, 1));
_discountTagStone->setPosition(Vec2(Director::getInstance()->getVisibleSize().width / 2, Director::getInstance()->getVisibleSize().height));
_discountTagStone->setScale(0.7);
_discountText = Label::createWithBMFont("ChooseLevel/discount.fnt", "8");
_discountTagStone->addChild(_discountText);
_discountText->setAnchorPoint(Vec2(0, 0));
_discountText->setPosition(160, 65);
layerUI->addChild(_discountTagStone);
//创建右上角 生命星按钮
string toolbarRightbgFilePath = "ChooseLevel/stagemap_toolbar_rightbg.png";
_toolbarRightbg = Button::create(toolbarRightbgFilePath, toolbarRightbgFilePath, "");
_toolbarRightbg->setAnchorPoint(Vec2(1, 1));
_toolbarRightbg->setPosition(Director::getInstance()->getVisibleSize());
_toolbarRightbg->setScale(0.7);
layerUI->addChild(_toolbarRightbg);
//创建生命星星的图片背景
auto starImage = Sprite::create("ChooseLevel/zh/stagemap_toolbar_overten.png");
starImage->setAnchorPoint(Vec2(1, 1));
starImage->setPosition(Director::getInstance()->getVisibleSize());
starImage->setScale(0.7);
layerUI->addChild(starImage);
auto text = Label::createWithSystemFont("010", "Arial", 24);
text->setAnchorPoint(Vec2(0, 0));
text->setPosition(Vec2(220, 73));
starImage->addChild(text);
}
void ChooseLevel::InitEvent()
{
//初始化首页按钮、商店按钮、排行榜按钮的三个事件
_toolbarHome->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
auto mainScene = MainScene::createScene();
Director::getInstance()->replaceScene(mainScene);
}
});
_toolbarShop->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("open the shop");
}
});
_toolbarLeaderboard->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("open the leaderboard");
}
});
//初始化中间的促销按钮
_discountTagStone->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("open the discountTagStone");
}
});
//初始化右上角生命星星事件
_toolbarRightbg->addTouchEventListener([](Ref* sender, Widget::TouchEventType type) {
if (type == ui::Widget::TouchEventType::ENDED)
{
log("click the toolbarRightbg");
}
});
}
static void problemLoading(const char* filename)
{
printf("Error while loading:%s\n", filename);
printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in ChooseLevel.cpp\n");
}