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

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

新聞中心

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

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

分析:

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

Player 類(lèi)

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

 
 
  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);     // 是否開(kāi)始畫(huà)紅色的路徑線
  19.     CC_SYNTHESIZE(bool, isRunAction, IsRunAction); // 玩家是否正在執(zhí)行射箭動(dòng)畫(huà)
  20.   
  21.     private:
  22.     Vec2 playerPos;            // 角色在 tmx 地圖上的位置
  23.     Size playerSize;           // 角色尺寸
  24.     Size winSize;              // 屏幕窗口尺寸
  25.     Sprite* playerbody;        // 角色身體
  26.     Sprite* playerarrow;       // 角色的弓箭,也就是會(huì)隨觸摸點(diǎn)旋轉(zhuǎn)的弓和箭部分
  27.     Sprite* hPBgSprite;        // 角色血條背景精靈
  28.     ProgressTimer* hpBar;      // 角色血條
  29.     ccQuadBezierConfig bezier; // 路徑貝賽爾
  30.     DrawNode* drawNode;        // 這里表示我們的線條對(duì)象
  31.   
  32. };

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

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

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

下面我們就從上到下依次來(lái)看看以上的各方法。

創(chuàng)建角色

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

 
 
  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. }

下面我們接著來(lái)看看 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.     // 設(shè)置Player的尺寸,大小略小于playerbody的尺寸,這樣利于我們后面更準(zhǔn)確的進(jìn)行碰撞設(shè)置。
  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)建如下所示的一個(gè)游戲角色。

因?yàn)闆](méi)有找到合適的游戲資源(原游戲中得到的資源都是零件,要使用需要把它們一幀一幀重組),所以我們的游戲一切從簡(jiǎn),不整那些復(fù)雜的。

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

設(shè)置playerbody位置時(shí),你可能已經(jīng)發(fā)現(xiàn),我們并沒(méi)有把角色身體設(shè)置在傳入的playerPos處,而是對(duì)它稍微做了一定的調(diào)整。這是因?yàn)槲覀儌魅氲奈恢盟蔷o貼本格瓦片底部的(我們制作tmx文件時(shí),需要這樣做。上章沒(méi)說(shuō)清楚,這章補(bǔ)起,要記住哦!)。如下圖所示:

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

而且上章我們也說(shuō)過(guò),由于分辨率適配的原因,對(duì)象組中對(duì)象的位置與實(shí)際的位置是有一定的偏差的,所以我們?cè)谠O(shè)置角色身體位置時(shí),需要修正這些偏差。

以上代碼中設(shè)置位置的原理圖如下:

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

創(chuàng)建好角色后,接下來(lái)我們需要?jiǎng)?chuàng)建角色的血量條,血量條可通過(guò) Cocos2d-x 中封裝好的進(jìn)度條類(lèi) ProgressTimer 來(lái)創(chuàng)建。其代碼段如下:

  
 
  1. void Player::createPlayerHpBar()
  2. {
  3.     // 創(chuàng)建血條底,即進(jìn)度條的底背景    
  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); // 設(shè)置進(jìn)度條樣式(條形或環(huán)形)
  10.     hpBar->setMidpoint(Vec2(0, 0.5f));        // 設(shè)置進(jìn)度條的起始點(diǎn),(0,y)表示最左邊,(1,y)表示最右邊,(x,1)表示最上面,(x,0)表示最下面。
  11.     hpBar->setBarChangeRate(Vec2(1, 0));      // 設(shè)置進(jìn)度條變化方向,(1,0)表示橫方向,(0,1)表示縱方向。
  12.     hpBar->setPercentage(100);                // 設(shè)置當(dāng)前進(jìn)度條的進(jìn)度
  13.     hpBar->setPosition(Vec2(hPBgSprite->getContentSize().width / 2, hPBgSprite->getContentSize().height / 2 ));
  14.     hPBgSprite->addChild(hpBar);
  15.     hPBgSprite->setVisible(false);   // 設(shè)置整個(gè)血條不可見(jiàn),我們將在Player 遭受攻擊的時(shí)候再顯示血條。
  16. }

#p#

旋轉(zhuǎn)角色弓箭

接下來(lái)我們來(lái)讓 Player 的弓箭部分跟隨著觸摸點(diǎn)/鼠標(biāo)旋轉(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ù)為觸摸點(diǎn)的位置。

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

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

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

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

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

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

4)計(jì)算弓箭旋轉(zhuǎn)時(shí)間。

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

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

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

觸摸響應(yīng)

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

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

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

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

  
 
  1. virtual bool onTouchBegan(Touch *touch, Event *unused_event);  // 開(kāi)始觸摸屏幕時(shí)響應(yīng)
  2. virtual void onTouchMoved(Touch *touch, Event *unused_event);  // 觸摸屏幕并在屏幕上滑動(dòng)時(shí)響應(yīng)
  3. virtual void onTouchEnded(Touch *touch, Event *unused_event);  // 觸摸結(jié)束時(shí)響應(yīng)
  4.   
  5. private:
  6.     Point preTouchPoint;      // 上一個(gè)觸摸點(diǎn)
  7.     Point currTouchPoint;     // 當(dāng)前觸摸點(diǎn)

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

 
 
  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)建單點(diǎn)觸摸監(jiān)聽(tīng)器
  8. auto listener = EventListenerTouchOneByOne::create();
  9. // 讓監(jiān)聽(tīng)器綁定事件處理函數(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)聽(tīng)器添加到事件調(diào)度器
  14. dispatcher->addEventListenerWithSceneGraphPriority(listener,this);

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

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

綁定好觸摸事件后,最后我們需要實(shí)現(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ù)中,處理方法是一樣的。即當(dāng)當(dāng)前觸摸點(diǎn)與之前的觸摸點(diǎn)不一致時(shí),就旋轉(zhuǎn) Player 的弓箭。

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

當(dāng)觸摸結(jié)束時(shí),Player 對(duì)象需要射出弓箭,這個(gè)我們暫時(shí)不寫(xiě)。

運(yùn)行游戲,此時(shí)你就可以看到想要的效果了。關(guān)于本章資源,請(qǐng)點(diǎn)此下載。


網(wǎng)站名稱(chēng):【精品教程】Cocos2d-xv3.6制作射箭游戲(二)
分享路徑:http://www.5511xx.com/article/cogcedi.html