CocoStudio sample讲解 SampleChangeEquip 换装系统

CocoStuido sample----DemoMap 源代码地址
https://github.com/chukong/CocoStudioSamples
大家可以预先下载这个源代码, 等下要用到里面的图片资源哦

换装系统是游戏里面可以提升游戏内容的部分, 就像电影里面女猪脚总是不停地上换一套一套漂亮的衣服. 我们在做游戏的时候, 就没有拍电影那么潇洒了, 要费好大力气才能实现换装. 现在CocoStudio提供了超酷的换装系统, 分分秒秒实现换装, 让我们游戏的主角也可以像电影女猪脚那样不停地换上漂亮, 酷帅的衣服装备了. 下面就让我们看看如何实现吧!

一 目标


在本教程里, 我们学习下如何使用CocoStudio来快速实现换装.
我们使用的cocos2d-x的版本是即将发布的2.2.3(即github上cocos2d-x的master分支较新版本), CocoStudio的版本是1.2.0.1. 不同的版本, 功能上会有差异, 大家学习时, 最好采用对应的版本.

二 创建项目并导入资源
今天我们要同时用到动画编辑和UI编辑器.
大家可以在下载到的源代码的SampleChangeEquip\SampleChangeEquip_Editor\EquipArmature\EquipArmature\Resources 目录下找到动画编辑器需要的资源.
在SampleChangeEquip\SampleChangeEquip_Editor\SampleChangeEquip_UI\SampleChangeEquip_UI\Resources 目录中找到UI编辑器需要的资源.

大家分别创建新的动画编辑器和UI编辑器项目, 并分别导入资源.

三 制作人物骨骼和界面UI
关于骨骼的制作, 由于篇幅关系, 我们这里无法详细展开. 大家可以参见下 “CocoStudio sample讲解 SampleCollision骨骼动画与简单碰撞”(http://www.cocoachina.com/bbs/read.php?tid=189665) .

我们在动画编辑器中制作完成后英雄如下图所示,

我们可以看到英雄由beltbone(皮带), glovebone(手套), helmetbone(头盔), necklacebone(项链), amourbone(衣服), weaponebone(武器)组成. 这些骨骼将是我们换装的基础.

我们再打开UI编辑器, 构建界面. 我们这里简单看下UI上英雄, 背包, 装备的构成.

如上图所示, 我们用一个层容器和一些按钮控件组成了英雄换装部分. 稍后会用程序将英雄添加到居中部位.
周围按钮可以接受对应的武器, 比如头盔按钮可以接受头盔, 如果放置了头盔, 也可以将头盔从这个区域拖走.

背包也是由层容器和众多按钮构成.

武器层由层容器和分组过的图片组成, 并整体处于不可见状态. 我们稍后会用程序的方式将其解析到正确的位置.
这里其实是一种取巧的做法, 相当于大家在真实游戏中的数据库的作用.

大家经过几次练习之后, 无论是人物还是界面都能很快构建出来. 如果大家在制作的过程中遇到了问题, 不妨到CocoaChina论坛的CocoStudio专区提出来, 大家多加交流.

四 导出资源
我们分别在两个编辑器里面用快捷键Ctrl+E打开导出对话框, 选择导出的路径, 按默认配置导出. 我们稍后会用到这些文件.

五 在cocos2d-x工程中添加导出后的资源
想必各位看官都已经熟练掌握了cocos2d-x工程的创建, 我这里就不再啰嗦了.
创建完工程之后, 需要将我们上面用CocoStudio导出的几个文件拷贝到cocos2d-x工程的Resources文件夹下.

六 代码实现
我们在HellWorld工程中, 添加一个Bag类, 我们着看下几个重要函数的实现. 其他的代码, 由于篇幅有限, 请大家参见源代码.

首先我们定于了一个枚举, 来确定装备的类型. 需要注意的是, 这些枚举的先后需要和我们在UI中equippanel的子节点对应.

enum{ EQUIP_TYPE_HELMET = 1,//头盔 EQUIP_TYPE_NECKLACE ,//项链 EQUIP_TYPE_ARMOUR ,//衣服 EQUIP_TYPE_WEAPON ,//武器 EQUIP_TYPE_SKILL ,//技能 EQUIP_TYPE_SHIELD ,//盾牌 EQUIP_TYPE_OTHER ,//其他 EQUIP_TYPE_BELT ,//皮带 EQUIP_TYPE_GLOVE //手套};

我们首先看下装备的解析.
//装备初始化void Bag ::initEquips(){ //获取装备面板 UIPanel* equipPanel = dynamic_cast <UIPanel >(uiLayer->getWidgetByName( “equippanel”)); CCArray equips = equipPanel->getChildren(); CCObject* object = NULL;
int bagGridCount = 1;
//第一个类型是头盔 int equipType = EQUIP_TYPE_HELMET ; //the first type of equip
CCARRAY_FOREACH (equips,object) { //该装备类型下所有节点 UIPanel* equipChildPanel = ( UIPanel*)object; CCArray* equips = equipChildPanel->getChildren();
int equipStartNum = 1;
CCARRAY_FOREACH_REVERSE (equips, object) { //获取到的装备 UIWidget* equip = dynamic_cast< UIWidget*>(object); //给装备添加了触摸回调 equip->addTouchEventListener( this, toucheventselector (Bag ::touchEvent)); //给装备设置ID, 其实是设置了装备的tag, 下面我们再看下其算法 initEquipID(equip,equipType,equipStartNum); //获取背包面板里面第bagGridCount格子 UIWidget* bagGrid = getBagGrid(bagGridCount++); changeParent(bagGrid,equip); //将装备加入背包格式.
equipStartNum++; } equipType++; }}

再来看下装备的tag算法.
//装备tag算法, 类型100 + 序号10void Bag ::initEquipID( UIWidget* pEquip, int type, int num){ // pEquip->setTag( type*100 + num *10);}

我们再来看下英雄面板的初始化.
//英雄面板初始化void Bag ::initPlayerEquipGrid(){ //获取英雄面板 UIPanel* playerPanel = dynamic_cast <UIPanel >(uiLayer->getWidgetByName( “playerpanel”));
CCArray
equipGrids = playerPanel->getChildren(); CCObject* object = NULL; //遍历英雄面板并给节点设置tag为类型100 int gridType = EQUIP_TYPE_HELMET ; CCARRAY_FOREACH (equipGrids,object) { UIWidget equipGrid = ( UIWidget*)object; equipGrid->setTag(gridType * 100); gridType++; }}

我们再来看下人物的初始化.
void Bag ::initArmature(){ //加载人物数据 CCArmatureDataManager ::sharedArmatureDataManager()->addArmatureFileInfo( “ArmatureAndEquip/EquipArmature.ExportJson”); armature = CCArmature::create( “EquipArmature” ); armature->getAnimation()->playByIndex(0); armature->setScale(0.28); armature->setPosition( ccp( CCDirector::sharedDirector()->getVisibleSize().width * 0.28, CCDirector::sharedDirector()->getVisibleSize().height * 0.55));
//将英雄添加到界面 UIWidget* armatureWidget = UIWidget::create(); uiLayer->addWidget(armatureWidget); armatureWidget->addNode(armature);
//initArmatureOriginEquips();}void Bag ::initArmatureOriginEquips(){ //隐藏编辑器中给英雄添加的默认装备 armature->getBone( “beltbone” )->changeDisplayWithIndex(-1,true ); armature->getBone( “necklacebone” )->changeDisplayWithIndex(-1,true ); armature->getBone( “weaponbone” )->changeDisplayWithIndex(-1,true ); armature->getBone( “helmetbone” )->changeDisplayWithIndex(-1,true );}

刚才我们给装备添加了触摸事件, 我们来看下装备如何响应触摸.
//触摸事件void Bag ::touchEvent( CCObject* pSender, TouchEventType type){ UIWidget* equip = ( UIWidget*) pSender; if ( type == TOUCH_EVENT_BEGAN )//触摸开始 { touchBeganEvent(equip); } if ( type == TOUCH_EVENT_MOVED )//移动 { touchMoveEvent(equip); } if ( type == TOUCH_EVENT_ENDED )//结束 { touchEndedEvent(equip); }}
//当触摸开始时void Bag ::touchBeganEvent( UIWidget* pEquip){ startGrid = ( UIWidget*) pEquip->getParent(); //记录移动开始的格子 pEquip->retain(); pEquip->removeFromParent(); //从当前父节点移除 pEquip->setPosition(CCPointZero); pEquip->setPosition( pEquip->getTouchStartPos()); //设置坐标 uiLayer->addWidget( pEquip); //放入uiLayer中, 可以理解为放入根节点中 pEquip->release();
pEquip->setZOrder(2); //调整zorder}
//移动过程中void Bag ::touchMoveEvent( UIWidget* pEquip){ CCPoint point = pEquip->getTouchMovePos();
pEquip->setPosition(point);}//移动结束时void Bag ::touchEndedEvent( UIWidget* pEquip){ //获取背包面板, 英雄面板 UIPanel* bagPanel = dynamic_cast <UIPanel >(uiLayer->getWidgetByName( “bagpanel”)); UIPanel playerPanel = dynamic_cast <UIPanel >(uiLayer->getWidgetByName( “playerpanel”));
if(hitTestPanel(bagPanel, pEquip)) { //放入了背包面板的格子上 if (targetGrid->getChildren()->count()>0) //格子上已经有装备 { UIWidget
originEquip = ( UIWidget*)targetGrid->getChildren()->objectAtIndex(0); changeParent(startGrid, originEquip); //将目标格子原装备放入移动起始格子 if(startGrid->getTag() >= 100) changeEquip(originEquip, startGrid); //英雄更新装备. } else if(startGrid->getTag()>=100) unequipEquip(); //英雄卸下装备 changeParent(targetGrid, pEquip); //将当前装备放入目标格子 return; } if(hitTestPanel(playerPanel, pEquip)) { //放入了英雄面板 if (targetGrid->getChildren()->count()>0) //格子上已经有装备 { UIWidget* originEquip = ( UIWidget*)targetGrid->getChildren()->objectAtIndex(0); changeParent(startGrid, originEquip); //将目标格子原装备放入移动起始格子 } changeParent(targetGrid, pEquip); //将当前装备放入目标格子 changeEquip( pEquip, targetGrid); //英雄更新装备 return; }
changeParent(startGrid, pEquip); //如果没有放入合适的格子, 则当前装备回到起始格子}

我们再来看下英雄是如何更新和卸载装备的.
//更新装备void Bag ::changeEquip( UIWidget* pWeapon, UIWidget* pGrid){ int equipType = pGrid->getTag()/100; if (equipType == EQUIP_TYPE_SKILL ) return ; if (equipType == EQUIP_TYPE_SHIELD ) return ; if (equipType == EQUIP_TYPE_OTHER ) return ;
//获取装备, 骨骼的节点名称 CCString* weaponName = CCString ::createWithFormat("%stex.png" ,pWeapon ->getName()); CCString* boneName = CCString::createWithFormat( “%sbone” ,pGrid ->getName());
//创建皮肤 CCSkin* weaponSkin = CCSkin ::createWithSpriteFrameName(weaponName->getCString()); armature->getBone(boneName->getCString())->addDisplay(weaponSkin,0); //添加皮肤到骨骼 armature->getBone(boneName->getCString())->changeDisplayWithIndex(0, true); //显示新添加的骨骼}

好了, 快来运行下吧.

1赞

分分秒秒实现换装新鲜出炉啦, 欢迎交(tu)流(cao)啊.

版主屌爆了

疼…:8:

一个字,,顶!!!

感谢在又深知年看到磁铁

吊炸天啊:11:

牛x 顶楼主

//当触摸开始时
void Bag ::touchBeganEvent( UIWidget* pEquip)
{
startGrid = ( UIWidget*) pEquip->getParent(); //记录移动开始的格子

         pEquip->retain();
         pEquip->removeFromParent(); //从当前父节点移除
         pEquip->setPosition(CCPointZero);
         pEquip->setPosition( pEquip->getTouchStartPos()); //设置坐标
        uiLayer->addWidget( pEquip); //放入uiLayer中, 可以理解为放入根节点中
         pEquip->release();

         pEquip->setZOrder(2); //调整zorder

}
能说说 这里为什么加 pEquip->retain(); 这一句吗

LZ,能把自然发一份给我吗。。。我去那个链接找不到。。。

在上面的链接上找不到资源,怎么找啊?