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;
};

滑动条对话框的私有属性。

扫描二维码关注公众号,回复: 4696794 查看本文章

然后是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());
        }

如果点击的是商店的话,只有等级足够并且金币足够的情况下才会弹出滑动条对话框。

种植逻辑不需要修改。

编译运行:

 至此,滑动条对话框加入完成。

代码链接:

https://github.com/sky94520/Farm/tree/Farm-08

猜你喜欢

转载自blog.csdn.net/bull521/article/details/85335147