cocos2dx3.2开发 RPG《Flighting》(五)只能行走的战斗场景
一、前言
前面幾節好像與我們一開始說的游戲不太相關,現在我們正式進入戰斗場景的開發。
不過凡事不要心急,要循序漸進,我們先搭建一個能夠讓角色在上面行走的戰斗場景吧。
二、正文
首先精簡一下Role類,讓他能夠實現移動功能就好了。
顧名思義,role就是角色,在戰斗場景出現的一個一個人都是一個role,廢話不多說,直接貼上經過精簡過得Role頭文件
class Role : public Node{ public:Role(); /*create 之前,請先確認已經將文件添加到ArmatureManager*/static Role* create(const std::string& name,FlightLayer* layer);virtual bool init(const std::string& name,FlightLayer* layer);void setControlable(bool b);virtual Rect getBoundingBox();virtual inline void setArmOffsetX(int x){m_arm_offsetX = x;}virtual inline void setArmOffsetY(int y){m_arm_offsetY = y;}protected: /*update相關的*/virtual void update(float delta);virtual void update_pos();/*從RoleProtocol中繼承下來的方法*/ public:virtual bool onTouchBegan(Touch* touch,Event* event);virtual void onTouchMoved(Touch* touch,Event* event);virtual void onTouchEnded(Touch* touch,Event* event);protected:/*與顯示相關的*/bool m_controlable;Armature* m_arm;int m_arm_offsetX;int m_arm_offsetY;bool m_armFaceTo; //朝向,默認為true,向左public:/*外部調用接口*/virtual inline void setDesPoint(const Point& p){m_desPoint = p;}//IDvirtual inline int getId(){return m_id;}protected:/*與戰斗相關的數據*/int m_id; //idPoint m_desPoint;//目標位置int m_speed; //移動速度int m_initSpeed;}; #endif經過精簡后,Role類就剩下這些函數和變量了,現在的這個Role類只能簡單的移動。不要心急,那我們就先實現role的移動吧。
好的,先撇開我們精簡過得Role類,先看我們的GameScene
GameScene.h
class GameScene : public Scene{ public:CREATE_FUNC(GameScene);void setHeroTeam(const HeroMessage& h1,const HeroMessage& h2,const HeroMessage& h3);void setMonsterDeq(deque<MonsterMessage> deq); private:virtual bool init();FlightLayer* layer; };GameScene的兩個接口setHeroTeam和setMonsterDeq在上一節的選人界面已經使用過了。 void GameScene::setHeroTeam(const HeroMessage& h1,const HeroMessage& h2,const HeroMessage& h3){layer->initTeam(h1,h2,h3); }void GameScene::setMonsterDeq(deque<MonsterMessage> deq){layer->initMonsterDeq(deq); }
GameScene也只是簡單地將信息傳遞給layer層罷了。
下面主要研究FlightLayer
class Role;typedef Role** Role_Ptr; class FlightLayer : public Layer{ friend class Role; /*外部提供接口*/ public:virtual bool init();CREATE_FUNC(FlightLayer);void addRole(Role* r);std::list<Role_Ptr> getRolesArray(){return m_rolesArray;}void initTeam(const HeroMessage& h1,const HeroMessage& h2,const HeroMessage& h3);void initMonsterDeq(deque<MonsterMessage> deq);/*內部使用函數*/ private:virtual void update(float delta);void initListener();bool comparePosY(Role_Ptr a,Role_Ptr b);/*更新layer中role的疊放次序*/void refreshLocalZOrder();/*保持m_cur_control的正確性*/void updateMyControl();virtual bool onTouchBegan(Touch* touch,Event* event);virtual void onTouchMoved(Touch* touch,Event* event);virtual void onTouchEnded(Touch* touch,Event* event);/*類成員變量*/ private:std::list<Role_Ptr> m_rolesArray;Role* m_cur_control;Role_Ptr m_cur_controlPtr; }; #endif當然啦,這個FlightLayer也是被我精簡過得。
這里需要注意的是,我們將Role**這個二級指針typedef為Role_Ptr,并不是一個新的類啊,至于為啥要用二級指針(其實改為用智能指針也行),等下我會好好說的。
好的開始好好講FlightLayer的實現
bool FlightLayer::init(){this->scheduleUpdate();initListener();m_cur_controlPtr = nullptr;m_cur_control = nullptr;Size visibleSize = Director::getInstance()->getVisibleSize();Sprite* BG = Sprite::create("flightBG.jpg");BG->setAnchorPoint(Point(0.5f,0.5f));BG->setPosition(visibleSize.width/2,visibleSize.height/2);this->addChild(BG);return true; }init函數除了啟動update和調用initListener函數之外沒有其他特別的。 void FlightLayer::initListener(){EventListenerTouchOneByOne* touchListener = EventListenerTouchOneByOne::create();touchListener->onTouchBegan = CC_CALLBACK_2(FlightLayer::onTouchBegan,this);touchListener->onTouchMoved = CC_CALLBACK_2(FlightLayer::onTouchMoved,this);touchListener->onTouchEnded = CC_CALLBACK_2(FlightLayer::onTouchEnded,this);Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener,this); }initListener函數也只是為layer添加觸摸事件監聽器而已。 bool FlightLayer::onTouchBegan(Touch* touch,Event* event){if(m_rolesArray.size() < 1){return false;}for(auto it = m_rolesArray.begin();it!=m_rolesArray.end();++it){if((**it)->onTouchBegan(touch,event)){m_cur_controlPtr = *it;m_cur_control = **it;return true;}}m_cur_controlPtr = nullptr;return false; }大致的意思是:當有點擊事件發生時,onTouchBegan方法被觸發,遍歷m_rolesArray,看哪個角色被選中(能夠接受觸摸事件),如果有角色被選中,就把觸摸事件交給那個角色處理。m_roleArray是一個Role_Ptr(Role**)的list,用于保存現在存活的role
那么為什么要用二級指針呢?
考慮以下問題:假設現在有兩個角色,如果用一級指針的話,定義如下:
Role* A = Role::create();
Role* B = Role::create();
好了,現在A要攻擊,攻擊目標設置為B(所謂攻擊目標就是一直向目標走去,要時刻檢測目標的當前位置)
如何設置攻擊目標?一級指針的話不就是在Role類里面加一個Role*類型的成員變量(Role* attackTarget)代表攻擊目標就可以了嗎。?
好了,前提條件說完了,現在出現這樣一種情況,A的攻擊目標設置為B(A->attackTarget = B),這時候A會時刻檢測自己的attackTarget的位置,并且不斷地走過去。
但是,假如這個時刻B被C打死了。。那么這個時候B從場景中清除掉(調用removeFromParent)。那么A怎么知道B已經死了,被清除掉了呢?
聰明的同學可能會想:你判斷一下A->attackTarget 是不是等于null不就可以了嗎?但是很抱歉,cocos2dx里面,調用玩removeFromParent之后,原來那塊中間在這一幀結束之前,并不不會置為null。也就是說B 進行remove后,A->attackTarget不等于null,那么有人又問了,你這里的m_rolesArray數組檢測每一個role*(假設m_roleArray里面放入的是Role*而不是Role**),如果死了(Hp==0)就把它賦值為空,不可以嗎?這個就是很經典的C語言關于值傳遞和地址傳遞的問題。你把m_rolesArray里面的臨時變量置為null有什么用?m_rolesArray和A里面的attackTarget指針指向都是同一塊空間(B的實際空間),但是兩個是完全不同的變量,并不能說你把m_rolesArray的指針置為null,A的attackTarget也會變成null,二者是互不影響的。
好的,講了那么多,大家可能會不懂。因為我這里也繞了很久。不過最后還是覺得很有必要理清楚,不懂的同學可以看一下《Effective?C++》里面介紹的RAII思想。
最后,補充一點是,最好用智能指針代替二級指針,這里我懶得修改了,不好意思哈。
貌似繞開了話題了,我們回到FlightLayer吧。
剛剛講到有一個m_rolesArray的List負責保存在場景中還存活的角色。那么這個這些角色是什么時候放進去的呢?
void FlightLayer::initTeam(const HeroMessage& h1,const HeroMessage& h2,const HeroMessage& h3){Hero* hero1 = Hero::create(h1.r_name,this);hero1->setPosition(-100,380);hero1->setDesPoint(Point(200,380));hero1->initWithMessage(h1);this->addRole(hero1);Hero* hero2 = Hero::create(h2.r_name,this);hero2->setPosition(-100,260);hero2->setDesPoint(Point(400,260));hero2->initWithMessage(h2);this->addRole(hero2);Hero* hero3 = Hero::create(h3.r_name,this);hero3->setPosition(-100,140);hero3->setDesPoint(Point(200,140));hero3->initWithMessage(h3);this->addRole(hero3);}void FlightLayer::initMonsterDeq(deque<MonsterMessage> deq){this->m_monsterDeq = deq; }void FlightLayer::addRole(Role* r){//r->setDesPoint(r->getPosition());this->addChild(r);Role_Ptr temp = (Role_Ptr)malloc(sizeof(Role*));*temp = r;m_rolesArray.push_back(temp); }對的,就是initTeam和initMonsterDeq這兩個接口。這里的addRole是一個基本操作,負責在堆里面開一個區域存放Role的實體。
再回過頭來看我們的三個觸摸事件觸發的回調函數。
bool FlightLayer::onTouchBegan(Touch* touch,Event* event){if(m_rolesArray.size() < 1){return false;}for(auto it = m_rolesArray.begin();it!=m_rolesArray.end();++it){if((**it)->onTouchBegan(touch,event)){m_cur_controlPtr = *it;m_cur_control = **it;return true;}}m_cur_controlPtr = nullptr;return false; }void FlightLayer::onTouchMoved(Touch* touch,Event* event){if(m_cur_control){m_cur_control->onTouchMoved(touch,event);}else{return;} }void FlightLayer::onTouchEnded(Touch* touch,Event* event){if(m_cur_control){m_cur_control->onTouchEnded(touch,event);Point tp = Director::getInstance()->convertToGL(touch->getLocationInView());if(!m_cur_control->getBoundingBox().containsPoint(tp)){m_cur_control->setDesPoint(m_cur_control->getEndPoint());}} }這三個簡化后的函數應該不難看懂。大概流程是這樣的
1.onTouchBegan接受到觸摸事件之后,檢測m_rolesArray,看是否有Role被點中了,如果有m_cur_controlPtr 和 m_cur_control就設置好,然后把觸摸事件交給m_cur_control(Role)處理。
2.onTouchMoved,沒什么,也是依賴于m_cur_control的處理。
3.onTouchEnded,就是用來設置m_cur_control的終點的。
是不是兜兜轉轉又回到去了Role類,關于精簡過的Role類我們還是標上注釋吧。
class Role : public Node{ public:Role(); /*create 之前,請先確認已經將文件添加到ArmatureManager*/static Role* create(const std::string& name,FlightLayer* layer);virtual bool init(const std::string& name,FlightLayer* layer);void setControlable(bool b);<span style="white-space:pre"> </span>//設置是不是可以控制的virtual Rect getBoundingBox();<span style="white-space:pre"> </span>//獲取范圍virtual inline void setArmOffsetX(int x){m_arm_offsetX = x;}<span style="white-space:pre"> </span>//設置x,y的偏移量virtual inline void setArmOffsetY(int y){m_arm_offsetY = y;}protected: /*update相關的*/virtual void update(float delta);<span style="white-space:pre"> </span>//update函數virtual void update_pos();public:virtual bool onTouchBegan(Touch* touch,Event* event);virtual void onTouchMoved(Touch* touch,Event* event);virtual void onTouchEnded(Touch* touch,Event* event);protected:/*與顯示相關的*/bool m_controlable;<span style="white-space:pre"> </span>//是否可觸摸Armature* m_arm;<span style="white-space:pre"> </span>//骨骼動畫int m_arm_offsetX;<span style="white-space:pre"> </span>//xy偏移量int m_arm_offsetY;bool m_armFaceTo; //朝向,默認為true,向左public:/*外部調用接口*/virtual inline void setDesPoint(const Point& p){m_desPoint = p;} //設置終點位置//IDvirtual inline int getId(){return m_id;}protected:/*與戰斗相關的數據*/int m_id; //idPoint m_desPoint;//目標位置int m_speed; //移動速度int m_initSpeed;}; #endif其實先跟大家說,Role的行走主要還是依賴于update函數
再解釋上面可能大家不太清楚的幾個名詞
1、xy的偏移量,因為我們用cocostudio做出來的骨骼動畫可能范圍和描點都不是和我們現在這個Role類(Node)完全重合的。所以可能要或多或少地調整一下骨骼動畫的位置。
2、朝向。就是我們的角色是面向哪里的,有左右之分(true/false)
三個觸摸回調函數實現在沒有講控制效果之前也就沒什么的。除了onTouchBegan要根據傳過來的觸摸點判斷是不是被點中。如果是,就返回true不是就返回false。
負責角色移動的還是要看update函數
</pre>update調用update_pos<pre name="code" class="cpp">void Role::update_pos(){if(m_desPoint.x > this->getPosition().x && m_armFaceTo == true){ m_armFaceTo = false;}if(m_desPoint.x < this->getPosition().x && m_armFaceTo == false){m_armFaceTo = true;}if(m_armFaceTo){m_arm->setVisible(false);m_arm->setPosition(m_arm_offsetX,m_arm_offsetY);m_arm->setScaleX(1.0f);m_arm->setVisible(true);}else{m_arm->setVisible(false);m_arm->setScaleX(-1.0f);m_arm->setPosition(-m_arm_offsetX,m_arm_offsetY);m_arm->setVisible(true);}if(!Rect(m_desPoint.x-m_speed/2,m_desPoint.y-m_speed/2,m_speed,m_speed).containsPoint(getPosition())){this->move();float distance = ccpDistance(getPosition(),m_desPoint);float t = distance / m_speed;float speed_x = (m_desPoint.x - getPositionX()) / t;float speed_y = (m_desPoint.y - getPositionY()) / t;setPositionX(getPositionX() + speed_x);setPositionY(getPositionY() + speed_y);}else{this->stand();}}講一講邏輯就可以了,就是不斷更新Role的x和y,直到接近m_desPoint,要轉身的時候就轉身。
有兩個函數 move()和stand() 其實是用來控制骨骼動畫的。move負責播放跑步的動畫,stand負責播放待機的動畫
對了,好像Role的init函數還沒有講?
bool Role::init(const std::string& name,FlightLayer* layer){m_layer = layer;if(!m_arm){m_arm = Armature::create(name);m_arm->setPosition(m_arm_offsetX,m_arm_offsetY);setContentSize(m_arm->getContentSize());this->addChild(m_arm,1);m_armFaceTo = true;scheduleUpdate();return true;}else{return false;} }很簡單吧。。也沒什么。
好吧,這節到此結束。
我的csdn地址:http://blog.csdn.net/hezijian22
郵箱地址:578690286@qq.com
如有問題或指教,歡迎與我交流,謝謝。
總結
以上是生活随笔為你收集整理的cocos2dx3.2开发 RPG《Flighting》(五)只能行走的战斗场景的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python神经网络1之TensorFl
- 下一篇: 总结:G1收集器