使用cocos2d-x製作基於Tile地圖的遊戲:不一樣的戰鬥(回合制戰鬥)(四)

本文是《<cocos2d-x for wp7>使用cocos2d-x製作基於Tile地圖的遊戲》教程的第四部分,也就是最後一個部分。如果你沒有對cocos2d-x不瞭解,而且沒有看過前面部分的教程。可以到我博客裏面找到相關文章學習。
本文假定你已經學習過前面的教程,並且對cocos2d-x編程有一定了解。或者你有相關的同等的經驗。
程序截圖:


遙想當年玩FC的時代,經典的遇敵方式是什麼呢,我覺得應該是踩地雷式的,就是在Tile地圖上走着走着,就進入戰鬥畫面了。並且是回合制戰鬥。想想都懷念了。那麼這類是怎麼做的呢。這裏,就來展示下怎麼製作吧。

在本文中,是基於<cocos2d-x for wp7>使用cocos2d-x製作基於Tile地圖的遊戲:碰撞檢測和收集物品(二)完成的基礎上繼續的,如果沒有保存有這個代碼,或者其他的原因沒有代碼。可以到這裏下載到需要的代碼(http://dl.dbank.com/c02vzbkoyl)。

這裏,我們這些數據都硬編碼上去,而且怪物只有一個,並且不能逃跑(因爲只設定了一個按鈕,就是攻擊按鈕)。當然,忍者攻擊和怪物被攻擊,怪物攻擊,怪物死亡都是動態的。而且,最重要的是,一切攻擊情況都在右下角顯示出來,而且是中文的哦。遙想當年神馬外星科技做的中文。呵呵。

在這個教程中,我們需要用到的怪物圖片,這個圖片是我在百度圖庫找到。另外修改了下使之透明。圖片下載:http://dl.dbank.com/c07co6i9aihttp://dl.dbank.com/c01acabz4y,並且添加到images文件夾。

添加中文支持

  爲了能夠實現中文,我們這裏要添加中文支持。因爲我覺得這種回合制遊戲還是中文的爽。添加中文支持,重複的問題了。這個不想重複討論。沒有操作過的直接看這裏:http://blog.csdn.net/fengyun1989/article/details/7486157

  如上面那篇文章所操作的,我同樣添加了一個FontProcessor工程,也添加了一個YaheiFont.spriteFont的字體文件到Content工程的font文件夾。唯一改的代碼就是messages.txt的路徑問題。不過看那文章大家應該懂得怎麼做了。另外,不一樣的是,我設置了那個YaheiFont文件裏面的size標籤爲22.因爲我發現用調用代碼方式無法改變字體大小。

並且,我添加了以下文字到messags.txt文件。因爲我只需要用到這些中文字符。“飛鏢攻擊怪物忍者遭受突然遭遇受到傷害死亡勝利HP躲閃衝撞”。注意,我在顯示文字的時候用到的標點都是英文標點,如果用到中文的標點,需要也添加到messages.txt。


戰鬥場景

下面我們先添加一個類BattleScene到classes文件夾。並且使之繼承與CCScene。修改代碼爲:
 
   class BattleScene : CCScene
    {
        public BattleScene()
        {
            this.addChild(BattleLayer.node());
        }
    }


    class BattleLayer : CCLayer
    {
        public static new BattleLayer node()
        {
            BattleLayer layer = new BattleLayer();
            if (layer.init())
                return layer;
            return null;
        }
    }

並且修改AppDelegate類的Launch方法。使之直接運行這個BattleScene。這樣方便測試。

            //TileMapScene pScene = new TileMapScene();
            BattleScene pScene = new BattleScene();
            //run
            pDirector.runWithScene(pScene);

現在添加以下代碼到BattleLayer:     
   CCSprite player;
        CCSprite monster;
        CCMenu menu;
        public override bool init()
        {
            if (!base.init())
                return false;
            CCSize winSize = CCDirector.sharedDirector().getWinSize();
            player = CCSprite.spriteWithFile(@"Resources/Player");
            player.position = new CCPoint(100, winSize.height - 100);
            this.addChild(player);
            monster = CCSprite.spriteWithFile(@"images/monster");
            monster.position = new CCPoint(winSize.width - 100, winSize.height - 100);
            this.addChild(monster);


            CCLabelTTF label = CCLabelTTF.labelWithString("飛鏢攻擊", "YaheiFont", 20);
            CCMenuItem attackItem = CCMenuItemLabel.itemWithLabel(label, this, attack);
            menu = CCMenu.menuWithItems(attackItem);
            menu.position = new CCPoint(100, 100);
            this.addChild(menu);
            return true;
        }


        void attack(object sender)
        { 
            
        }

上面我們做了什麼呢,添加了一個忍者和一個怪物,並且添加了一個菜單,注意,菜單是用到了我們上面做的中文字體。不是圖片哦。編譯運行,就能看到我們的中文菜單和忍者怪物。當然,現在點擊攻擊沒有任何反應。


下面添加以下聲明到BattleLayer類中;


 
       CCLabelTTF battleMsg;
        CCLabelTTF HPLabel;
        int playerHP = 40;
        int monsterHP = 30;
        int playerA = 10;
        int monsterA = 8;
        int playerDefence = 5;
        int monsterDefence = 3;
        float playerHit = 0.9f;
        float monsterHit = 0.6f;
        float playerDodge = 0.2f;
        float monsterDodge = 0.1f;
        bool attacking = false;
     Random random = new Random();

這裏,我們硬編碼了忍者和怪物的數據。上面的數據有HP,攻擊力(A),防禦力(Defence),命中率(Hit),躲閃率(Dodge)。下面。在init添加代碼:
           battleMsg = CCLabelTTF.labelWithString("遭遇怪物","YaheiFont", 32);
            battleMsg.position = new CCPoint(winSize.width - 200, 100);
            this.addChild(battleMsg);


            HPLabel = CCLabelTTF.labelWithString(String.Format("HP:{0}", playerHP), "Arial", 32);
            HPLabel.position = new CCPoint(100, winSize.height - 150);
            this.addChild(HPLabel);

這裏初始化了這些label。這些用來顯示相關信息的。

添加這麼一個方法:


        void updateBattle(bool isPlayerAttack)
        {
if (isPlayerAttack)
            {
                int beingAttack = (int)(playerHit * (1.0f - monsterDodge) * 100);
                if (random.Next() % 100 < beingAttack)
                {
                    int damage = playerA - monsterDefence;
                    monsterHP = monsterHP - damage;
                    if (monsterHP <= 0)
                    {
                        battleMsg.setString("忍者飛鏢攻擊,\n怪物受到傷害{0}HP\n怪物死亡,忍者勝利");
                        attacking = false;                        
                    }
                    else
                        battleMsg.setString(String.Format("忍者飛鏢攻擊,\n怪物受到傷害{0}HP", damage));
                }
                else
                    battleMsg.setString("忍者飛鏢攻擊,\n怪物躲閃");
            }
            else
            {
                int beingAttack = (int)(monsterHit * (1.0f - playerDodge) * 100);
                if (random.Next() % 100 < beingAttack)
                {
                    int damage = monsterA - playerDefence;
                    playerHP = playerHP - damage;
                    HPLabel.setString(String.Format("HP:{0}", playerHP));
                    if (playerHP <= 0)
                    {
                        battleMsg.setString("怪物衝撞攻擊,\n忍者受到傷害{0}HP\n忍者死亡");
                    }
                    else
                        battleMsg.setString(String.Format("怪物衝撞攻擊,\n忍者受到傷害{0}HP", damage));
                }
                else
                    battleMsg.setString("怪物衝撞攻擊,\n忍者躲閃");
            }
        }
在這個方法裏面,更新了戰鬥的相關數據更新,我這裏是這麼定義命中的,攻擊者的命中率*(1-被打者的躲閃率)。然後取一個隨機數,如果在命中的範圍,就算是命中,不然就躲閃。
傷害=攻擊力-防禦力。戰鬥就這樣簡單定義了。

下面,要做的是定義動作,忍者攻擊的時候飛鏢肯定是要發射出去的吧。怪物被打總得疼一下吧。那樣就得有動作了。


        void playerAttack()
        {
            CCSprite projectile = CCSprite.spriteWithFile(@"images/Projectile");
            projectile.position = new CCPoint(player.position.x, player.position.y);
            projectile.runAction(CCSequence.actions(
                CCMoveTo.actionWithDuration(1.0f, new CCPoint(monster.position.x, monster.position.y)),
                CCCallFuncN.actionWithTarget(this, playerAttackDone)));
            this.addChild(projectile);
        }


        void playerAttackDone(object sender)
        {
            CCSprite sprite = sender as CCSprite;
            monster.runAction(CCBlink.actionWithDuration(0.3f, 3));
            updateBattle(true);
            this.removeChild(sprite, true);
        }

並且在attack方法裏面添加這麼一行;

playerAttack();

上面我們做了什麼呢,在忍者攻擊的時候,添加了一個飛鏢精靈使之從忍者位置運動到怪物位置。並且,添加了一個回調函數來實現當飛鏢命中怪物的時候,怪物做一個blink的運動來閃一下表示疼。

編譯運行就發現,忍者能發射飛鏢了。而且飛鏢命中怪物的時候,怪物閃了一下。它感覺到疼了。

下面,怪物被打了當然要反擊了。 
 
       void monsterAttack()
        {
            var moveleft = CCMoveBy.actionWithDuration(0.1f, new CCPoint(-130, 0));
            var delay = CCDelayTime.actionWithDuration(0.5f);
            monster.runAction(CCSequence.actions(delay, moveleft, moveleft.reverse(), delay,
                CCCallFunc.actionWithTarget(this, monsterAttackDone)));
        }


        void monsterAttackDone()
        {
            updateBattle(false);
        }

怪物攻擊只有一種,就是衝撞攻擊,這個做法就是讓怪物速度移動然後回來。這個效果做得有點一般,不過看起來還過得去。下面,我們要設計的是攻擊流程,忍者攻擊完怪物攻擊。攻擊期間菜單應該不能使用。那麼,添加以下代碼:

          //add at the end of the monsterAttackDone 
       if (attacking)
            {
                attacking = false;
                playerAttack();


            }
            else
                menu.visible = true;


       //add at the end of playerAttackDone
            if (attacking)
            {
                attacking = false;
                monsterAttack();
            }
            else
                menu.visible = true;


            //replace the attack method code of these
            int r = random.Next() % 7;
            if (r == 0)
            {
                battleMsg.setString("怪物突然攻擊");
                attacking = true;
                monsterAttack();
            }
            else 
            {
                attacking = true;
                playerAttack();
            }
            menu.visible = false;

上面,設定了怪物有一定機率突然襲擊。並且用attacking來判斷是否已經開始攻擊。先攻擊方攻擊結束後attacking會置爲false,然後後攻擊方攻擊。那麼在後攻擊方攻擊後,attacking爲false,那麼就停止攻擊,menu重現,進入下一回合。

現在,可以基本完成這個戰鬥了。還有一些要修改的。先添加Microsoft.phone和System.Window的引用,因爲我們用PhoneApplicationService來保存TileMapScene場景的引用,用做場景返回用。其實這個瘋狂的做法微軟認爲是不安全的。但是感覺還不錯。

添加方法:
       void returnTileScene()
        {
            TileMapScene tileScene = null;
            if (PhoneApplicationService.Current.State.ContainsKey("TileScene"))
                tileScene = (TileMapScene)PhoneApplicationService.Current.State["TileScene"];
            if (tileScene != null)
            {
                CCDirector.sharedDirector().replaceScene(tileScene);
            }
            else
            {
                TileMapScene pScene = new TileMapScene();
                PhoneApplicationService.Current.State["TileScene"] = pScene;
            }
        }

並在updateBattle中怪物死亡的if語句最後添加:
                        monster.runAction(CCSequence.actions(CCFadeOut.actionWithDuration(0.3f),CCCallFunc.
                            actionWithTarget(this,returnTileScene)));

這樣,怪物死亡後,就會慢慢隱去,然後跳轉到TileMapScene。

不過,雖然忍者基本不會死去,但是也是有可能的,畢竟如果你運氣實在是太背的話。先添加一個GameoverScene,代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cocos2d;
using Microsoft.Phone.Shell;
namespace cocos2dTIleMapGameDemo.Classes
{
    class GameOverScene : CCScene
    {
        public GameOverScene()
        {
            CCLabelTTF label = CCLabelTTF.labelWithString("YOU LOSE!", "Arial", 32);
            label.position = new CCPoint(400, 300);
            this.addChild(label);
            this.runAction(CCSequence.actions(CCDelayTime.actionWithDuration(3.0f), CCCallFunc.actionWithTarget(this, gameOverDone)));
        }


        void gameOverDone()
        {
            TileMapScene tileScene = null;
            if (PhoneApplicationService.Current.State.ContainsKey("TileScene"))
                tileScene = (TileMapScene)PhoneApplicationService.Current.State["TileScene"];
            if (tileScene != null)
            {
                CCDirector.sharedDirector().replaceScene(tileScene);
            }
            else
            {
                TileMapScene pScene = new TileMapScene();
                PhoneApplicationService.Current.State["TileScene"] = pScene;
            }
        }


    }
}

設定3秒後返回TileMapScene,滿血復活,哈哈。

那麼在忍者死亡的if語句最後添加:

                        GameOverScene scene = new GameOverScene();
                        CCDirector.sharedDirector().replaceScene(scene);

到這裏,我們的戰鬥場景就算是設計完成了。

然後修改AppDelegate的Launch爲:

            TileMapScene pScene = new TileMapScene();
        PhoneApplicationService.Current.State["TileScene"] = pScene;
            //BattleScene pScene = new BattleScene();
            //run
            pDirector.runWithScene(pScene);

並且在TileMapLayer裏面的ccTouchEnded的最後添加:
                Random random = new Random();
                if (random.Next() % 7 == 0)
                {
                    BattleScene scene = new BattleScene();
                    CCDirector.sharedDirector().replaceScene(scene);
                }

設定有七分之一的概率遇敵。

這樣,到這裏,我們所有的工作就算是做完了,現在,我們已經擁有了一個不錯的遊戲,可以喫食物,並且有踩地雷式的回合制戰鬥。

到這裏,我們的這個《<cocos2d-x for wp7>使用cocos2d-x製作基於Tile地圖的遊戲》系列教程就結束了。


示例代碼下載:http://dl.dbank.com/c0f7l0f025


何去何從:
  1. 把戰鬥數據重構出來,並且保存到全局變量以容易調用。
  2. 添加更多的怪物,更多的戰鬥方式。更多的武器。
  3. 添加逃跑功能。
  4. 。。。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章