終于進入我們的遊戲的主題了——跑!
來,我們開始讓主角跑起來~
1. 修改一下糟糕的代碼
我們要給主角加一個動畫,不斷地播放跑步動作。我們來打開熟悉的TollgateScene.cpp檔案,糟糕,我發現這個檔案的init函數有點龐大了。
于是,我做了一個艱難的決定,把建立玩家精靈的工作移到Player類的init函數裡:
<code>01</code>
<code>//Player.h檔案</code>
<code>02</code>
<code>03</code>
<code>#ifndef __PLAYER_H__</code>
<code>04</code>
<code>#define __PLAYER_H__</code>
<code>05</code>
<code>06</code>
<code>#include "Entity.h"</code>
<code>07</code>
<code>08</code>
<code>class</code><code>Player :</code><code>public</code><code>Entity {</code>
<code>09</code>
<code>public</code><code>:</code>
<code>10</code>
<code> </code><code>bool</code><code>initWithTiledMap(CCTMXTiledMap* map);</code>
<code>11</code>
<code>12</code>
<code> </code><code>static</code><code>Player* createWithTiledMap(CCTMXTiledMap* map);</code>
<code>13</code>
<code>};</code>
<code>14</code>
<code>15</code>
<code>#endif</code>
<code>//Player.cpp檔案</code>
<code>#include "Player.h"</code>
<code>Player* Player::createWithTiledMap( CCTMXTiledMap* map )</code>
<code>{</code>
<code> </code><code>Player* mPlayer =</code><code>new</code><code>Player();</code>
<code> </code><code>if</code><code>(mPlayer && mPlayer->initWithTiledMap(map)) {</code>
<code> </code><code>}</code>
<code> </code><code>else</code><code>{</code>
<code> </code><code>CC_SAFE_DELETE(mPlayer);</code>
<code> </code><code>return</code><code>mPlayer;</code>
<code>16</code>
<code>}</code>
<code>17</code>
<code>18</code>
<code>bool</code><code>Player::initWithTiledMap( CCTMXTiledMap* map )</code>
<code>19</code>
<code>20</code>
<code> </code><code>/* 加載對象層 */</code>
<code>21</code>
<code> </code><code>CCTMXObjectGroup* objGroup = map->objectGroupNamed(</code><code>"objects"</code><code>);</code>
<code>22</code>
<code>23</code>
<code> </code><code>/* 加載玩家坐标對象 */</code>
<code>24</code>
<code> </code><code>CCDictionary* playerPointDic = objGroup->objectNamed(</code><code>"PlayerPoint"</code><code>);</code>
<code>25</code>
<code> </code><code>float</code><code>playerX = playerPointDic->valueForKey(</code><code>"x"</code><code>)->floatValue();</code>
<code>26</code>
<code> </code><code>float</code><code>playerY = playerPointDic->valueForKey(</code><code>"y"</code><code>)->floatValue();</code>
<code>27</code>
<code>28</code>
<code> </code><code>/* -------------- 加載玩家 --------------- */</code>
<code>29</code>
<code> </code><code>CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();</code>
<code>30</code>
<code> </code><code>CCSprite* playerSprite = CCSprite::create(</code><code>"sprite/player1.png"</code><code>);</code>
<code>31</code>
<code> </code><code>playerSprite->setPosition(ccp(playerX, playerY));</code>
<code>32</code>
<code>33</code>
<code> </code><code>/* 精靈添加到地圖 */</code>
<code>34</code>
<code> </code><code>map->addChild(playerSprite);</code>
<code>35</code>
<code>36</code>
<code> </code><code>/* 綁定精靈對象 */</code>
<code>37</code>
<code> </code><code>setSprite(playerSprite);</code>
<code>38</code>
<code> </code><code>return</code><code>true</code><code>;</code>
<code>39</code>
Player類改得有點多,把建立和初始化的函數加了一個參數:地圖對象。這樣我們就可以直接在player的init函數中幫精靈對象添加到地圖了。
然後現在TollgateScene.cpp的init函數就好看多了:
<code>bool</code><code>TollgateScene::init()</code>
<code> </code><code>/* 加載地圖 */</code>
<code> </code><code>CCTMXTiledMap* map = CCTMXTiledMap::create(</code><code>"map/level01.tmx"</code><code>);</code>
<code> </code><code>this</code><code>->addChild(map);</code>
<code> </code><code>/* 建立玩家 */</code>
<code> </code><code>Player* mPlayer = Player::createWithTiledMap(map);</code>
2.主角跑動動畫
現在開始真正給主角加一個動畫了,我們先給Player類新增一個函數:
<code>void</code><code>Player::run()</code>
<code> </code><code>CCArray* framesList = CCArray::create();</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player1.png"</code><code>, CCRectMake(0, 0, 77, 134)));</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player2.png"</code><code>, CCRectMake(0, 0, 66, 129)));</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player3.png"</code><code>, CCRectMake(0, 0, 99, 132)));</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player4.png"</code><code>, CCRectMake(0, 0, 111, 135)));</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player5.png"</code><code>, CCRectMake(0, 0, 94, 132)));</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player6.png"</code><code>, CCRectMake(0, 0, 64, 128)));</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player7.png"</code><code>, CCRectMake(0, 0, 96, 133)));</code>
<code> </code><code>framesList->addObject(CCSpriteFrame::create(</code><code>"sprite/player8.png"</code><code>, CCRectMake(0, 0, 103, 138)));</code>
<code> </code><code>CCAnimation* animation = CCAnimation::createWithSpriteFrames(framesList, 0.2f);</code>
<code> </code><code>animation->setLoops(-1);</code>
<code> </code><code>mSprite->runAction(CCAnimate::create(animation));</code>
我為了盡量減少教程裡出現的代碼量,以及盡量讓素材簡單,我采用最直接的方式來建立CCSpriteFrame對象,如果大家不喜歡的話,可以用CCSpriteFrameCache的方式來建立,這裡就不多說了。建立動畫的方式也不說了,我預設大家有了解過。
好吧,還是簡單說一下,先建立一組CCSpriteFrame對象,然後用這組對象建立一個CCAnimation對象,這個對象還不能用來形成動畫,還必須建立一個CCAnimate對象,然後精靈類通過runAction方法來執行動畫。setLoops(-1)是為了循環播放動畫。
然後,繼續打開我們熟悉的TollgateScene.cpp的init函數,加上一句代碼:
OK,編譯運作,精靈已經在跑動了!當然,隻是原地跑。
<code>1</code>
<code>/* 建立玩家 */</code>
<code>2</code>
<code>3</code>
<code>4</code>
<code> </code><code>/* 讓玩家跑起來 */</code>
<code>5</code>
<code> </code><code>mPlayer->run();</code>
接下來要新增的代碼有點多,慢慢來。
3. 控制器
讓主角向前跑,我想采用組合來實作,把向前跑作為一個功能單獨寫成一個類,主角隻要增加一個成員變量(向前跑的類),就能實作向前跑的動作。而這個類,就是控制器。
因為考慮到不止一個控制器,我們來稍微寫多幾行代碼,以便以後擴充。先來實作控制器的父類(在實體檔案夾建立Controller.h和Controller.cpp檔案):
<code>//Controller.h檔案</code>
<code>#ifndef __CONTROLLER_H__</code>
<code>#define __CONTROLLER_H__</code>
<code>#include "cocos2d.h"</code>
<code>#include "ControllerListener.h"</code>
<code>using</code><code>namespace</code> <code>cocos2d;</code>
<code>class</code><code>Controller :</code><code>public</code><code>CCNode {</code>
<code> </code><code>/* 設定監聽對象 */</code>
<code> </code><code>void</code><code>setControllerListener(ControllerListener* mControllerListener);</code>
<code>protected</code><code>:</code>
<code> </code><code>ControllerListener* mControllerListener;</code>
<code>//Controller.cpp檔案</code>
<code>#include "Controller.h"</code>
<code>void</code><code>Controller::setControllerListener( ControllerListener* mControllerListener )</code>
<code>6</code>
<code>7</code>
<code> </code><code>this</code><code>->mControllerListener = mControllerListener;</code>
<code>8</code>
很簡單的一個類,隻有一個變量和一個方法,我們來看看ControllerListener是做什麼用的。ControllerListener就是将要被控制的對象,比如我們主角,隻要繼承了ControllerListener接口,就能夠被控制器控制。很友善吧~針對接口程式設計,使得代碼稍微沒有那麼糟糕。
看看ControllerListener的代碼:
<code>//ControllerListener.h檔案</code>
<code>#ifndef __CONTROLLER_LISTENER_H__</code>
<code>#define __CONTROLLER_LISTENER_H__</code>
<code>class</code><code>ControllerListener {</code>
<code> </code><code>virtual</code><code>void</code> <code>setSimplePosition(</code><code>int</code><code>x,</code><code>int</code><code>y) = 0;</code>
<code> </code><code>virtual</code><code>CCPoint getCurPosition() = 0;</code>
也很簡單,隻有頭檔案,定義了兩個虛函數,用來設定和擷取被控制對象的坐标。我不太喜歡C++的編碼,有點繁瑣,我對Java中毒很深,嘻嘻,Java要定義一個接口的話就簡單多了,啊喂,跑題了~
4. 簡單移動控制器
我們來實作我們的第一個控制器,控制物體隻往前移動的控制器,看代碼:
<code>//SimpleMoveController.h檔案</code>
<code>#ifndef __SIMPLE_MOVE_CONTROLL_H__</code>
<code>#define __SIMPLE_MOVE_CONTROLL_H__</code>
<code>class</code><code>SimpleMoveControll :</code><code>public</code><code>Controller {</code>
<code> </code><code>CREATE_FUNC(SimpleMoveControll);</code>
<code> </code><code>virtual</code><code>bool</code> <code>init();</code>
<code> </code><code>virtual</code><code>void</code> <code>update(</code><code>float</code><code>dt);</code>
<code> </code><code>/* 設定移動速度 */</code>
<code> </code><code>void</code><code>setiSpeed(</code><code>int</code><code>iSpeed);</code>
<code>private</code><code>:</code>
<code> </code><code>int</code><code>iSpeed;</code>
<code>#endif</pre></code>
<code><pre</code><code>class</code><code>=</code><code>"cpp"</code><code>></code><code>// SimpleMoveControll.cpp檔案</code>
<code>#include "SimpleMoveControll.h"</code>
<code>bool</code><code>SimpleMoveControll::init()</code>
<code> </code><code>this</code><code>->iSpeed = 0;</code>
<code> </code><code>/* 每一幀都要調用update函數,是以要這樣設定 */</code>
<code> </code><code>this</code><code>->scheduleUpdate();</code>
<code>void</code><code>SimpleMoveControll::update(</code><code>float</code><code>dt )</code>
<code>40</code>
<code>41</code>
<code> </code><code>if</code><code>(mControllerListener == NULL) {</code>
<code>42</code>
<code> </code><code>return</code><code>;</code>
<code>43</code>
<code>44</code>
<code> </code><code>CCPoint pos = mControllerListener->getCurPosition();</code>
<code>45</code>
<code> </code><code>pos.x += iSpeed;</code>
<code>46</code>
<code> </code><code>mControllerListener->setSimplePosition(pos.x, pos.y);</code>
<code>47</code>
<code>48</code>
<code>49</code>
<code>void</code><code>SimpleMoveControll::setiSpeed(</code><code>int</code><code>iSpeed )</code>
<code>50</code>
<code>51</code>
<code> </code><code>this</code><code>->iSpeed = iSpeed;</code>
<code>52</code>
SimplerMoveController繼承了Controller類,也很簡單,擁有一個成員變量iSpeed,用來設定移動速度。
這裡簡單介紹一下update(float dt)函數,update函數是CCNode節點的函數,有什麼用呢?很強大的。我們都知道,遊戲的畫面是一幀幀的繪制,進而形成豐富多彩的世界。而程式隻需要在每一幀裡執行操作,繪制圖形。
Update函數提供了一個入口,讓我們可以在遊戲的每一幀裡執行我們自己想要做的事情。是的,就算你隻是在裡面放一個屁都是允許的(你不會當真了吧?= =)。
但是咯,不可能每個CCNode對象都執行一次update函數吧?(不是每個人都需要update不是麼?),是以,預設情況下update函數是不會被調用的,需要對象通過scheduleUpdate方法來注冊被調用的權限。
然後float dt參數是什麼呢?我們都知道CPU是一個很忙的孩子(雖然它很聰明),CPU不可能讓所有update函數同時進行的,隻能是一個個執行,是以總有人先調用有人後調用,float
dt參數就記錄了某個update函數從最後一次被調用到本次調用時經過了多少毫秒。那這又有什麼用呢?很有用,但是我們先不管~
然後,SimpleMoveController在update函數裡做了一件很偉大的事情,那就是改變被控制者的X坐标,使得被控制者往前移動了一段距離。
5. 給主角綁定一個控制器吧
整了這麼多代碼,主角都還沒有跑起來,太累了,我不寫了!啊,才怪呢,現在寫,現在寫~
有個不幸的消息,我們需要稍微改改Entity類(好吧,不要恨我,代碼總是逐漸優化的嘛T^T):
<code>//Entity.h檔案</code>
<code>#ifndef __ENTITY_H__</code>
<code>#define __ENTITY_H__</code>
<code>class</code><code>Entity :</code><code>public</code><code>CCNode,</code><code>public</code><code>ControllerListener {</code>
<code> </code><code>void</code><code>setSprite(CCSprite* mSprite);</code>
<code> </code><code>void</code><code>setController(Controller* controller);</code>
<code> </code><code>/* 實作SimpleMoveListener接口的方法 */</code>
<code> </code><code>virtual</code><code>void</code> <code>setSimplePosition(</code><code>int</code><code>x,</code><code>int</code><code>y);</code>
<code> </code><code>virtual</code><code>CCPoint getCurPosition();</code>
<code> </code><code>CCSprite* mSprite;</code>
<code> </code><code>Controller* mController;</code>
<code><span style=</code><code>"font-size: 14px;"</code><code>>我們給<span style=</code><code>"font-family: Calibri;"</code><code>>Entity</span>加了一個父類,那就是<span style=</code><code>"font-family: Calibri;"</code><code>>ControllerListener</span>,大家都知道為什麼了,因為我們的角色需要被當做一個被控制器控制的對象。繼承了<span style=</code><code>"font-family: Calibri;"</code><code>>ControllerListener</span>之後,當然就要實作它的方法了。</span></code>
<code><span style=</code><code>"font-size: 14px;"</code><code>>另外,我還為<span style=</code><code>"font-family: Calibri;"</code><code>>Entity</span>新增了一個方法,那就是<span style=</code><code>"font-family: Calibri;"</code><code>>setController</span>,當然了,因為我們需要綁定一個控制器。</span></code>
<code><span style=</code><code>"font-size: 14px;"</code><code>>三個方法的實作如下:</span></code>
<code><span style=</code><code>"font-family: Calibri; font-size: 14px;"</code><code>> </span></code>
<code><pre</code><code>class</code><code>=</code><code>"cpp"</code><code>>1</code><code>//Entity.cpp的部分代碼</code>
<code>void</code><code>Entity::setController( Controller* controller )</code>
<code> </code><code>this</code><code>->mController = controller;</code>
<code> </code><code>controller->setControllerListener(</code><code>this</code><code>);</code>
<code>void</code><code>Entity::setSimplePosition(</code><code>int</code><code>x,</code><code>int</code><code>y )</code>
<code> </code><code>if</code><code>(mSprite) {</code>
<code> </code><code>mSprite->setPosition(ccp(x, y));</code>
<code>cocos2d::CCPoint Entity::getCurPosition()</code>
<code> </code><code>return</code><code>mSprite->getPosition();</code>
<code>53</code>
<code> </code><code>return</code><code>CCPoint::CCPoint(0, 0);</code>
<code>54</code>
好了,好了,最後了,大家堅持住,我們打開TollgateScene.cpp的init函數吧,我們要開始跑喇~在init函數最後加上:
<code>/* ------------ 建立玩家簡單移動控制器 -------------- */</code>
<code> </code><code>SimpleMoveControll* mSMoveControll = SimpleMoveControll::create();</code>
<code> </code><code>mSMoveControll->setiSpeed(1);</code>
<code> </code><code>/* 控制器要添加到場景中才能獲得update事件 */</code>
<code> </code><code>this</code><code>->addChild(mSMoveControll);</code>
<code> </code><code>mPlayer->setController(mSMoveControll);</code>
簡單說明一下哈,建立一個移動控制器,設定移動速度為1,然後把控制器添加到場景中(這樣它才能獲得update函數的調用),最後把控制器添加到主角身上。
來,編譯運作,看主角瘋狂地跑動吧!
太帥了,簡直就是一個流氓沖着良民狂奔啊喂~= =
本文轉蓬萊仙羽51CTO部落格,原文連結:http://blog.51cto.com/dingxiaowei/1366383,如需轉載請自行聯系原作者