SDL農場遊戲開發 9.滑動對話方塊
本節實現滑動條對話方塊,介面如下:
滑動條對話方塊用在購買物品和出售物品時的個數選擇 ,同時也能有效避免玩家的誤碰操作。
該對話方塊主要使用了滑動條,兩個按鈕控制元件以及數個標籤控制元件,它對應的UI檔案是scene/slider_dialog.xml。(使用了其他引擎請根據需要自行建立介面檔案,也可在程式碼中生成)。
上一節實現的物品層的優先順序為-1和-2,而對話方塊的優先順序應該比物品層的優先順序要大,以便於捕獲併吞並事件,因此這裡為-3和-4。像確認按鈕、取消按鈕等控制元件的優先順序為-4,吞併監聽器的優先順序為-3。
注:控制元件的優先順序也可以為其他值,但要保證控制元件之間的相對大小不能改變。
1.SliderDialog
首先是SliderDialog.h
/** * 滑動條對話方塊 * 需要:名稱為fonts/1.fnt sprite/slider_dialog_ui_res.xml * 注意:Widget優先順序為-4 阻擋監聽器優先順序為-3 */ class SliderDialog : public Node { public: /** * 當點選確認或者取消時回撥該函式 * @param bool 確認/取消 * @param int 當前選擇的個數 */ typedef function<void (bool, int)> sliderDialogCallback; public: SliderDialog(); virtual ~SliderDialog(); CREATE_FUNC(SliderDialog); bool init();
滑動條對話方塊相對於物品層來說功能要單一一些,因為它只需要一個回撥函式來確認該對話方塊是點選了 確認/取消 按鈕以及當前選擇的個數。
public: SliderDialog(); virtual ~SliderDialog(); CREATE_FUNC(SliderDialog); bool init(); /** * 更新顯示的標題 * @param title 標題 */ void updateShowingTitle(const string& title); /** * 設定最大值 * @param percent 滑動條最大值 */ void setMaxPercent(int percent); /** * 設定當前進度 * @param percent 當前進度 */ void setPercent(int percent); /** * 設定顯示的單價 * @param price */ void setPrice(int price); /** * 設定回撥函式 也可以負責隱藏此對話方塊 */ void setCallback(const sliderDialogCallback& callback); bool isShowing() const; void setShowing(bool showing);
公共函式,允許在對話方塊顯示之前更新標題、最大值、當前選中值和顯示的物品的單價等。
private:
void clickBtnCallback(Object* sender, bool ok);
void sliderCallback(ui::Slider* slider, ui::Slider::EventType type);
bool onTouchBegan(Touch* touch, SDL_Event* event);
void onTouchMoved(Touch* touch, SDL_Event* event);
void onTouchEnded(Touch* touch, SDL_Event* event);
clickBtnCallback是確認和取消按鈕的回撥函式,第二個引數ok來標識是確認按鈕還是取消按鈕;sliderCallback則是滑動條在滑動時的回撥函式,它主要用於更新顯示(個數和金幣);後三個函式是吞併監聽器的回撥函式。
private:
sliderDialogCallback m_callback;
Sprite* m_pBackSprite;
//該控制元件使用到了fonts/1.fnt
LabelBMFont* m_pTitleLabel;
ui::Slider* m_pSlider;
LabelAtlas* m_pNumberLabel;
LabelAtlas* m_pWorthLabel;
ui::Button* m_pOKBtn;
ui::Button* m_pCancelBtn;
int m_nPrice;
//吞併事件
EventListenerTouchOneByOne* m_pListener;
bool m_bShowing;
};
滑動條對話方塊的私有屬性。
然後是SliderDialog.cpp中實現。
bool SliderDialog::init()
{
auto manager = ui::UIWidgetManager::getInstance();
auto node = manager->createWidgetsWithXml("scene/slider_dialog.xml");
this->addChild(node);
//獲取有用的控制元件
m_pBackSprite = node->getChildByName<Sprite*>("bg");
m_pTitleLabel = node->getChildByName<LabelBMFont*>("title_text");
m_pSlider = node->getChildByName<ui::Slider*>("number_slider");
m_pSlider->addEventListener(SDL_CALLBACK_2(SliderDialog::sliderCallback, this));
m_pNumberLabel = node->getChildByName<LabelAtlas*>("num_label");
m_pWorthLabel = node->getChildByName<LabelAtlas*>("worth_label");
//確認/取消按鈕
m_pOKBtn = node->getChildByName<ui::Button*>("ok_btn");
m_pCancelBtn = node->getChildByName<ui::Button*>("cancel_btn");
m_pOKBtn->addClickEventListener(SDL_CALLBACK_1(SliderDialog::clickBtnCallback, this, true));
m_pCancelBtn->addClickEventListener(SDL_CALLBACK_1(SliderDialog::clickBtnCallback, this, false)); //預設不顯示
this->setShowing(false);
return true;
}
先是xml檔案生成UI,之後獲到控制元件並繫結函式。
void SliderDialog::updateShowingTitle(const string& title)
{
m_pTitleLabel->setString(title);
}
void SliderDialog::setMaxPercent(int maxPercent)
{
m_pSlider->setMaxPercent(maxPercent);
//根據當前進度來設定總價值
int percent = (int)m_pSlider->getPercent();
int value = percent * m_nPrice;
m_pNumberLabel->setString(StringUtils::toString(percent));
m_pWorthLabel->setString(StringUtils::toString(value));
}
void SliderDialog::setPercent(int percent)
{
int curPercent = (int)m_pSlider->getPercent();
if (curPercent == percent)
return ;
m_pSlider->setPercent(percent);
//重新獲取當前值
curPercent = (int)m_pSlider->getPercent();
int value = curPercent * m_nPrice;
m_pNumberLabel->setString(StringUtils::toString(percent));
m_pWorthLabel->setString(StringUtils::toString(value));
}
void SliderDialog::setPrice(int price)
{
m_nPrice = price;
}
void SliderDialog::setCallback(const sliderDialogCallback& callback)
{
m_callback = callback;
}
SliderDialog物件是在FarmScene中複用的,所以setMaxPercent和setPercent中加入了容錯,來確保當前選中值小於等於最大值。
void SliderDialog::setShowing(bool showing)
{
if (showing == m_bShowing)
return ;
m_bShowing = showing;
if (m_bShowing)
{
m_pListener = EventListenerTouchOneByOne::create();
m_pListener->onTouchBegan = SDL_CALLBACK_2(SliderDialog::onTouchBegan, this);
m_pListener->onTouchMoved = SDL_CALLBACK_2(SliderDialog::onTouchMoved, this);
m_pListener->onTouchEnded = SDL_CALLBACK_2(SliderDialog::onTouchEnded, this);
m_pListener->setSwallowTouches(true);
m_pListener->setPriority(-3);
_eventDispatcher->addEventListener(m_pListener, this);
}
else if (m_pListener != nullptr)
{
_eventDispatcher->removeEventListener(m_pListener);
m_pListener = nullptr;
}
m_pSlider->setTouchEnabled(m_bShowing);
m_pOKBtn->setTouchEnabled(m_bShowing);
m_pCancelBtn->setTouchEnabled(m_bShowing);
}
和物品層類似,滑動對話方塊也是採用了邏輯隱藏,而物理隱藏交給上層。
void SliderDialog::clickBtnCallback(Object* sender, bool ok)
{
if (m_callback != nullptr)
{
int percent = (int)m_pSlider->getPercent();
m_callback(ok, percent);
}
}
void SliderDialog::sliderCallback(ui::Slider* slider, ui::Slider::EventType type)
{
int percent = (int)slider->getPercent();
int value = percent * m_nPrice;
m_pNumberLabel->setString(StringUtils::toString(percent));
m_pWorthLabel->setString(StringUtils::toString(value));
}
bool SliderDialog::onTouchBegan(Touch* touch, SDL_Event* event)
{
return true;
}
void SliderDialog::onTouchMoved(Touch* touch, SDL_Event* event)
{
}
void SliderDialog::onTouchEnded(Touch* touch, SDL_Event* event)
{
}
2.FarmScene的更新
首先是要在FarmScene.h中新增屬性和回撥函式宣告。
private:
//滑動條對話方塊回撥函式
void sliderDialogCallback(bool ret, int percent);
private:
//...
//揹包層型別
GoodLayerType m_goodLayerType;
//滑動條對話方塊
SliderDialog* m_pSliderDialog;
接著先在init函式中初始化滑動條對話方塊。
bool FarmScene::init()
{
//...
//物品層
//滑動條對話方塊
m_pSliderDialog = SliderDialog::create();
m_pSliderDialog->setPosition(visibleSize.width * 0.5f, visibleSize.height * 0.5f);
m_pSliderDialog->setVisible(false);
m_pSliderDialog->setCallback(SDL_CALLBACK_2(FarmScene::sliderDialogCallback, this));
this->addChild(m_pSliderDialog);
//初始化土壤和作物
this->initializeSoilsAndCrops();
/...
return true;
}
此時先把m_pSliderDialog->setVisible(false)註釋掉,編譯執行介面如下:
void FarmScene::sliderDialogCallback(bool ret, int percent)
{
m_pSliderDialog->setVisible(false);
m_pSliderDialog->setShowing(false);
//點選了取消按鈕
if (!ret)
return;
auto dynamicData = DynamicData::getInstance();
Value gold = this->getValueOfKey(GOLD_KEY);
//倉庫 出售
if (m_goodLayerType == GoodLayerType::Warehouse)
{
int number = m_pSelectedGood->getNumber();
gold = gold.asInt() + m_pSelectedGood->getCost() * percent;
number -= percent;
//減少物品
dynamicData->subGood(m_pSelectedGood, percent);
//解除引用
if (number == 0)
{
SDL_SAFE_RELEASE_NULL(m_pSelectedGood);
}
//更新顯示 可能會改變m_pSelectedGood
vector<Good*>* pBagGoodList = DynamicData::getInstance()->getBagGoodList();
this->showGoodLayer("bag_title_txt1.png", "sell_text.png", pBagGoodList, m_nCurPage);
}
//商店 購買
else if (m_goodLayerType == GoodLayerType::Shop)
{
string goodName = m_pSelectedGood->getGoodName();
auto type = m_pSelectedGood->getGoodType();
Value gold = this->getValueOfKey(GOLD_KEY);
gold = gold.asInt() - m_pSelectedGood->getCost() * percent;
//增加物品
dynamicData->addGood(type, goodName, percent);
}
//金幣回寫
dynamicData->setValueOfKey(GOLD_KEY, gold);
//更新金幣顯示
m_pFarmUILayer->updateShowingGold(gold.asInt());
m_pGoodLayer->updateShowingGold(gold.asInt());
}
在本遊戲裡使用到滑動條對話方塊的目前只有購買和出售,所以只是對這兩種情況進行了判斷。
接著就是要更改FarmScene::useBtnCallback函數了,當前邏輯應該是 在點選了購買或出售按鈕後出現滑動條對話方塊,而不是直接對物品進行操作。
//出售
if (m_goodLayerType == GoodLayerType::Warehouse)
{
int number = m_pSelectedGood->getNumber();
int gold = m_pSelectedGood->getCost();
//填充
m_pSliderDialog->setVisible(true);
m_pSliderDialog->setShowing(true);
m_pSliderDialog->setPrice(gold);
m_pSliderDialog->setMaxPercent(number);
//設定當前進度值為1
m_pSliderDialog->setPercent(1);
m_pSliderDialog->updateShowingTitle(m_pSelectedGood->getName());
}
如果點選的是出售按鈕的話,則撥出滑動條對話方塊即可。
else if (m_goodLayerType == GoodLayerType::Shop)
{
//判斷等級
string goodName = m_pSelectedGood->getGoodName();
int id = atoi(goodName.c_str());
auto pCropSt = StaticData::getInstance()->getCropStructByID(id);
auto lv = this->getValueOfKey(FARM_LEVEL_KEY).asInt();
if (lv < pCropSt->level)
{
//auto text = STATIC_DATA_STRING("not_enough_lv_text");
//Toast::makeText(this, text, Color3B(255, 255, 255), 1.f);
printf("level low\n");
return ;
}
//撥出滑動條對話方塊
Value gold = this->getValueOfKey(GOLD_KEY);
int cost = m_pSelectedGood->getCost();
int maxPercent = gold.asInt() / cost;
//一個都買不起,提示
if (maxPercent == 0)
{
//auto text = STATIC_DATA_STRING("not_enough_money_text");
//Toast::makeText(this, text, Color3B(255, 255, 255), 1.f);
printf("not enough money\n");
return ;
}
m_pSliderDialog->setVisible(true);
m_pSliderDialog->setShowing(true);
m_pSliderDialog->setPrice(cost);
m_pSliderDialog->setMaxPercent(maxPercent);
//設定當前進度為1
m_pSliderDialog->setPercent(1);
m_pSliderDialog->updateShowingTitle(m_pSelectedGood->getName());
}
如果點選的是商店的話,只有等級足夠並且金幣足夠的情況下才會彈出滑動條對話方塊。
種植邏輯不需要修改。
編譯執行:
至此,滑動條對話方塊加入完成。
程式碼連結: