CocoStudio sample讲解 SampleChangeEquip 换装系统

文章转载自:http://www.cocoachina.com/bbs/read.php?tid=194122

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

目标:给角色穿上装备,然后再卸下装备

二 创建项目并导入资源  
今天我们要同时用到动画编辑和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类:

Bag.h

#ifndef __BAG_H__
#define __BAG_H__

#include "cocos2d.h"
#include "cocos-ext.h"
using namespace cocos2d::ui;


class Bag : public cocos2d::CCLayer
{
public:
	static Bag* create();
	virtual bool init();
	virtual ~Bag(void);

	void touchEvent(cocos2d::CCObject* pSender,
		TouchEventType type);
	void closeCallback(cocos2d::CCObject* pSender, TouchEventType type);
private:
	void touchBeganEvent(UIWidget* pEquip);
    void touchMoveEvent(UIWidget* pEquip);
    void touchEndedEvent(UIWidget* pEquip);
	void initUILayer();
	void initArmature();
	void initArmatureOriginEquips();
	void initEquips();
	void initEquipID(UIWidget* pEeuip,int type,int num);
	UIWidget* getBagGrid(int count);

	void initPlayerEquipGrid();

	bool hitTestPanel(UIPanel* pPanel,
		UIWidget* pEquip);
	bool equipAndGridInSameType(UIWidget* pEquip,
		UIWidget* pGrid);
	void swapEquipGrid(UIWidget* pExistEquip,
		UIWidget* newEquip);

	void changeEquip(UIWidget* pWeapon,
		UIWidget* pGrid);
	void unequipEquip();

	void changeParent(UIWidget* pGrid,
		UIWidget* pEquip);

	UILayer* uiLayer;
	UIWidget* startGrid;//开始点击的格子
    UIWidget* targetGrid;//目标格子
	cocos2d::extension::CCArmature* armature;
};


#endif

Bag.cpp(主要内容)

#include "Bag.h"



USING_NS_CC;
USING_NS_CC_EXT;

//首先我们定于了一个枚举, 来确定装备的类型. 需要注意的是, 这些枚举的先后需要和我们在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
};

//重写Bag
Bag* Bag::create()
{
	Bag *pRet = new Bag();
    if (pRet && pRet->init())
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
}

bool Bag::init()
{
	if(!CCLayer::init()) return false;

	initUILayer();
	initArmature();
	initEquips();
	initPlayerEquipGrid();

	return true;
}
//加载装备面板
void Bag::initUILayer()
{
    UIWidget* widget = dynamic_cast<Layout*>(cocostudio::timeline::NodeReader::getInstance()->createNode("SampleChangeEquip_UI_1/SampleChangeEquip_UI_1.ExportJson"));
	uiLayer = UILayer::create();
	uiLayer->addWidget(widget);
	this->addChild(uiLayer);

	UIWidget* closeButton = dynamic_cast<UIWidget*>(uiLayer->getWidgetByName("closebutton"));
	closeButton->addTouchEventListener(this,toucheventselector(Bag::closeCallback));
	
}
//人物的初始化.  
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::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++;
	}
}
//取得背包所有格子
UIWidget* Bag::getBagGrid(int count)
{
	CCString* ccstring = CCString::createWithFormat("baggrid%d",count);
	UIWidget* bagGrid = dynamic_cast
				<UIWidget*>(uiLayer->getWidgetByName(ccstring->getCString()));
	return bagGrid;
}
//装备tag算法, 类型*100 + 序号*10
void 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;

	int gridType = EQUIP_TYPE_HELMET;
	 //遍历英雄面板并给节点设置tag为类型*100
	CCARRAY_FOREACH(equipGrids,object)
	{
		UIWidget* equipGrid = (UIWidget*)object;
		equipGrid->setTag(gridType * 100);
		gridType++;
	}
}
//给装备添加了触摸事件
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);
}
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);//将目标格子原装备放入移动起始格子,如果startGrid上也有物品则互换位置
            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);//将目标格子原装备放入移动起始格子,如果startGrid上也有物品则互换位置
        }
        changeParent(targetGrid, pEquip);//将当前装备放入目标格子
        changeEquip(pEquip, targetGrid);//英雄更新装备.
        return;
    }

	changeParent(startGrid,pEquip);//如果没有放入合适的格子, 则当前装备回到起始格子

}
bool Bag::hitTestPanel(UIPanel* pPanel,UIWidget* pEquip)
{
	CCObject* object = NULL;
	CCARRAY_FOREACH(pPanel->getChildren(),object)
	{
		UIWidget* grid = (UIWidget*)object;

		if(grid->hitTest(pEquip->getPosition()))
		{
			if(equipAndGridInSameType(pEquip,grid))
            {
                targetGrid = grid;
                return true;
            }
		}
	}
	return false;
}
bool Bag::equipAndGridInSameType(UIWidget* pEquip,UIWidget* pGrid)
{
	int gridType = pGrid->getTag()*0.01;
	//pGrid is bagGrid and have no children
	if((pGrid->getTag() < 100)&&(!pGrid->getChildren()->count())) return true;
    //pgrid and startGrid is baggrid
    else if((pGrid->getTag() <100)&&(startGrid->getTag() <100)) return true;
    //pGrid and equip in same type
	else if(gridType == (int(pEquip->getTag()*0.01))) return true;
    else if(pGrid->getChildren()->count())
    {
        int originEquipType = ((UIWidget*) pGrid->getChildren()->objectAtIndex(0))->getTag()*0.01;
        int newEquip = pEquip->getTag()*0.01;
        if(originEquipType == newEquip) return true;
    }
	return false;
}
//互换位置
void Bag::swapEquipGrid(UIWidget* pExistEquip,UIWidget* newEquip)
{
	UIWidget* parentGrid = (UIWidget*)pExistEquip->getParent();	
	changeParent(startGrid,pExistEquip);
	changeParent(parentGrid,newEquip);
}
//更新装备
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);//显示新添加的骨骼
}
//卸载装备
void Bag::unequipEquip()
{
	int equipType = startGrid->getTag() * 0.01;
	//获取骨骼名称
	CCString* boneName = CCString::createWithFormat("%sbone",startGrid->getName());
	switch (equipType)
	{
	case EQUIP_TYPE_WEAPON:
	case EQUIP_TYPE_BELT:
	case EQUIP_TYPE_HELMET:
	case EQUIP_TYPE_NECKLACE:
		armature->getBone(boneName->getCString())->changeDisplayWithIndex(-1,true);//隐藏装备
		break;
	case	EQUIP_TYPE_SKILL:
	case	EQUIP_TYPE_SHIELD:
	case	EQUIP_TYPE_OTHER:
		break;
	default:
		{
		//默认的皮肤,也就是没有装备时的角色
			CCString* equipName = CCString::createWithFormat("%s0tex.png",startGrid->getName());
			CCSkin* equipDefaultSkin = CCSkin::createWithSpriteFrameName(equipName->getCString());
			armature->getBone(boneName->getCString())->addDisplay(equipDefaultSkin,0);
			break;
		}
	}
}

void Bag::changeParent(UIWidget* pGrid,UIWidget* pEquip)
{
	pEquip->retain();
	pEquip->removeFromParent();
	pEquip->setPosition(CCPointZero);
	pGrid->addChild(pEquip);
	pEquip->release();
}




void Bag::closeCallback(CCObject* pSender,TouchEventType type)
{
	cocos2d::extension::CCArmatureDataManager::purge();
	cocos2d::extension::ActionManager::shareManager()->purge();
	cocos2d::extension::SceneReader::sharedSceneReader()->purge();
	cocos2d::extension::GUIReader::shareReader()->purge();
#if (CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID)
    CCDirector::sharedDirector()->end();
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    exit(0);
#endif
}

Bag::~Bag(void)
{
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值