天天看點

Cocos2d-x遊戲執行個體-《跑跑跑》制作教程(第三篇)——讓主角跑

終于進入我們的遊戲的主題了——跑!

來,我們開始讓主角跑起來~

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 &amp;&amp; mPlayer-&gt;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-&gt;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-&gt;objectNamed(</code><code>"PlayerPoint"</code><code>);</code>

<code>25</code>

<code>    </code><code>float</code><code>playerX = playerPointDic-&gt;valueForKey(</code><code>"x"</code><code>)-&gt;floatValue();</code>

<code>26</code>

<code>    </code><code>float</code><code>playerY = playerPointDic-&gt;valueForKey(</code><code>"y"</code><code>)-&gt;floatValue();</code>

<code>27</code>

<code>28</code>

<code>    </code><code>/* -------------- 加載玩家 --------------- */</code>

<code>29</code>

<code>    </code><code>CCSize visibleSize = CCDirector::sharedDirector()-&gt;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-&gt;setPosition(ccp(playerX, playerY));</code>

<code>32</code>

<code>33</code>

<code>    </code><code>/* 精靈添加到地圖 */</code>

<code>34</code>

<code>    </code><code>map-&gt;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>-&gt;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-&gt;addObject(CCSpriteFrame::create(</code><code>"sprite/player1.png"</code><code>, CCRectMake(0, 0, 77, 134)));</code>

<code>    </code><code>framesList-&gt;addObject(CCSpriteFrame::create(</code><code>"sprite/player2.png"</code><code>, CCRectMake(0, 0, 66, 129)));</code>

<code>    </code><code>framesList-&gt;addObject(CCSpriteFrame::create(</code><code>"sprite/player3.png"</code><code>, CCRectMake(0, 0, 99, 132)));</code>

<code>    </code><code>framesList-&gt;addObject(CCSpriteFrame::create(</code><code>"sprite/player4.png"</code><code>, CCRectMake(0, 0, 111, 135)));</code>

<code>    </code><code>framesList-&gt;addObject(CCSpriteFrame::create(</code><code>"sprite/player5.png"</code><code>, CCRectMake(0, 0, 94, 132)));</code>

<code>    </code><code>framesList-&gt;addObject(CCSpriteFrame::create(</code><code>"sprite/player6.png"</code><code>, CCRectMake(0, 0, 64, 128)));</code>

<code>    </code><code>framesList-&gt;addObject(CCSpriteFrame::create(</code><code>"sprite/player7.png"</code><code>, CCRectMake(0, 0, 96, 133)));</code>

<code>    </code><code>framesList-&gt;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-&gt;setLoops(-1);</code>

<code>    </code><code>mSprite-&gt;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-&gt;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>-&gt;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&lt;/pre&gt;</code>

<code>&lt;pre</code><code>class</code><code>=</code><code>"cpp"</code><code>&gt;</code><code>// SimpleMoveControll.cpp檔案</code>

<code>#include "SimpleMoveControll.h"</code>

<code>bool</code><code>SimpleMoveControll::init()</code>

<code>    </code><code>this</code><code>-&gt;iSpeed = 0;</code>

<code>    </code><code>/* 每一幀都要調用update函數,是以要這樣設定 */</code>

<code>    </code><code>this</code><code>-&gt;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-&gt;getCurPosition();</code>

<code>45</code>

<code>    </code><code>pos.x += iSpeed;</code>

<code>46</code>

<code>    </code><code>mControllerListener-&gt;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>-&gt;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>&lt;span style=</code><code>"font-size: 14px;"</code><code>&gt;我們給&lt;span style=</code><code>"font-family: Calibri;"</code><code>&gt;Entity&lt;/span&gt;加了一個父類,那就是&lt;span style=</code><code>"font-family: Calibri;"</code><code>&gt;ControllerListener&lt;/span&gt;,大家都知道為什麼了,因為我們的角色需要被當做一個被控制器控制的對象。繼承了&lt;span style=</code><code>"font-family: Calibri;"</code><code>&gt;ControllerListener&lt;/span&gt;之後,當然就要實作它的方法了。&lt;/span&gt;</code>

<code>&lt;span style=</code><code>"font-size: 14px;"</code><code>&gt;另外,我還為&lt;span style=</code><code>"font-family: Calibri;"</code><code>&gt;Entity&lt;/span&gt;新增了一個方法,那就是&lt;span style=</code><code>"font-family: Calibri;"</code><code>&gt;setController&lt;/span&gt;,當然了,因為我們需要綁定一個控制器。&lt;/span&gt;</code>

<code>&lt;span style=</code><code>"font-size: 14px;"</code><code>&gt;三個方法的實作如下:&lt;/span&gt;</code>

<code>&lt;span style=</code><code>"font-family: Calibri; font-size: 14px;"</code><code>&gt; &lt;/span&gt;</code>

<code>&lt;pre</code><code>class</code><code>=</code><code>"cpp"</code><code>&gt;1</code><code>//Entity.cpp的部分代碼</code>

<code>void</code><code>Entity::setController( Controller* controller )</code>

<code>    </code><code>this</code><code>-&gt;mController = controller;</code>

<code>    </code><code>controller-&gt;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-&gt;setPosition(ccp(x, y));</code>

<code>cocos2d::CCPoint Entity::getCurPosition()</code>

<code>        </code><code>return</code><code>mSprite-&gt;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-&gt;setiSpeed(1);</code>

<code>    </code><code>/* 控制器要添加到場景中才能獲得update事件 */</code>

<code>    </code><code>this</code><code>-&gt;addChild(mSMoveControll);</code>

<code>    </code><code>mPlayer-&gt;setController(mSMoveControll);</code>

簡單說明一下哈,建立一個移動控制器,設定移動速度為1,然後把控制器添加到場景中(這樣它才能獲得update函數的調用),最後把控制器添加到主角身上。

來,編譯運作,看主角瘋狂地跑動吧!

太帥了,簡直就是一個流氓沖着良民狂奔啊喂~= =

本文轉蓬萊仙羽51CTO部落格,原文連結:http://blog.51cto.com/dingxiaowei/1366383,如需轉載請自行聯系原作者

繼續閱讀