繼續上一篇文章繼續聊吧,這章內容會比較多,也會附上代碼,很多朋友加了群,大家在群里面探討了很多東西,這讓大家都覺得受益匪淺,這便是極好的,廢話不多了,精靈是游戲的重要組成部分,那ARPG里面的精靈必然是要做得很細的,因為精靈要能動,能跑,能打,甚至要能做各種交互動作等等。
大家可以看一下下面的題,是精靈制作的流程思路:
上圖的人物素材來自於網絡流傳的夢幻西游,地圖還有其他素材是以前公司同事制作的,如果游戲正式上線,會換一套完整的自制的素材。圖中大家可以看到一個人物有很多部件組合而成,高端一點的游戲甚至部件多達幾十上百種,甚至做出骨骼動畫。不過以我現在的做法是按照幀動畫方式實現各個人物的動作,人物的body部分細節處理會很多,還有大家看到圖中的字體(字體渲染描邊,我做的是最簡單的了,兩個LABEL合成在一起,效果能達到就行),由於人物要有方向,而且我們一次性就做8方向的人物動畫,那就需要8個方向的連幀圖片:
首先,要組裝起來我們首先要建一個實體角色類,這個類里面不放別的,就統一放角色的部件屬性:
MainRoledata.h類
#ifndef _MAIN_ROLE_DATA_ #define _MAIN_ROLE_DATA_ #include "cocos2d.h" #include "../Commen_ActionToDo.h" #include "../Commen_Direction.h" USING_NS_CC; class MainRoledata { public : //人物的TAG int tags; //人物姓名 CCString* spiritname; //人物初始坐標 CCPoint nowpoint; //人物默認像素圖 CCString* spiritUrl; //人物站立像素圖路徑 CCString* spiritUrl_zhan; //人物跑動像素圖路徑 CCString* spiritUrl_pao; //人物攻擊像素圖路徑 CCString* spiritUrl_attack; //人物施法像素圖路徑/增加人物BUF CCString* spiritUrl_magic; //人物站立最大幀 int maxcut_zhan; //人物跑動最大幀 int maxcut_pao; //人物戰斗最大幀 int maxcut_attack; //人物施法最大幀 int maxcut_magic; //人物當前動作 Commen_ActionToDo acttodo; //人物當前朝向 Commen_Direction dir; //動畫時間 float actiontime; }; #endif
枚舉幾個方向,和動作的類:
enum Commen_Direction { up=0, down=1, lefts=2, rigth=3, rigth_up=4, rigth_down=5, left_down=6, left_up=7 };
enum Commen_ActionToDo { run=1, stand=2, attack=3, death=4, funny=5, magicup=6 };
OK,然后配置精靈數據,建了一個GetNPCData.cpp,GetNPCData.h,主要就是拿來初始化數據,大致的思路是要將上面的Model填充數據,相信大家
能夠用很多種方式去實現,填充數據(讀取XML配置文件,直接寫在代碼中配置);
接下來我們正式組裝完整的八面玲瓏的精靈,建立SpiritsPlayer.cpp,SpiritsPlayer.h;
文件內容如下:
#ifndef _SPIRIT_PLAYER_ #define _SPIRIT_PLAYER_ #include "cocos2d.h" #include "../Commen_ActionToDo.h" #include "../Commen_Direction.h" #include "../GameData/MainRoledata.h" #include "../Commen/PublicShowUI.h" #include "../Effects/EffectsCommen.h" USING_NS_CC; class SpiritsPlayer : cocos2d::CCSprite { public: CCSprite* npc; CCSprite* yinzi; CCSprite* sp_liaotianbd; PublicShowUI* p_ui_name; CCArray *stringArray; CCAnimate* playdonghua; CCAnimate* playdonghua2; Commen_Direction move_dir; bool endflag; bool endflag2; bool thiszhujiao_flag; void Spirits_talkabout_hid(); SpiritsPlayer(MainRoledata roledata,int zOrder,bool zhujiaoflag); ~SpiritsPlayer(void); CCAnimation* getNowAnt(MainRoledata roledata); CCAnimate* updateNowAnt(MainRoledata roledata); void updateNpcPoint(CCPoint newpoint); void moveTomap_dir(CCPoint newpoint); void moveTomap_move(int uestime,CCPoint newpoint,bool npcflag); //人物移動完成的回調 void moveoverCallBack(void); //普通NPC移動完成的回調 void moveoverCallBackforNpc(void); //根據點擊坐標獲得人物的朝向 Commen_Direction getNowPointDir(CCPoint newpoint); // 觸摸點是否在精靈上 bool isTouchInside(CCPoint thisPos); //移動方式 void movemethod(int uestime,CCPoint newpoint); private: //角色基本數據 MainRoledata thisroledata; CCFiniteTimeAction *actall; CCActionInterval* act_moveto_zi; CCActionInterval* act_moveto_npc; CCActionInterval* act_moveto_yinzi; CCActionInterval* act_moveto_eff; CCActionInterval* act_moveto_eff_zhujiao; CCFiniteTimeAction *actbackfun; int flag ; private: CCRect rect(); }; #endif//_SPIRIT_PLAYER_
#include "../ImagePaths.h" #include "../GameData/GetNPCData.h" #include "../Commen/FontChina.h" SpiritsPlayer::SpiritsPlayer(MainRoledata roledata,int zOrder,bool zhujiaoflag) { //先初始化部分數據 thisroledata = roledata; act_moveto_zi =NULL; act_moveto_npc =NULL; act_moveto_yinzi =NULL; actall=NULL; thiszhujiao_flag = zhujiaoflag; p_ui_name = new PublicShowUI(); flag = 0; npc = SpiritsPlayer::create(roledata.spiritUrl->getCString()); if(npc==NULL) { CCLog("圖層路徑有誤,請檢查路徑"); return; } //設置NPC初始位置坐標(該坐標取決於當前畫層) npc->setPosition(roledata.nowpoint); //NPC動畫設置 playdonghua = SpiritsPlayer::updateNowAnt(roledata); npc->runAction(playdonghua); /**開始添加角色各部件**/ //添加角色名稱 CCLabelTTF* label = CCLabelTTF::create(roledata.spiritname->getCString(), "微軟雅黑",12); label->setColor(ccWHITE); label->setDirty(true); label->setPosition(ccp(npc->getContentSize().width/2,npc->getContentSize().height+6)); CCLabelTTF* labelback = CCLabelTTF::create(roledata.spiritname->getCString(), "微軟雅黑",12); labelback->setColor(ccBLACK); labelback->setDirty(true); labelback->setPosition(ccp(npc->getContentSize().width/2+1,npc->getContentSize().height+6-1)); //添加NPC人物腳下陰影 yinzi = CCSprite::create(p_yinzi); if(yinzi==NULL) { CCLog("圖層路徑有誤,請檢查路徑"); return; } if(zhujiaoflag==true) { yinzi->setPosition(ccp(npc->getContentSize().width/2,12)); } else { yinzi->setPosition(ccp(npc->getContentSize().width/2,1)); } npc->addChild(yinzi,-1,110); npc->addChild(label,2,111); npc->addChild(labelback,1,112); } cocos2d::CCRect SpiritsPlayer::rect() { //獲取精靈區域大小 return CCRectMake(npc->getPositionX()- npc->getContentSize().width * npc->getAnchorPoint().x,npc->getPositionY()-npc->getContentSize().height* npc->getAnchorPoint().y,npc->getContentSize().width, npc->getContentSize().height); } bool SpiritsPlayer::isTouchInside(CCPoint thisPos) { CCPoint localPos = thisPos; CCRect rc = rect(); bool isTouched = rc.containsPoint(localPos); if (isTouched == true) { CCLog(FontChina::G2U("觸發點擊")); }else { CCLog(FontChina::G2U("未點擊")); } return isTouched; } void SpiritsPlayer::Spirits_talkabout_hid() { CCLog(FontChina::G2U("************調用了*****************")); } CCAnimate* SpiritsPlayer::updateNowAnt(MainRoledata roledata) { //NPC動畫 CCAnimation* donghua = SpiritsPlayer::getNowAnt(roledata); if(roledata.actiontime>0) { donghua->setDelayPerUnit(roledata.actiontime/roledata.maxcut_zhan); } else { donghua->setDelayPerUnit(2.0f/15.0f);//執行默認時間 } donghua->setRestoreOriginalFrame(true); donghua->setLoops(-1); CCAnimate* playdonghua = CCAnimate::create(donghua); return playdonghua; } /************* * 主角位移移動 *************/ void SpiritsPlayer::moveTomap_move(int uestime, CCPoint newpoint,bool npcflag) { if(npcflag==true) { actbackfun = CCCallFunc::create(this, callfunc_selector(SpiritsPlayer::moveoverCallBackforNpc)); } else { actbackfun = CCCallFunc::create(this, callfunc_selector(SpiritsPlayer::moveoverCallBack)); } movemethod(uestime,newpoint); } void SpiritsPlayer::movemethod(int uestime,CCPoint newpoint) { npc->stopAction(actall); act_moveto_npc = CCMoveTo::create(uestime,ccp(newpoint.x,newpoint.y+20)); actall = CCSequence::create(act_moveto_npc,actbackfun,NULL); npc->runAction(actall); } /************* * 改變移動方向 *************/ void SpiritsPlayer::moveTomap_dir(CCPoint newpoint) { GetNPCData npcdata = GetNPCData(); npcdata.GetNPCchapter1(); move_dir=SpiritsPlayer::getNowPointDir(newpoint); npcdata.role_player.dir=move_dir; npcdata.role_player.acttodo = run; npcdata.role_player.actiontime=0.5; npc->stopAction(playdonghua); playdonghua = SpiritsPlayer::updateNowAnt(npcdata.role_player); npc->runAction(playdonghua); } /************* * 根據點擊坐標獲得人物的朝向 *************/ Commen_Direction SpiritsPlayer::getNowPointDir(CCPoint newpoint) { Commen_Direction thisdir = rigth_down; //默認為右下 //計算移動數據 float center_x,center_y,npc_x,npc_y; int move_x,move_y; //更新NPC方向,狀態 CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); CCSize size = CCDirector::sharedDirector()->getWinSize(); center_x = size.width/2; center_y = size.height/2; npc_x = npc->getPositionX(); npc_y = npc->getPositionY(); move_x = (int)(npc_x -newpoint.x ); move_y = (int)(npc_y -newpoint.y - 20); if(move_x>=10&&move_y<=-10) { //左上 thisdir = left_up; } else if(move_x>=10&&move_y>=10) { //左下 thisdir = left_down; } else if(move_x<=-10&&move_y<=-10) { //右上 thisdir = rigth_up; } else if(move_x<=-10&&move_y>=10) { //右下 thisdir =rigth_down; } else if(move_x>-10&&move_x<10&&move_y>0) { //下 thisdir =down; } else if(move_x>-10&&move_x<10&&move_y<0) { //上 thisdir =up; } else if(move_x>0&&move_y>-10&&move_y<10) { //左 thisdir = lefts; } else if(move_x<0&&move_y>-10&&move_y<10) { //右 thisdir =rigth; } return thisdir; } /************* * 移動完成后的回調 *************/ void SpiritsPlayer::moveoverCallBack() { //移動完成之后恢復站立狀態 GetNPCData npcdata = GetNPCData(); npcdata.GetNPCchapter1(); npcdata.role_player.dir=move_dir; npcdata.role_player.acttodo = stand; npcdata.role_player.actiontime=1.1f; npc->stopAction(playdonghua); playdonghua = SpiritsPlayer::updateNowAnt(npcdata.role_player); npc->runAction(playdonghua); } /************* * 普通NPC移動完成后的回調 *************/ void SpiritsPlayer::moveoverCallBackforNpc() { } /************* * 點擊瞬移至此 *************/ void SpiritsPlayer::updateNpcPoint(CCPoint newpoint) { p_ui_name->updataGameText(ccp(newpoint.x,newpoint.y+npc->getContentSize().height/2+10)); npc->setPosition(newpoint); yinzi->setPosition(ccp(newpoint.x,newpoint.y-npc->getContentSize().height/2+5)); } /********************* * 八方向人物動作合成器 *********************/ CCAnimation* SpiritsPlayer::getNowAnt(MainRoledata roledata) { CCAnimation* thisdonghua = CCAnimation::create(); switch (roledata.dir) { case up: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s06%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s06%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: break; case death: break; case funny: break; default: break; } break; case down: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s04%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s04%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: break; case death: break; case funny: break; default: break; } break; case lefts: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s05%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s05%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: break; case death: break; case funny: break; default: break; } break; case rigth: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s07%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s07%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: break; case death: break; case funny: break; default: break; } break; case rigth_up: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s03%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s03%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: for(int i = 0; i<=roledata.maxcut_attack ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s03%03d.png",roledata.spiritUrl_attack->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case magicup: for(int i = 0; i<=roledata.maxcut_magic ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s03%03d.png",roledata.spiritUrl_magic->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case death: break; case funny: break; default: break; } break; case rigth_down: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[1000] = {0}; sprintf(donghuaurl,"%s00%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s00%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: for(int i = 0; i<=roledata.maxcut_attack ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s00%03d.png",roledata.spiritUrl_attack->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case magicup: for(int i = 0; i<=roledata.maxcut_magic ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s00%03d.png",roledata.spiritUrl_magic->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case death: break; case funny: break; default: break; } break; case left_down: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s01%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s01%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: for(int i = 0; i<=roledata.maxcut_attack ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s01%03d.png",roledata.spiritUrl_attack->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case magicup: for(int i = 0; i<=roledata.maxcut_magic ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s01%03d.png",roledata.spiritUrl_magic->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case death: break; case funny: break; default: break; } break; case left_up: switch (roledata.acttodo) { case run: for(int i = 0; i<=roledata.maxcut_pao ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s02%03d.png",roledata.spiritUrl_pao->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case stand: for(int i = 0; i<=roledata.maxcut_zhan ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s02%03d.png",roledata.spiritUrl_zhan->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case attack: for(int i = 0; i<=roledata.maxcut_attack ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s02%03d.png",roledata.spiritUrl_attack->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case magicup: for(int i = 0; i<=roledata.maxcut_magic ; i++) { char donghuaurl[100] = {0}; sprintf(donghuaurl,"%s02%03d.png",roledata.spiritUrl_magic->getCString(),i); thisdonghua->addSpriteFrameWithFileName(donghuaurl); } break; case death: break; case funny: break; default: break; } break; default: break; } return thisdonghua; } SpiritsPlayer::~SpiritsPlayer(void) { }
sprintf(donghuaurl,"%s06%03d.png",roledata.spiritUrl_pao->getCString(),i);
總體思路就是,我們通過了幀連接的拼接來構成動畫,通過我們之前寫好的model數據來定義我們任務的朝向等問題
比如00000代表右,01000就代表右上,這個也得根據自己素材的模型來寫不同的處理邏輯,萬變不離其中;
如果我們的圖片是在一張大圖的集合中,我們可以同過CCRect來處理獲取幀圖片方式!
CCAnimation* getNowAnt(MainRoledata roledata);
CCAnimate* updateNowAnt(MainRoledata roledata);
通過這兩個方法集合,我們就能獲取到八面玲瓏的朝向,甚至我們連,后續的動畫機制也加入了,比如,跑動,打斗,做各種動作等!
Commen_Direction SpiritsPlayer::getNowPointDir(CCPoint newpoint); //獲取朝向
void SpiritsPlayer::moveTomap_dir(CCPoint newpoint) //改變朝向方法
以上兩個方法,我們是專門處理,之后精靈移動,移動完之后還得恢復到站立狀態,移動過程中改變人物朝向問題!所以要考慮的很清楚
很清晰,思路清楚了,才能方便以后的拓展。
OK了,有了以上的精靈類,將其實例化到一個簡單的圖片地圖上,他就會動了;
添加的時候我們直接addchild方法!!
SpiritsPlayer* role_main = new SpiritsPlayer(basedatas->role_player,1,false);
nowmap->addChild(role_main->npc, 999);
nowmap 暫且可以用一張圖片CCSprite代替!!!
下一篇博客我會非常詳細的將地圖制作方法寫出來給大家一起分享,由於其實我之前都是C#,JAVA做的很多,很多地方和細節還需要重構
還需要大家多指點一下,這也是我學習的好機會;
游戲demo及素材下載地址(demo里面包含了所有的素材資料);
http://pan.baidu.com/share/link?shareid=4012433582&uk=4097703620&third=15
ps:廣告我建了一個QQ群:和大家一起分享cocos2dx開發經驗【41131516】