CocoStudio sample讲解 DemoShop商店界面

CocoStudio sample讲解 DemoShop商店界面
CocoStuido sample----DemoShop源代码地址
https://github.com/chukong/CocoStudioSamples
大家可以预先下载这个源代码, 本教程所有内容均包含在内

一、目标
本节目标是做一个商店界面,包括商品的展示和购买,用户的交互以及动态改变购买的物品属性。效果如下图:

      <img title = '01.gif' src='http://cdn.cocimg.com/bbs/attachment/Fid_48/48_183396_9552c0d64d139b0.gif' > 

二、分析界面
本节的内容较为复杂,开始之前我们分解一下界面,这个商品demo分为主窗口层、商品层、排行榜层、购买弹出层。

      <img title = '2.png' src='http://cdn.cocimg.com/bbs/attachment/Fid_48/48_183396_c91c7d022ecf472.png' > 

三、构建界面

打开UI编辑器,创建一个新的UI工程,起名为“DemoShop”,资源可以在示例中获取。
开始编辑之前先把给根节点配置一个背景图片,接下来就可以开始布局界面了。

如上图所示,出来有两个文本作为标题外还有两个滚动层及右下角一个按钮。这样就完成了主体,下面制作物品单元,由于界面中的所有商品单元都是十分类似,所以我们可以先制作一个,然后采用复制并逐个调整的办法。
下图中是用一个panel中添加的所有子控件,为了清晰暂未给容器添加背景。

添加完成子控件后可以配置容器的图片,然后记得将颜色混合设置为“无颜色”。

接下来是复制单元格,开始复制前先将这个单元格拖动到对应滚动层内,注意这里说的不是位置上的关系的“内”,而是层级关系的“内”。

将单元格复制到左侧的商品层内就可以开始使用复制的功能了,这里可以使用“Ctrl+C”、“Ctrl+V”来快速完成。这里注意由于每一次复制后的空间都会保持跟原控件相同,所以每一次复制都要将复制后的控件移动到其他位置,复制完成后可以通过布局快捷键快速的调整位置。
如选中左侧的4个单元个,分别设置纵向对其、纵向等距即可快速的调整到合适位置。

接下来是右侧的滚动层,我们也可以先将一个单元格拖入到右侧的滚动层,保证在层级关系和位置都在右侧的滚动层内,如下图:

右侧的列表是一个可以滚动的容器,里面包含了8个单元格,明显超出了显示的区域,我们需要先将滚动层的“滚动区域高度”设置大于滚动层本身的高度。如下图:

快速复制控件并调整位置,商品的界面完成,制作完成后就可以勾选“裁剪”属性,这样滚动层超出自身区域的内容将不会显示,清理滚动层的背景颜色。将两个滚动层的颜色设置为“无颜色”。

下面还剩一个弹出层,这个弹出层虽然内容较多,但是都是前面介绍过的内容,这里不做细述。制作完成后将其放置在画布中央并隐藏即可。

注意,由于本示例中控件非常多,关于给每一个控件的命名请自行参考示例程序,如果是自己定义的,一定要在程序中修改对应控件的名称。

四、代码实现

头文件(定义宏及全局变量)




#ifndef __TestCpp__CocosGUIExamplesWeaponScene__
#define __TestCpp__CocosGUIExamplesWeaponScene__


#include "cocos2d.h"
#include "cocos-ext.h"
//#include "../../testBasic.h"


USING_NS_CC;
USING_NS_CC_EXT;
using namespace gui;


#define WEAPON_ITEM_LAYOUT_TAG  1
//shop中两个滚动层子容器的item起始tag值
#define SHOP_ITEM_LAYOUT_TAG        100
#define RANKING_ITEM_LAYOUT_TAG     200
//定义三种货币的默认金额
#define COUPON_MAX                  300
#define BINDING_MAX                 400
#define MEDAL_MAX                   500


class CocosGUIExamplesWeaponScene : public CCScene
{        
public:
    CocosGUIExamplesWeaponScene();
    ~CocosGUIExamplesWeaponScene();
 
    virtual void onEnter();
    virtual void onExit();
 
protected:
    // a selector callback
    void menuCloseCallback(CCObject* pSender, TouchEventType type);
 
    // shop
    void ShopInit();
 
    // popup
    void popupInit();
    void popupClose(CCObject* pSender, TouchEventType type);
    void popupLogic(CCObject* pSender, TouchEventType type);
    void popupCalculate(CCObject* pSender, TouchEventType type);    
 
protected:
    TouchGroup* m_pUILayer;
 
    int m_nIndex;//记录点击的物品层索引
    int m_nCount;//全局记录购买数量
    int m_nCoupon;//优惠券的数额
    int m_nBinding;//绑定的金钱数额
    int m_nMedal;//奖励的金钱数额
};


#endif /* defined(__TestCpp__CocosGUIExamplesWeaponScene__) */


实现文件








#include "CocosGUIExamplesWeaponScene.h"


//商品图片索引
const char* shop_textures =
{
    "cocosgui/gui_examples/DemoShop/armour.png",
    "cocosgui/gui_examples/DemoShop/helmet.png",
    "cocosgui/gui_examples/DemoShop/shield.png",
    "cocosgui/gui_examples/DemoShop/sword.png",
    "cocosgui/gui_examples/DemoShop/gloves.png",
    "cocosgui/gui_examples/DemoShop/dimensity.png",
    "cocosgui/gui_examples/DemoShop/dart.png",
    "cocosgui/gui_examples/DemoShop/backpack.png",
};
//商品名称数组
const char* shop_names =
{
    "Armour",
    "Helmet",
    "Shield",
    "Sword",
    "Gloves",
    "Dimensity",
    "Dart",
    "Backpack",
};
//商品种类数组
const char* shop_price_units =
{
    "Counpon",
    "Binding",
    "Medal",
    "Counpon",
    "Binding",
    "Medal",
    "Counpon",
    "Binding",
};
//商品价格索引
const int shop_prices =
{
    19,
    10,
    22,
    20,
    8,
    17,
    5,
    4,
};
//初始化函数
CocosGUIExamplesWeaponScene::CocosGUIExamplesWeaponScene()
: m_nIndex(-1)
, m_nCount(0)
, m_nCoupon(COUPON_MAX)
, m_nBinding(BINDING_MAX)
, m_nMedal(MEDAL_MAX)
{
    CCScene::init();
}
//析构函数
CocosGUIExamplesWeaponScene::~CocosGUIExamplesWeaponScene()
{
 
}
//进入场景
void CocosGUIExamplesWeaponScene::onEnter()
{
    CCScene::onEnter();
 
    m_pUILayer = TouchGroup::create();
    m_pUILayer->scheduleUpdate();
    addChild(m_pUILayer);
 
    ShopInit();
    popupInit();
//    BuyInit();    
}
//退出场景
void CocosGUIExamplesWeaponScene::onExit()
{
 //移除主体层
    m_pUILayer->removeFromParent();
    //清理内存
    SceneReader::sharedSceneReader()->purge();
    GUIReader::shareReader()->purge();
 cocos2d::extension::ActionManager::shareManager()->purge();
 
    CCScene::onExit();
}
//关闭按钮回调方法
void CocosGUIExamplesWeaponScene::menuCloseCallback(CCObject* pSender, TouchEventType type)
{
 //判断点击抬起,如果抬起执行结束
    if (type == TOUCH_EVENT_ENDED)
    {
        CCDirector::sharedDirector()->end();
 
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        exit(0);
#endif
    }
}


// 初始化画面
void CocosGUIExamplesWeaponScene::ShopInit()
{
    // 从json文件加载UI界面
    Layout* shop_root = static_cast<Layout*>(GUIReader::shareReader()->widgetFromJsonFile("cocosgui/gui_examples/DemoShop/DemoShop.json"));
    m_pUILayer->addWidget(shop_root);    
 
    //获取左侧商品层
    ScrollView* shop_scrollview = static_cast<ScrollView*>(shop_root->getChildByName("shop_ScrollView"));
    //遍历所有的子容器
    for (int i = 0; i < shop_scrollview->getChildren()->count(); ++i)
    {
 //获取指定索引的子容器
        Layout* shop_layout = static_cast<Layout*>(shop_scrollview->getChildren()->objectAtIndex(i));
        shop_layout->setTag(SHOP_ITEM_LAYOUT_TAG + i);
 
        //查找指定容器内的名为“buy_Button”按钮
        Button* buy_button = static_cast<Button*>(shop_layout->getChildByName("buy_Button"));
        buy_button->addTouchEventListener(this, toucheventselector(CocosGUIExamplesWeaponScene::popupLogic));
    }
 
    // 获取右侧排行榜层
    ScrollView* ranking_scrollview = static_cast<ScrollView*>(shop_root->getChildByName("ranking_ScrollView"));
    //遍历排行榜内的子容器
    for (int i = 0; i < ranking_scrollview->getChildren()->count(); ++i)
    {
        Layout* ranking_layout = static_cast<Layout*>(ranking_scrollview->getChildren()->objectAtIndex(i));
 
        for (int j = 0; j < shop_scrollview->getChildren()->count(); ++j)
        {
            Layout* shop_layout = static_cast<Layout*>(shop_scrollview->getChildren()->objectAtIndex(j));
            if (strcmp(ranking_layout->getName(), shop_layout->getName()) == 0)
            {
                ranking_layout->setTag(RANKING_ITEM_LAYOUT_TAG + (shop_layout->getTag() - SHOP_ITEM_LAYOUT_TAG));
            }
        }
    }
    for (int i = 0; i < ranking_scrollview->getChildren()->count(); ++i)
    {
        Layout* ranking_layout = static_cast<Layout*>(ranking_scrollview->getChildren()->objectAtIndex(i));
 
        // 购买按钮
        Button* buy_button = static_cast<Button*>(ranking_layout->getChildByName("buy_Button"));
        buy_button->addTouchEventListener(this, toucheventselector(CocosGUIExamplesWeaponScene::popupLogic));
    }
 
    // 获取返回按钮并添加事件监听(右下角按钮)
    Button* back_button = static_cast<Button*>(shop_root->getChildByName("back_Button"));
    back_button->addTouchEventListener(this, toucheventselector(CocosGUIExamplesWeaponScene::menuCloseCallback));
}


//初始化弹出框
void CocosGUIExamplesWeaponScene::popupInit()
{
    // 获取弹出框
    Layout* buy_layout = static_cast<Layout*>(m_pUILayer->getWidgetByName("buy_Panel"));
 
    // 添加数量按钮
    Button* add_button = static_cast<Button*>(buy_layout->getChildByName("add_Button"));
    add_button->addTouchEventListener(this, toucheventselector(CocosGUIExamplesWeaponScene::popupCalculate));
 
    // 减少数量按钮
    Button* sub_button = static_cast<Button*>(buy_layout->getChildByName("sub_Button"));
    sub_button->addTouchEventListener(this, toucheventselector(CocosGUIExamplesWeaponScene::popupCalculate));
 
    //
    LabelAtlas* number_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("number_LabelAtlas"));
    number_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nCount)->getCString());
 
    // coupon number labelatlas
    LabelAtlas* couponNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("coupon_number_LabelAtlas"));
    couponNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nCoupon)->getCString());
 
    // binding number labelatlas
    LabelAtlas* bindingNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("binding_number_LabelAtlas"));
    bindingNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nBinding)->getCString());
 
    // medal number labelatlas
    LabelAtlas* medalNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("medal_number_LabelAtlas"));
    medalNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nMedal)->getCString());
 
    // 购买按钮
    Button* buy_button = static_cast<Button*>(buy_layout->getChildByName("buy_Button"));
    buy_button->addTouchEventListener(this, toucheventselector(CocosGUIExamplesWeaponScene::popupClose));
    buy_button->setTouchEnabled(true);
 
    // 关闭按钮
    Button* close_button = static_cast<Button*>(buy_layout->getChildByName("close_Button"));
    close_button->addTouchEventListener(this, toucheventselector(CocosGUIExamplesWeaponScene::popupClose));
}
//点击弹出框的关闭按钮的事件
void CocosGUIExamplesWeaponScene::popupClose(CCObject *pSender, TouchEventType type)
{
    if (type == TOUCH_EVENT_ENDED)
    {
 //查找购买弹出框并隐藏
        Layout* buy_layout = static_cast<Layout*>(m_pUILayer->getWidgetByName("buy_Panel"));
        buy_layout->setVisible(false);
 
        // process shop ranking touchEnabled
        CCObject* obj = NULL;
 
        //左侧商品滚动层
        ScrollView* shop_scrollview = static_cast<ScrollView*>(m_pUILayer->getWidgetByName("shop_ScrollView"));
        //遍历控件,
        CCARRAY_FOREACH(shop_scrollview->getChildren(), obj)
        {
            Layout* shop_layout = static_cast<Layout*>(obj);
 
            // 将所有的buy_button设置为可点击
            Button* buy_button = static_cast<Button*>(shop_layout->getChildByName("buy_Button"));
            buy_button->setTouchEnabled(true);
        }
 
        // 排行榜层
        ScrollView* ranking_scrollview = static_cast<ScrollView*>(m_pUILayer->getWidgetByName("ranking_ScrollView"));
        // ranking scrollview children
        CCARRAY_FOREACH(ranking_scrollview->getChildren(), obj)
        {
            Layout* ranking_layout = static_cast<Layout*>(obj);
 
            //
            Button* buy_button = static_cast<Button*>(ranking_layout->getChildByName("buy_Button"));
            buy_button->setTouchEnabled(true);
        }
    }
}
//弹出购买层
void CocosGUIExamplesWeaponScene::popupLogic(CCObject *pSender, TouchEventType type)
{
    if (type == TOUCH_EVENT_ENDED)
    {        
        Widget* widget = static_cast<Widget*>(pSender);
        Widget* parent = static_cast<Widget*>(widget->getParent());
 
        // buy layout
        Layout* buy_layout = static_cast<Layout*>(m_pUILayer->getWidgetByName("buy_Panel"));
        buy_layout->setVisible(true);
 
        // icon imageview
        ImageView* icon_imageview = static_cast<ImageView*>(buy_layout->getChildByName("icon_ImageView"));
        // name labelBMFont
        LabelBMFont* name_labelBMFont = static_cast<LabelBMFont*>(buy_layout->getChildByName("name_LabelBMFont"));
        // price unit labelBMFont
        LabelBMFont* priceUnit_labelBMFont = static_cast<LabelBMFont*>(buy_layout->getChildByName("price_unit_LabelBMFont"));
        // price labelBMFont
        LabelBMFont* price_labelBMFont = static_cast<LabelBMFont*>(buy_layout->getChildByName("price_LabelBMFont"));
 
        ScrollView* shop_scrollview = static_cast<ScrollView*>(m_pUILayer->getWidgetByName("shop_ScrollView"));
        ScrollView* ranking_scrollview = static_cast<ScrollView*>(m_pUILayer->getWidgetByName("ranking_ScrollView"));
        int tag = parent->getTag();
        int index = 0;
 //通过点击的按钮tag值设置index值,用于加载不同的图片
        if (tag >= SHOP_ITEM_LAYOUT_TAG && tag <= shop_scrollview->getChildren()->count() + SHOP_ITEM_LAYOUT_TAG)
        {
            index = tag - SHOP_ITEM_LAYOUT_TAG;
        }
        else if (tag >= RANKING_ITEM_LAYOUT_TAG && tag <= ranking_scrollview->getChildren()->count() + RANKING_ITEM_LAYOUT_TAG)
        {
            index = tag - RANKING_ITEM_LAYOUT_TAG;
        }
        m_nIndex = index;
        icon_imageview->loadTexture(shop_textures);//根据索引加载商品图片
        name_labelBMFont->setText(shop_names);//设置商品名称
        priceUnit_labelBMFont->setText(shop_price_units);//设置商品种类
        price_labelBMFont->setText(CCString::createWithFormat("%d", shop_prices)->getCString());//设置价格
 
 
        //重置购买数量
        m_nCount = 0;
        // number labelatlas
        LabelAtlas* number_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("number_LabelAtlas"));
        number_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nCount)->getCString());
 
        m_nCoupon = COUPON_MAX;
        m_nBinding = BINDING_MAX;
        m_nMedal = MEDAL_MAX;
        // coupon number labelatlas
        LabelAtlas* couponNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("coupon_number_LabelAtlas"));
        couponNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nCoupon)->getCString());
 
        // binding number labelatlas
        LabelAtlas* bindingNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("binding_number_LabelAtlas"));
        bindingNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nBinding)->getCString());
 
        // medal number labelatlas
        LabelAtlas* medalNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("medal_number_LabelAtlas"));
        medalNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nMedal)->getCString());
 
 
        // process shop ranking touchEnabled
        CCObject* obj = NULL;
 
        // shop scrollview children
        CCARRAY_FOREACH(shop_scrollview->getChildren(), obj)
        {
            Layout* shop_layout = static_cast<Layout*>(obj);
 
            // buy button
            Button* buy_button = static_cast<Button*>(shop_layout->getChildByName("buy_Button"));
            buy_button->setTouchEnabled(false);
        }
 
        // ranking scrollview children
        CCARRAY_FOREACH(ranking_scrollview->getChildren(), obj)
        {
            Layout* ranking_layout = static_cast<Layout*>(obj);
 
            // buy button
            Button* buy_button = static_cast<Button*>(ranking_layout->getChildByName("buy_Button"));
            buy_button->setTouchEnabled(false);
        }
    }
}
//控制数量的按钮回调
void CocosGUIExamplesWeaponScene::popupCalculate(CCObject *pSender, TouchEventType type)
{
    if (type == TOUCH_EVENT_ENDED)
    {
        Button* button = static_cast<Button*>(pSender);
        Widget* buy_layout = static_cast<Widget*>(button->getParent());
 
        int price = shop_prices;
        if (strcmp(button->getName(), "add_Button") == 0)   // 判断是添加数量按钮
        {
            if (strcmp(shop_price_units, "Counpon") == 0)
            {
                if (m_nCoupon >= price)
                {
                    m_nCount++;
                    m_nCoupon -= price;
                }                
            }
            else if (strcmp(shop_price_units, "Binding") == 0)
            {
                if (m_nBinding >= price)
                {
                    m_nCount++;
                    m_nBinding -= price;
                }
            }
            if (strcmp(shop_price_units, "Medal") == 0)
            {
                if (m_nMedal >= price)
                {
                    m_nCount++;
                    m_nMedal -= price;
                }
            }
        }
        else if (strcmp(button->getName(), "sub_Button") == 0)  // 如果是减少按钮
        {
            if (m_nCount > 0)
            {
                m_nCount--;
 
                if (strcmp(shop_price_units, "Counpon") == 0)
                {
                    m_nCoupon += price;
                }
                else if (strcmp(shop_price_units, "Binding") == 0)
                {
                    m_nBinding += price;
                }
                if (strcmp(shop_price_units, "Medal") == 0)
                {
                    m_nMedal += price;
                }
            }
        }
 
        //设置数值
        LabelAtlas* number_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("number_LabelAtlas"));
        number_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nCount)->getCString());
 
        // coupon number labelatlas
        LabelAtlas* couponNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("coupon_number_LabelAtlas"));
        couponNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nCoupon)->getCString());
 
        // binding number labelatlas
        LabelAtlas* bindingNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("binding_number_LabelAtlas"));
        bindingNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nBinding)->getCString());
 
        // medal number labelatlas
        LabelAtlas* medalNumber_labelAtlas = static_cast<LabelAtlas*>(buy_layout->getChildByName("medal_number_LabelAtlas"));
        medalNumber_labelAtlas->setStringValue(CCString::createWithFormat("%d", m_nMedal)->getCString());
    }

}






我喜欢这样的教程。谢谢!

感谢您的支持,我们会更加努力做出更多更优秀的教程来帮助大家快速入门我们的CocoStudio工具。

LZ,按照你的例子我给按钮添加方法
button1->addTouchEventListener(this, toucheventselector(Test::onButton));
但是这样写怎么抱错了?2.1.4的版本里是这样添加的:
ButtonHome->addReleaseEvent(this, coco_releaseselector(HelpScene::onButtonHelp));,我能很清楚的知道是添加什么状态下的方法,例如是点击按钮还是按下按钮,但是新的版本里,也就是你这个例子,我没看出来哪里给它设置了点击方法啊,而且我按照你写的竟然报错了,求指教啊。。。

您好,该教程是cocos2d-x2.2.3版本的教程,请勿使用2.1.4版本。教程的代码都在github仓库,需要的话直接下载工程自行编译。感谢您对CocoStudio的支持。

cocos2d-x2.2.3版本现在好像没有了,找不到下载地址了?

版主,2.2.3版本在哪里可以下载吗?求地址呢。

哥们你误会了,我说的是我现在用的就是2.2.3的版本,我也找Test工程里面看了,是用 addTouchEventListener(this, toucheventselector(Test::onButton)); 这个方法,我也是这样写的,但是就是不知道为啥报错了,我的类继承与 UILayer,而Test例子里继承的是UIScene,请问是这个原因吗?我觉得给按钮添加方法只是个很简单的过程,是不是新版本里要注意什么细节而我没注意到?

你给谁添加的事件呢?不是UIButton么?如果是的话你是否在编辑器中勾选交互选项呢?常见的错误一个是没有勾选“交互属性”,一个是事件添加错误。请检查这两项。感谢您对CocoStudio的支持。

到cocos2d-x.org官网下载。http://www.cocos2d-x.org/download

它给我报这样的错误,难道是什么头文件没导入吗?超级郁闷。。。。。:6:

你当前类的类名是“test”?要确保Test::onButton方法已经声明。

首先感谢你的帮助,但是我真的声明了啊。。:6:

虽然我的代码略显稚嫩,难道不是这样写的?

找到方法了,希望能帮到跟我同样遇到这个问题的同僚们,2.2.3和3.0版本里都是,在用到这个之前要先开辟命名空间 using namespace cocos2d::ui;应该不是只有我一个人遇到这个问题吧?

我想问一下版主,像你这样做的话,如果弹出层的按钮刚好与下面那层的按钮重叠了,不会发生触摸事件穿透的问题么,?或者是版主怎么解决的,我遇到了类似问题

:14: 忍不住要赞一个,希望有更多这样的教程~

突然想到一个问题,CocoStudio可以用CCSpriteBatchNode吗?像这个背包界面,这么多元素,如果能批次渲染,会好些!

两个按钮是有优先级的,后添加的按钮会被触发事件。

现在armature是支持bathnode了,但是UI由于结构的特殊性暂不支持,后期会考虑与原生控件做整合。