日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
【精品教程】Cocos2d-xv3.6制作射箭游戲(二)

本章我們的主要任務是創(chuàng)建射箭的弓箭手(也就是游戲豬腳),并且讓這個豬腳隨著觸摸點的改變不斷的旋轉(zhuǎn)手中的弓箭。

分析:

對于這個射箭的角色而言,它能不停的射出弓箭。當我們按住屏幕上某點時,會從該角色拿弓箭的手的位置“畫”一條標注箭支運動軌跡的紅線(看似拋物線);當在屏幕上滑動手指或鼠標時,這條紅線會隨著觸摸點的位置不停的變換軌跡;當松開屏幕上的手指或鼠標時,會射出一支弓箭,這支弓箭會按最終的紅線路徑移動。另外,玩家手中的弓箭會隨著屏幕上的手指或鼠標旋轉(zhuǎn)。

Player 類

下面我們一起來創(chuàng)建這個 Player 豬腳類,其初步定義如下:

 
 
  1. class Player: public Sprite
  2. {
  3.     public:
  4.     Player();
  5.   
  6.     bool init(Vec2 playerPos);
  7.     static Player* create(Vec2 playerPos);
  8.   
  9.     void createPlayer();
  10.     void createPlayerHpBar();
  11.     void rotateArrow(Point touchPoint);
  12.     void createAndShootArrow( Point touchPoint);
  13.     void shootArrow();
  14.     void finishRunAction();
  15.     void update(float dt);      
  16.   
  17.     CC_SYNTHESIZE(int, playerHp, PlayerHp);        // 玩家血量值
  18.     CC_SYNTHESIZE(bool, startDraw, StartDraw);     // 是否開始畫紅色的路徑線
  19.     CC_SYNTHESIZE(bool, isRunAction, IsRunAction); // 玩家是否正在執(zhí)行射箭動畫
  20.   
  21.     private:
  22.     Vec2 playerPos;            // 角色在 tmx 地圖上的位置
  23.     Size playerSize;           // 角色尺寸
  24.     Size winSize;              // 屏幕窗口尺寸
  25.     Sprite* playerbody;        // 角色身體
  26.     Sprite* playerarrow;       // 角色的弓箭,也就是會隨觸摸點旋轉(zhuǎn)的弓和箭部分
  27.     Sprite* hPBgSprite;        // 角色血條背景精靈
  28.     ProgressTimer* hpBar;      // 角色血條
  29.     ccQuadBezierConfig bezier; // 路徑貝賽爾
  30.     DrawNode* drawNode;        // 這里表示我們的線條對象
  31.   
  32. };

以上的各方法都是我們這兩章需要實現(xiàn)的,其他更多的方法我們將在后面需要的時候再擴充。

其中CC_SYNTHESIZE宏的作用是定義一個保護型的變量,并聲明一個getfunName函數(shù)和setfunName函數(shù),你可以用getfunName函數(shù)得到變量的值,用setfunName函數(shù)設置變量得值。如:CC_SYNTHESIZE(int, playerHp, PlayerHp);定義了一個整型的 playerHp 變量,同時還聲明了 getPlayerHp() 和 setPlayerHp() 兩個方法。

ccQuadBezierConfig是我們新定義的一個結(jié)構(gòu)體,后面我們會詳細的講解。

下面我們就從上到下依次來看看以上的各方法。

創(chuàng)建角色

首先是 Player 的初始化(init)和創(chuàng)建(create),這里我們通過給定 Player 的位置來創(chuàng)建該角色,而這個傳入的坐標位置應該是我們從 TiledMap 的對象層中讀取到的位置(上章有講)。具體代碼如下:

 
 
  1. Player * Player::create(Vec2 playerPos)
  2. {
  3.     Player *pRet  = new Player();
  4.     if (pRet && pRet->init(playerPos))
  5.     {
  6.         pRet->autorelease();
  7.         return pRet;
  8.     }else
  9.     {
  10.         delete pRet;
  11.         pRet = NULL;
  12.         return NULL;
  13.     }
  14. }
  15. bool Player::init(Vec2 playerPos)
  16. {
  17.     if (!Sprite::init())
  18.     {
  19.         return false;
  20.     }
  21.     this->playerPos = playerPos;
  22.     createPlayer();        // 創(chuàng)建角色
  23.     createPlayerHpBar();   // 創(chuàng)建角色血量條
  24.     scheduleUpdate();
  25.     return true;
  26. }

下面我們接著來看看 createPlayer 方法,該方法將初始化我們的 Player 角色,代碼如下所示:

  
 
  1. void Player::createPlayer()
  2. {
  3.     playerbody = Sprite::createWithSpriteFrameName("playerbody.png");
  4.     playerSize = Size(playerbody->getContentSize().width/2, playerbody->getContentSize().height / 3*2);  
  5.     // 設置Player的尺寸,大小略小于playerbody的尺寸,這樣利于我們后面更準確的進行碰撞設置。
  6.     playerbody->setAnchorPoint(Vec2(0.7f, 0.4f));
  7.     this->addChild(playerbody);
  8.     this->setPosition(Vec2(playerPos.x+ GameManager::getInstance()->getObjectPosOffX(), playerPos.y + playerSize.height * 0.4f));
  9.   
  10.     playerarrow = Sprite::createWithSpriteFrameName("playerarrow.png");
  11.     playerarrow->setPosition(Vec2(0, 0));
  12.     playerarrow->setAnchorPoint(Vec2(0.3f, 0.5f));
  13.     this->addChild(playerarrow);   
  14. }

createPlayer 方法中我們將創(chuàng)建如下所示的一個游戲角色。

因為沒有找到合適的游戲資源(原游戲中得到的資源都是零件,要使用需要把它們一幀一幀重組),所以我們的游戲一切從簡,不整那些復雜的。

這里我們只把角色簡單分成了兩個部分,第一部分當然是玩家的身體playerbody,第二部分是隨著觸摸點/鼠標旋轉(zhuǎn)的手和弓箭playerarrow。(PS:當然因為資源限制這個原因,可能會稍稍降低咱游戲的檔次,應該不能怪我啰!O(∩_∩)O~)

設置playerbody位置時,你可能已經(jīng)發(fā)現(xiàn),我們并沒有把角色身體設置在傳入的playerPos處,而是對它稍微做了一定的調(diào)整。這是因為我們傳入的位置它是緊貼本格瓦片底部的(我們制作tmx文件時,需要這樣做。上章沒說清楚,這章補起,要記住哦!)。如下圖所示:

Y值坐標也不可太接近本格瓦片底部,也就是不要設為9.990,9.998這類太接近10的,因為 tmx 文件中存放的坐標值是整數(shù),如果設為9.990,9.998,那么存放的值會是9.990 X 32 = 319.68 = 320,同理 9.998 X 32 也是 320。320 對于瓦片大小是32 X 32的地圖來說是個特殊的數(shù)字,因為 320 /32 = 10。這樣在程序中就會誤以為9.990,9.998之類的點是坐標上的第10個點。

而且上章我們也說過,由于分辨率適配的原因,對象組中對象的位置與實際的位置是有一定的偏差的,所以我們在設置角色身體位置時,需要修正這些偏差。

以上代碼中設置位置的原理圖如下:

其中,對象組在 X 軸上的偏移值我們把它保存在了 GameManager 中,而 GameManager 是個單例類,后面章節(jié)我們會詳細的講解。當然如果你現(xiàn)在就想運行代碼,那就先把GameManager::getInstance()->getObjectPosOffX()部分去掉吧。

創(chuàng)建好角色后,接下來我們需要創(chuàng)建角色的血量條,血量條可通過 Cocos2d-x 中封裝好的進度條類 ProgressTimer 來創(chuàng)建。其代碼段如下:

  
 
  1. void Player::createPlayerHpBar()
  2. {
  3.     // 創(chuàng)建血條底,即進度條的底背景    
  4.     hPBgSprite = Sprite::createWithSpriteFrameName("hpbg.png");
  5.     hPBgSprite->setPosition(Vec2(playerbody->getContentSize().width / 2, playerbody->getContentSize().height));
  6.     playerbody->addChild(hPBgSprite);
  7.     // 創(chuàng)建血條 
  8.     hpBar = ProgressTimer::create(Sprite::createWithSpriteFrameName("hp1.png"));
  9.     hpBar->setType(ProgressTimer::Type::BAR); // 設置進度條樣式(條形或環(huán)形)
  10.     hpBar->setMidpoint(Vec2(0, 0.5f));        // 設置進度條的起始點,(0,y)表示最左邊,(1,y)表示最右邊,(x,1)表示最上面,(x,0)表示最下面。
  11.     hpBar->setBarChangeRate(Vec2(1, 0));      // 設置進度條變化方向,(1,0)表示橫方向,(0,1)表示縱方向。
  12.     hpBar->setPercentage(100);                // 設置當前進度條的進度
  13.     hpBar->setPosition(Vec2(hPBgSprite->getContentSize().width / 2, hPBgSprite->getContentSize().height / 2 ));
  14.     hPBgSprite->addChild(hpBar);
  15.     hPBgSprite->setVisible(false);   // 設置整個血條不可見,我們將在Player 遭受攻擊的時候再顯示血條。
  16. }

#p#

旋轉(zhuǎn)角色弓箭

接下來我們來讓 Player 的弓箭部分跟隨著觸摸點/鼠標旋轉(zhuǎn)。所以我們定義了如下的函數(shù):

  
 
  1. void Player::rotateArrow(Point touchPoint)
  2. {
  3.     // 1    
  4.     auto playerPos = this->getPosition();
  5.     auto pos = playerPos + playerarrow->getPosition();
  6.     // 2
  7.     Point vector = touchPoint - pos;
  8.     auto rotateRadians = vector.getAngle();
  9.     auto rotateDegrees = CC_RADIANS_TO_DEGREES( -1 * rotateRadians);
  10.     // 3
  11.     if (rotateDegrees >= -180 && rotateDegrees <= -90){
  12.         rotateDegrees = -90;
  13.     }
  14.     else if (rotateDegrees >= 90 && rotateDegrees <= 180){
  15.         rotateDegrees = 90;
  16.     }
  17.     // 4
  18.     auto speed = 0.5 / M_PI;
  19.     auto rotateDuration = fabs(rotateRadians * speed);
  20.     // 5
  21.     playerarrow->runAction( RotateTo::create(rotateDuration, rotateDegrees));
  22. }

rotateArrow方法的參數(shù)為觸摸點的位置。

1)獲取角色弓箭在游戲場景中位置;

2)計算弓箭的旋轉(zhuǎn)角度。

這里利用三角正切函數(shù)來計算,原理如下圖所示:

vector(offX,offY) 是觸摸點到弓箭之間的向量,通過 getAngle 方法,我們可以得到 vector 向量與X軸之間的弧度。

再者,我們需要把弧度 rotateRadians 轉(zhuǎn)化為角度,CC_RADIANS_TO_DEGREES就是能把弧度轉(zhuǎn)化為角度的宏。轉(zhuǎn)化時乘 -1 是因為Cocos2d-x中規(guī)定順時針方向為正,這與我們計算出的角度方向相反,所以轉(zhuǎn)化的時候需要把角度a變?yōu)?a。

3)控制旋轉(zhuǎn)角度的范圍,即只讓它在角色右半邊內(nèi)旋轉(zhuǎn)。

4)計算弓箭旋轉(zhuǎn)時間。

speed表示炮塔旋轉(zhuǎn)的速度,0.5 / M_PI其實就是 1 / 2PI,它表示1秒鐘旋轉(zhuǎn)1個圓。

rotateDuration表示旋轉(zhuǎn)特定的角度需要的時間,計算它用弧度乘以速度。

5)讓弓箭執(zhí)行旋轉(zhuǎn)動作。

觸摸響應

好了,現(xiàn)在 Player 就初步定義好了。接下來,我們回到游戲場景把Player加入進去,并來測試下弓箭是否跟隨觸摸點旋轉(zhuǎn)。

在 Cocos2d-x 3.x 引擎中,實現(xiàn)觸摸響應的流程基本是一致的。所以在 3.6 中,其過程依舊是:

  • 重載觸摸回調(diào)函數(shù);
  • 創(chuàng)建并綁定觸摸事件;
  • 實現(xiàn)觸摸回調(diào)函數(shù)。

所以我們要測試弓箭是否跟隨觸摸點旋轉(zhuǎn),第一步請先在 GameScene 中重寫如下的觸摸回調(diào)函數(shù),并聲明變量:

  
 
  1. virtual bool onTouchBegan(Touch *touch, Event *unused_event);  // 開始觸摸屏幕時響應
  2. virtual void onTouchMoved(Touch *touch, Event *unused_event);  // 觸摸屏幕并在屏幕上滑動時響應
  3. virtual void onTouchEnded(Touch *touch, Event *unused_event);  // 觸摸結(jié)束時響應
  4.   
  5. private:
  6.     Point preTouchPoint;      // 上一個觸摸點
  7.     Point currTouchPoint;     // 當前觸摸點

接著,我們需要在 GameScene 的 init 初始化函數(shù)中創(chuàng)建并綁定觸摸事件,并先隨便創(chuàng)建一個 Player 對象,用于測試。如下:

 
 
  1. SpriteFrameCache::getInstance()->addSpriteFramesWithFile("texture.plist", "texture.pvr.ccz");  
  2. player = Player::create(Vec2(winSize.width / 4, winSize.height/5)); 
  3. this->addChild(player);
  4.   
  5. // 獲取事件分發(fā)器
  6. auto dispatcher = Director::getInstance()->getEventDispatcher();
  7. // 創(chuàng)建單點觸摸監(jiān)聽器
  8. auto listener = EventListenerTouchOneByOne::create();
  9. // 讓監(jiān)聽器綁定事件處理函數(shù)
  10. listener->onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan,this);
  11. listener->onTouchMoved = CC_CALLBACK_2(GameScene::onTouchMoved,this);
  12. listener->onTouchEnded = CC_CALLBACK_2(GameScene::onTouchEnded,this);
  13. // 將事件監(jiān)聽器添加到事件調(diào)度器
  14. dispatcher->addEventListenerWithSceneGraphPriority(listener,this);

Player 的位置是固定的,我們當然不能隨便設,這里只是為了測試。后面的章節(jié)中我們會創(chuàng)建一個類來專門管理從 TiledMap 中得到的對象,包括Player、敵人、道具,磚塊等。

以上 plist 和 pvr.ccz文件是我們的打包資源,它們是用 Texturepacker 編輯器打包而來。更多詳細內(nèi)容請點此查看。

綁定好觸摸事件后,最后我們需要實現(xiàn)它們,代碼如下:

 
 
  1. bool GameScene::onTouchBegan(Touch *touch, Event *unused_event)
  2. {
  3.     currTouchPoint = touch->getLocation();
  4.     if( !currTouchPoint.equals(preTouchPoint)){
  5.         player->rotateArrow(currTouchPoint);
  6.     }
  7.     preTouchPoint = currTouchPoint;  
  8.     return true;  
  9. }
  10.   
  11. void GameScene::onTouchMoved(Touch *touch, Event *unused_event)
  12. {
  13.     currTouchPoint = touch->getLocation();
  14.     if( !currTouchPoint.equals(preTouchPoint)){
  15.         player->rotateArrow(currTouchPoint);
  16.     }
  17.     preTouchPoint = currTouchPoint;
  18. }
  19.   
  20. void GameScene::onTouchEnded(Touch *touch, Event *unused_event)
  21. {
  22.     // 射箭,下章內(nèi)容
  23. }

在 onTouchBegan 和 onTouchMoved 函數(shù)中,處理方法是一樣的。即當當前觸摸點與之前的觸摸點不一致時,就旋轉(zhuǎn) Player 的弓箭。

getLocation 方法將 touch 對象中保存的屏幕坐標轉(zhuǎn)換成我們需要的 Cocos2d 坐標。 分不清屏幕坐標和Cocos2d 坐標的童鞋請參考Cocos2d-x3.0坐標系詳解一文。

當觸摸結(jié)束時,Player 對象需要射出弓箭,這個我們暫時不寫。

運行游戲,此時你就可以看到想要的效果了。關于本章資源,請點此下載。


網(wǎng)頁題目:【精品教程】Cocos2d-xv3.6制作射箭游戲(二)
分享地址:http://m.5511xx.com/article/cogcedi.html