cocos2dx 背包界面的实现

由于最近负面情绪比较多,说以废话比较多。看博客的朋友可以直接跳到下面的==正题==

如果是连博客都不想看的,可以直接看代码(最下方)。

最近的2周感觉被项目给压榨了。连着忙了2周,任务比较赶是一个,其二,是我们程序这边终于有了个管事的了。

上周6下午开会讨论了一下我们工作中的问题。主要有4个。


各个人做的事情没有人确认。所以定了个这样的流程。

通过禅道系统。

  1. 比如,现在有一个任务:背包系统。项目经理把任务会指派给策划。
  2. 策划完成策划案后,交给项目经理确认。
  3. 项目经理在确认之后,再把任务指派给美术和程序,美术和程序此时需要做确认操作(看策划案,理清需求。美术会在这个时候拆分任务,估计时间。程序暂时只是了解一下,等到美术出图再做任务拆分和时间估计)。
  4. 美术绘图拆图完成后,把图片上传到svn的某个位置。交给项目经理确认。
  5. 项目经理确认后,美术把任务指派给程序。程序确认策划案和图片资源后,开工。
  6. 程序完成后,先给美术确认。美术确认后,交给策划确认(策划还负责测试工作)
  7. 策划确认后,项目经理关闭任务。

不知道同事做了什么。

以后要写任务日志了。而且要及时的把已经完成的任务关闭。

通过禅道,每个人都要记录下自己在做某个任务的时候,做了什么,以及耗时,预计剩余时间。

时间粒度最好在0.5小时到2小时之间。比如:背包界面的日志如下:

  • 开始做背包界面 预计剩余耗时: 10小时
  • 界面UI摆放及按钮事件的相应 耗时:2小时 预计剩余耗时: 8小时
  • 背包中单个物品(图标,等级、数量、品质、名字的显示) 耗时: 2小时 预计剩余耗时: 6小时
  • 背包右侧滑块的实现(滑块大小的调整已经位置同步)(滑块用于提示背包滚动的百分比) 耗时:2小时 预计剩余耗时 4小时
  • fix bug : 触摸机制导致点在物品上时不能滑动背包,只有点在物品间空隙位置时才能滑动背包(已解决) 耗时:1小时 预计剩余耗时 4小时
  • 背包中单个物品(装备满级标志,装备碎片标志,已装备的表示的显示) 耗时: 1小时
  • 背包界面以实现,实际耗时8小时

弃用qq,改用rtx(qq太多干扰的信息了,)。虽然通信靠吼是最实时的,但容易打断思路,如果同时正在专注中,用rtx。


测试力度不够,人都是有惰性的。一个迭代是否完成了,需要认真的测试。而不是仅仅看看程序的演示。


ok,上周6下午的讨论大致就是上面4个。

就这样,周一实践了现在。感觉就是,工作效率是蛮高的,气氛也蛮不错。

当然,也有一个负面的情绪。。现在下班后完全不想动脑子了,上班太累了。这个也是自己的问题,以后不要一直坐在那,要多去阳台喝喝水。


说到游戏,我近几个月都在都战斗,所以UI也有点生疏了。之前的UI是用cocosstudio1.6的ui编辑器弄的。我感觉既然需要重新熟悉一遍,还不如就用最新的cocos 2.2.6。

背包系统包括了:

  1. 背包界面(背包里有装备,装备碎片,道具,宝箱,开宝箱的钥匙)
  2. 装备详情,强化,出售
  3. 选择装备界面(用于伙伴选择装备(一个玩家可以有n个伙伴),装备强化也是吃装备的(这个是脑子进水了的))
  4. 装备碎片合成装备
  5. 道具???(突然发现我也不知道道具有那些要做)
  6. 开宝箱(又是一个脑子进水的,要在背包系统里面开宝箱,这明明是宝箱系统的东西)

— 以上写于20150514:周4夜间

周五开会说要在月底之前赶出:商城、新手引导。加上之前的需要重做的背包系统、伙伴系统、主界面、关卡选择。还有那些需要换皮的界面(可以说是所有以前做过的界面、策划说功能没怎么改、可实际情况是、以前的界面都是全屏的、现在是类似弹出的对话框的、都需要重新摆界面)。还有就是暂时被搁置的战斗系统(说是UI太赶,要我先做UI,可,我的战斗系统就没事了吗?)

可以说是一次完整的换皮加添加新功能。程序这边是从5月7号才开始有事做的。也就是说,在我预计需要6-7周的工作,需要在3周内做完。

不要问为什么需要这么久。我们程序虽然有4个,但客户端包括我只有2个程序。还有1个是服务器端。

还有一个是大牛,30岁左右,之前主要做服务器端,是项目经理以前的同事。因为项目赶,上周加入了我们项目。他的C++是很牛逼的。但由于他对cocos2d-x和cocostudio摆UI不熟悉。他能做的也就是管理进度,然后,叫我们加班(比如周六加班啦,平时延长工作时间啦。)。(其实我火了,他竟然扬言晚上会陪我们程序到最后一个人。呵呵,不知道我离公司就20分钟。其实我习惯在公司待到10点,因为公司网速好。但我不是加班,是在弄自己的事。)

好吧,其实我发现了,我们程序这边算是有个头头了。


呵呵,又写了一大堆废话。有部分原因是赶进度真的是身心疲惫。第二个原因是。策划那边严重的混日子。周五我们有个策划正式离职了(公司11个人,他请了我,和另外两个程序吃饭。4个人吃了三百七十多,够可以的了。如果说同事之间是不传递负面情绪的。那我们4个算是朋友吧,有什么不爽都会说的那种。)。项目经理(兼系统策划)在陪产,他老婆就这几天的预产期(不是昨天就是今天了)。还有一个策划,是老板的堂弟,用他的话说,他的工资只有2k多,天天上班玩玩游戏看看游戏直播的。

老板提的需求的改动,策划那边活活从4月初弄到了5月初,美术那边都没赶玩,我们程序这边就更不好开工了。而且很多的交互也提示对话框都没有考虑到。

好了,说了这么多,都是别人的锅。但结果是,要我们背。呵呵,网络是个好东西,可以发泄负面情绪。


正题

背包界面(背包里有装备,装备碎片,道具,宝箱,开宝箱的钥匙)的实现。


界面摆放

先上图,第一张是美术给的效果图

美术给的效果图

第二张是摆好后的背包界面

摆好后的背包界面

下面说说界面摆放的一些技巧。我的习惯是把美术给的效果图直接放在cocos的最底层,然后就可以很方便的对坐标了。如果有某些图片或者控件挡住了效果图的画,我会把他暂时设置为透明度0%。在位置摆好后,再把透明度设置为100%。

接下来是背包的item

背包的Item

ok,UI摆好后,就可以写代码了。


—以上写于20150518:周日夜间(周一凌晨吧)

根据我的习惯,会把界面中的变量剥离处理,我叫他界面model。游戏项目遗留问题我们项目的UI系统是用C++写的。

好了,先定义界面的模型,也就是界面要显示的内容


#ifndef DialogBagModel_h__
#define DialogBagModel_h__

#include <vector>
#include <string>
#include <stdint.h>
#include "NodeBagItemModel.h"

//背包界面的model
struct DialogBagModel
{
    //背包列表
    std::vector<NodeBagItemModel> item_list;

    //背包上限
    int capacity_limit;

    //创建测试数据
    void createTestData(){

        capacity_limit = 90;

        for (int i = 0; i < 20; i++)
        {
            NodeBagItemModel a;
            a.gridModel.grid_bg_color = i % 5 + 1;
            a.gridModel.icon_id = 10001 + i;
            a.gridModel.level = i+1;
            a.gridModel.type = NodeBagGridModel::Equip;
            a.name = "zhuang bei";
            item_list.push_back(a);
        }

    }

    //从模型层获取数据
    void crateFromModel();

};

#endif // DialogBagModel_h__

代码很简单,一个列表,一个上限。还有1个函数用于生成测试数据。

由于项目需要联网获取用户信息,其实是一件比较不利于快速调试的事情。所以,自己生成界面会用到的数据,而不是依赖网络,是一个好的做法。

这样做可以确保如果有问题,可以很容易的分辨是显示的问题还是服务器端数据的问题。


#ifndef DialogBag_h__
#define DialogBag_h__

#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "cocostudio/CocoStudio.h"
USING_NS_CC;
using namespace ui;
using namespace cocostudio;

#include <vector>
#include <string>
#include "NodeBagItem.h"
#include "DialogBagModel.h"

//背包界面
class DialogBag : public Layer{
public:
    //创建界面
    CREATE_FUNC(DialogBag);

    //界面初始化
    virtual bool init();

    virtual void onEnter();
    virtual void onExit();

    //设置界面显示内容
    void setupDialogBag(DialogBagModel* model);

    //绑定cocos文件到程序控件
    void bindingNode(cocos2d::Node* dialog);

    void onBtnCallback(Ref *object, Widget::TouchEventType type);

    //ScrollView滚动事件的回调
    void onSVCallback(Ref*, ui::ScrollView::EventType type);

    //ScrollView触摸事件的回调
    void onSVTouch(Ref *object, Widget::TouchEventType type);

    //背包中某一个item被点击
    void onItemClick(int sno, int id);

    //该函数响应背包内容更新的事件
    void doUpdateViewEvent(EventCustom* event);

    virtual ~DialogBag();
    DialogBag();
private:

    Button* btn_close;//关闭按钮
    Button* btn_back;//返回按钮

    ui::ScrollView* sv;//滚动层

    ImageView* slider_base;//滑块底部
    ImageView* slider_bar;//滑块

    Text* bg_capacity_label;//显示背包容量的文字

    Vector<NodeBagItem*> item_list;//背包item的列表
private:
    float slider_bar_min_pos_y;//用于控制滑块的位置
    float slider_bar_area;//滑块y轴可移动的距离的范围

    DialogBagModel* model_;
    EventListenerCustom* bag_updateEventListener;//用于监听背包内容更新
};

#endif // DialogBag_h__

上面的头文件就是背包界面的Layer。其实可以把这个界面看成是一个模态对话框,因为他屏蔽了下层的触摸事件。

当然,触摸的屏蔽依靠的是Layout,在cocos中添加一个和屏幕一样大的Layout在界面最底部,就好了。

接下来让我们看看界面初始化。其实没什么内容,就是加载csb文件。



bool DialogBag::init()
{
    Layer::init();

    //初始化字段
    slider_bar_min_pos_y = 0;
    slider_bar_area = 0;

    //csb文件位于KouDaiCE D:\temp\ui2\KouDaiCE
    auto dialog = CSLoader::createNode("DialogBag.csb");
    bindingNode(dialog);
    this->addChild(dialog); 

    return true;
}

我在注释中写出类cocos项目的路径,其实是因为我还没有把这个工程放入svn中去。把所有的东西都做好版本管理,是一个好习惯。


void DialogBag::bindingNode(cocos2d::Node* dialog)
{
    //按钮
    btn_close = (Button*)dialog->getChildByName("Button_3");
    btn_back = (Button*)dialog->getChildByName("Button_1");

    btn_close->addTouchEventListener(CC_CALLBACK_2(DialogBag::onBtnCallback, this));
    btn_back->addTouchEventListener(CC_CALLBACK_2(DialogBag::onBtnCallback, this));

    bg_capacity_label = (Text*)dialog->getChildByName("Text_1_0");

    //滚动层
    sv = (ScrollView*)dialog->getChildByName("ScrollView_1");
    sv->addEventListener(CC_CALLBACK_2(DialogBag::onSVCallback, this));
    sv->addTouchEventListener(CC_CALLBACK_2(DialogBag::onSVTouch, this));

    //滑块
    slider_base = (ImageView*)dialog->getChildByName("Image_1");
    slider_bar = (ImageView*)slider_base->getChildByName("Image_2");
}

绑定控件,这里只需要对你感兴趣的控件进行绑定。最后是在cocos中给控件取一个好名字,而不是"Text_1_0""Button_3"。当然,有时候觉得getChildByName不够智能(有时会把控件放在很深的树形节点中),可以使用Helper::seekWidgetByName,不过,前题是跟节点得是Widget而不是Node。当然,你可以自己写一个seekNodeByName

ok,下面是正题。通过界面model设置界面。这里可能写的比较乱,model_是类的成员函数。由于是个指针,所以需要手动控制他的释放。其实直接传入结构体而不是结构体指针也是可以的,而且会更方便(但是要特别注意,如果一个指针释放了。我可以保证他为NULL,但结构体的话,就会看到一堆乱七八糟的值。所以我还是选择用指针。)

void DialogBag::setupDialogBag(DialogBagModel* model)
{

    CC_SAFE_DELETE(model_);
    model_ = model;

    //设置背包容量的文字---------------------------------------------------------------
    int capacity = model_->capacity_limit;
    int size = model_->item_list.size();
    bg_capacity_label->setString(StringUtils::format("%d/%d", size, capacity));


    //根据item的数量动态设置其在背包中的位置,已经滚动区域的大小------------------------
    int width = 792;//滚动层的宽
    int height = 352;//滚动层的高
    int padding = 6;//只是为了对坐标,并不是真正的内边距

    int count_per_row = 6;//每行的个数

    float row_width = 127+padding;//127是图片btn_daojudibang.png
    float column_heigth = height / 2;

    //因为空格子也要显示
    int max = (capacity > size) ? capacity : size;

    //计算列数
    float total_column = ((max - 1) / count_per_row + 1);

    //计算内容区的高度
    int container_hight = total_column*column_heigth;
    container_hight = (container_hight > height) ? container_hight : height;

    sv->setInnerContainerSize(Size(width, container_hight));

    //更新item-------------------------------------------------------------------------
    int old_item_list_size = item_list.size();//默认为0

    //如果有多余的Item,删除多余的item
    if (old_item_list_size>max)
    {
        for (int idx = old_item_list_size; idx < max;idx--)//倒着删
        {
            auto item = item_list.at(idx-1);
            item->removeFromParent();
            item_list.popBack();
        }
    }

    //更新或者创建item
    for (int idx = 0; idx < max; idx++)
    {
        int row = idx % count_per_row;
        int column = idx / count_per_row;

        NodeBagItem* item;
        if (old_item_list_size>idx)
        {
            //复用这个item
            item = item_list.at(idx);
        }
        else{
            //不够就创建
            item = NodeBagItem::create();
            sv->addChild(item);
            item_list.pushBack(item);
        }

        if (idx < size)
        {
            NodeBagItemModel data = model_->item_list[idx];
            item->setupNodeBagItem(data);
        }
        else if (idx < capacity) //空格子
        {
            NodeBagItemModel data;
            data.createGridNothing();
            item->setupNodeBagItem(data);
        }

        //计算并设置位置
        int x = padding/2 + row_width * row;
        int y = padding / 2 + container_hight - (column + 1)*(column_heigth);
        item->setPosition(x, y);

    }

    //计算滑块的大小和位置-------------------------------------------------------------
    float slider_orign_height = slider_base->getContentSize().height;
    float slider_bar_height = slider_orign_height*(2.0 / total_column);
    slider_bar->setContentSize(Size(11, slider_bar_height));

    this->slider_bar_min_pos_y = slider_bar_height;
    this->slider_bar_area = slider_orign_height - slider_bar_height;

    if (slider_bar->getPositionY() < slider_bar_min_pos_y)
    {
        slider_bar->setPositionY(slider_bar_min_pos_y);
    }
}

由于考虑到背包的更新(比如装备强化后(装备吃装备然后升等级),其实这个时候背包页面也是存在的,所以需要更新背包内容)。在更新item的时候,如果item存在,则修改显示内容。如果不存在,则创建一个新的。当然,如果有多余,就删了。

还有就要注意cocos2dx的坐标系的原点在左下角。如果想让添加到ScrollViewInnerContainer里的东西显示在最顶端,就要反过来。或者,把他想成是最高的位置。int y = padding / 2 + container_hight - (column + 1)*(column_heigth);

厄,发现贴了好多代码了。其实我最讨厌在网页里看代码,但是,还是继续吧。已经贴了50%了。




//ScrollView滚动事件的回调
void DialogBag::onSVCallback(Ref*, ScrollView::EventType type)
{
    if (type == ScrollView::EventType::SCROLLING)
    {
        //根据InnerContainer的滚动更新滑块的位置
        float current_pos = -sv->getInnerContainer()->getPositionY();
        float pos_area = sv->getInnerContainerSize().height - sv->getContentSize().height;

        //由于ScrollView开启了回弹效果,所以他的位置会超出范围,所以需要修正
        if (current_pos<0)
        {
            current_pos = 0;
        }
        else if (current_pos>pos_area){
            current_pos = pos_area;
        }

        float percent = current_pos / pos_area;

        //设置滑块的位置
        if (slider_bar_area != 0)
        {
            slider_bar->setPositionY(slider_bar_min_pos_y + slider_bar_area*percent);
        }

    }
}

//ScrollView触摸事件的回调
void DialogBag::onSVTouch(Ref *object, Widget::TouchEventType type)
{
    //由于按钮的点击事件会屏蔽ScrollView的滚动事件。即点击在按钮上的时候是不能拖动滚动层的。
    //所以背包中的item并不是button。而是自己实现的点击判定
    //处理背包中item的点击,模拟按钮的点击效果。
    if (type == Widget::TouchEventType::ENDED)
    {
        auto pos = sv->getTouchEndPosition();
        CCLOG("ended x = %f , y = %f", pos.x, pos.y);

        auto pos_began = sv->getTouchBeganPosition();

        float dis = pos.distance(pos_began);

        for (auto item : item_list)
        {
            auto btn = item->getBtn();
            btn->loadTexture("bag/btn_daojudibang.png");//设置所有item为非选中状态

            if (dis < 10 && btn->hitTest(pos)){//如果移动距离小于10,判断为1次点击操作
                CCLOG("on click item : sno = %lld , id = %d", item->get_sno(), item->get_id());
                onItemClick(item->get_sno(), item->get_id());
            }
        }

    }
    if (type == Widget::TouchEventType::BEGAN)
    {
        auto pos = sv->getTouchBeganPosition();
        CCLOG("began x = %f , y = %f", pos.x, pos.y);

        for (auto item : item_list)
        {
            auto btn = item->getBtn();
            if (btn->hitTest(pos))
            {
                //设置点中的item为点中状态
                btn->loadTexture("bag/pressed_daojudibang.png");
            }
        }

    }
    if (type==Widget::TouchEventType::MOVED)
    {
        auto pos = sv->getTouchMovePosition();
        CCLOG("moved x = %f , y = %f", pos.x, pos.y);
    }
}


ok。一个完整的背包界面就搞定了。

—以上写于20150518:夜间


没想到被cn.cocos2d-x.org给转载了。happy!

其实一个完整的背包还有一大堆肮脏的代码。就一并贴出来吧。首先是和背包项有关的2个类,一个是界面模型,一个是界面。

NodeBagItemModel.h


#ifndef NodeBagItemModel_h__
#define NodeBagItemModel_h__

#include <string>
#include <stdint.h>
#include "NodeBagGrid.h"

class ModelEquipment;
class ModelProp;

//背包中的item的model
struct NodeBagItemModel
{

    int64_t sno;//装备有唯一序列号,道具为0
    std::string name;
    bool is_suipian;
    bool yi_zhuang_bei;
    bool isSelect;//用于装备选择界面,理论上应该移到子类中去。

    NodeBagGridModel gridModel;

    void createGridNothing();//创建一个空的item
    void createFromEquipModel(ModelEquipment* item);//根据装备的相关数据创建界面model,从数据model填充界面model
    void createFromPropModel(ModelProp* item);//根据道具的相关数据创建界面model,从数据model填充界面model

    //肮脏的代码,由于装备碎片的icon_id是合成后的装备的icon_id,再在左上角加一个碎片的碎。
    //所以添加了一个多余的字段,用于获取其原始id
    int debris_id;
};

#endif // NodeBagItemModel_h__

哈哈,代码中其实已经有肮脏的使用了拼音的变量了。bool is_碎片bool 已装备。还有bool isSelect,这个变量是用于装备选择界面(而不是背包界面的,背包选择界面中的item会有选中的效果)的。但由于偷懒就没有拆分成2个类。

NodeBagItemModel.cpp


#include "NodeBagItemModel.h"
#include "ModelEquipment.h"
#include "ModelProp.h"

void NodeBagItemModel::createGridNothing()
{
    this->sno = 0;
    this->name = "";
    this->isSelect = false;

    this->yi_zhuang_bei = false;
    this->is_suipian = false;

    gridModel.icon_id = -1;
    gridModel.level = -1;
    gridModel.grid_bg_color = 1;
    gridModel.num = 0;
    gridModel.is_max_level = false;
}

void NodeBagItemModel::createFromEquipModel(ModelEquipment* item)
{
    this->gridModel.type = NodeBagGridModel::Equip;
    this->gridModel.grid_bg_color = item->getColor();
    this->gridModel.icon_id = item->getId();
    this->gridModel.level = item->getLevel();

    this->sno = item->getSno();
    this->name = item->getName();

    if (item->getLevelLimit() == item->getLevel())
    {
        this->gridModel.is_max_level = true;
    }
    else{
        this->gridModel.is_max_level = false;
    }

    this->yi_zhuang_bei = false;
    if (item->getEquipPartnerId() > 0)
    {
        this->yi_zhuang_bei = true;
    }

    this->is_suipian = false;
}

void NodeBagItemModel::createFromPropModel(ModelProp* item)
{
    this->gridModel.type = NodeBagGridModel::Other;
    this->gridModel.grid_bg_color = 1;
    this->gridModel.icon_id = item->getId();
    this->gridModel.level = -1;
    this->sno = 0;
    this->gridModel.num = item->getNum();
    this->name = item->getName();
    this->gridModel.is_max_level = false;

    this->yi_zhuang_bei = false;

    this->is_suipian = false;
    if (item->getType() == ModelProp::EQUIPMENT_DEBRIS)
    {
        this->is_suipian = true;
        ModelEquipmentDebrisProp* debris_item = ((ModelEquipmentDebrisProp*)item);
        this->gridModel.icon_id = debris_item->getTargetId();

        this->debris_id = item->getId();

    }
}

cpp中的3个函数都是用于初始化结构体的。第一个是什么都没有的空item(由于有背包上限,所以空格子也要显示。这是策划要求的,但目前背包界面还没有做筛选标签(在筛选标签页里,这些空格子估计是不会显示的)。第二个是装备,第三个是道具(其实我一直不知道道具在程序中怎么说,prop?item?dao_ju?没有一个是没有歧义、让我满意的)。


NodeBagItem就没什么好说的了,根据model设置显示内容。页面是通过CSLoader::createNode("NodeBagItem.csb");加载的。但里面的NodeBagGrid是手动创建的。

NodeBagItem.h


#ifndef NodeBagItem_h__
#define NodeBagItem_h__

#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "cocostudio/CocoStudio.h"
#include "NodeBagGrid.h"
#include "NodeBagItemModel.h"
USING_NS_CC;
using namespace ui;
using namespace cocostudio;

//背包中的物品item(长方形),包含NodeBagGrid以及一些额外信息(名字,是否已装备等)
class NodeBagItem : public Node{
public:
    CREATE_FUNC(NodeBagItem);
    virtual bool init();

    void setupNodeBagItem(NodeBagItemModel model);

    void setSelected(bool isSelected);
    bool isSelected(){ return isSelected_; }

    ImageView* getBtn(){ return btn; }

    int64_t get_sno();
    int get_id();

private:
    ImageView* btn;
    NodeBagGrid* grid;
    Text* name_label;
    bool isSelected_;
    Sprite* isSelected_sp;

    Sprite* sp_debris;
    Sprite* already_use;
private:
    NodeBagItemModel view_model;
};

#endif // NodeBagItem_h__

NodeBagItem.cpp

include “NodeBagItem.h”

static const std::string text1 = “\xE7\xA9\xBA”;//空

bool NodeBagItem::init()
{
Node::init();

Node* node = CSLoader::createNode("NodeBagItem.csb");

this->addChild(node);

btn = (ImageView*)node->getChildByName("Image_2");

auto center_x = btn->getContentSize().width / 2;
auto center_y = btn->getContentSize().height / 2;

grid = NodeBagGrid::create();
btn->addChild(grid);

grid->setPosition(Vec2(center_x, center_y + 20));

auto position_y_name = 20;

name_label = (Text*)node->getChildByName("Text_1");

isSelected_sp = Sprite::create("bag/img_xuanzhong.png");
isSelected_sp->setPosition(center_x, center_y);

this->addChild(isSelected_sp);

setSelected(false);

sp_debris = (Sprite*)node->getChildByName("img_flag_suipian_1");
already_use = (Sprite*)node->getChildByName("img_yizhuangbei_2");

return true;

}

void NodeBagItem::setupNodeBagItem(NodeBagItemModel model)
{

view_model = model;

grid->setupNodeBagGrid(model.gridModel);

if (model.name != "")
{
    name_label->setString(model.name);
}
else{
    name_label->setString(text1);
}

sp_debris->setVisible(model.is_suipian);
already_use->setVisible(model.yi_zhuang_bei);

}

void NodeBagItem::setSelected(bool isSelected)
{
this->isSelected_ = isSelected;

isSelected_sp->setVisible(isSelected);

}

int64_t NodeBagItem::get_sno()
{
return view_model.sno;
}

int NodeBagItem::get_id()
{
int id = view_model.gridModel.icon_id;

//肮脏的代码,由于碎片的icon_id是合成后的装备的icon_id。
//所以添加了一个多余的字段,用于获取其原始id
if (view_model.is_suipian == true)
{
    id = view_model.debris_id;
}

return id;

}


最后给出图标的显示节点代码实现。

NodeBagGrid.h(里面包含了NodeBagGridModel和NodeBagGrid两个类)


#ifndef NodeBagGrid_h__
#define NodeBagGrid_h__

#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "cocostudio/CocoStudio.h"
USING_NS_CC;
using namespace ui;
using namespace cocostudio;



struct NodeBagGridModel
{
    enum ItemType
    {
        Equip,//装备,需要显示等级,
        Other,//包括道具、宝箱和钥匙,需要显示数量
    };

    ItemType type;//物品的类型
    int icon_id;//道具的id,id和图片资源一一对应
    int level;//如果是装备,显示等级
    int grid_bg_color;//装备的稀有度通过框框的背景色表示
    //bool has_jiahao;
    bool is_max_level;//装备有等级上限,满级会显示max标志
    int num;//其他类型的物品需要显示数量
};


//物品节点(正方形的格子),对应一件道具或装备
class NodeBagGrid : public Node{
public:
    CREATE_FUNC(NodeBagGrid);
    virtual bool init();
    void setupNodeBagGrid(NodeBagGridModel model);

protected:
    //Sprite* btn_jia;
    ImageView* btn;
    Sprite* equip_icon;
    Sprite* lv_flag;
    TextAtlas* lable_level;
    Sprite* lv_max_flag;
};

#endif // NodeBagGrid_h__

NodeBagGridModel是item中的一部分,也在其他的很多界面里用到了,其实这里的命名有问题,他其实更像是一个icon(道具icon,装备icon),而不是叫什么背包格子。

NodeBagGrid.cpp


#include "NodeBagGrid.h"


void NodeBagGrid::setupNodeBagGrid(NodeBagGridModel model)
{

    //如果是装备,装备的品质通过底板背景表示
    switch (model.grid_bg_color)
    {
    case 1:
        btn->loadTexture("common/img_zhuangbeikuang.png");
        break;
    case 2:
        btn->loadTexture("common/img_huang.png");
        break;
    case 3:
        btn->loadTexture("common/img_lv.png");
        break;
    case 4:
        btn->loadTexture("common/img_lang.png");
        break;
    case 5:
        btn->loadTexture("common/img_zi.png");
        break;
    default:
        break;
    }

    //默认不显示满级标识
    lv_max_flag->setVisible(false);


    if (model.icon_id != -1)//有id说明不是空格子
    {
        //设置图标icon
        auto equip_icon_path = StringUtils::format("icon/big_icon/%d.png", model.icon_id);
        equip_icon->setTexture(equip_icon_path);



        if (model.type == NodeBagGridModel::Equip)//装备
        {
            //设置等级或者满级标识
            lv_flag->setVisible(true);
            lable_level->setVisible(true);

            lable_level->setString(StringUtils::format("%d", model.level));
            lv_flag->setPosition(lable_level->getPositionX() - lable_level->getContentSize().width - 15, lable_level->getPositionY());

            lv_max_flag->setVisible(model.is_max_level);
            if (model.is_max_level)//注意:满级会设置等级文字为不可见,装备id为-1也会设置为不可见
            {
                lv_flag->setVisible(false);
                lable_level->setVisible(false);
            }
            else{
                lv_flag->setVisible(true);
                lable_level->setVisible(true);
            }
        }
        else{//道具需要显示数量
            lv_flag->setVisible(false);
            lable_level->setVisible(true);
            lable_level->setString(StringUtils::format("%d", model.num));

        }

    }
    else{//-1为空格子
        equip_icon->setTexture(nullptr);
        equip_icon->setTextureRect(Rect::ZERO);

        lv_flag->setVisible(false);
        lable_level->setVisible(false);
    }


}

bool NodeBagGrid::init()
{
    Node::init();

    btn = ImageView::create("common/img_zhuangbeikuang.png");
    this->addChild(btn);

    equip_icon = Sprite::create();
    btn->addChild(equip_icon);
    equip_icon->setPosition(Vec2(btn->getContentSize().width / 2, btn->getContentSize().height / 2));

    lv_flag = Sprite::create("bag/img_lv.png");
    btn->addChild(lv_flag);
    lv_flag->setPosition(88 + 15, 10 + 5);

    lable_level = TextAtlas::create("1234567890", "common/img_shuzi01.png", 225 / 15, 19, "+");
    btn->addChild(lable_level);
    lable_level->setPosition(Vec2(88 + 15, 10 + 5));
    lable_level->setAnchorPoint(Vec2(1, 0.5));

    lv_max_flag = Sprite::create("common/img_Max.png");
    lv_max_flag->setPosition(lable_level->getPosition());
    lv_max_flag->setAnchorPoint(Vec2(1, 0.5));
    btn->addChild(lv_max_flag);

    return true;
}


— 以上写于20150521:8点


阅读更多
换一批

没有更多推荐了,返回首页