Cocos2d-xna : 橫版戰略游戲開發實驗6 CCAnimate創建角色動畫


游戲從出現以來,一直提升着玩家的需求,游戲開發者不止是考慮簡單的位置移動,例如將游戲中各種演員做成動畫,就可以大大提升游戲的品質,在咱們的這個三國統帥的游戲中,小兵和將領是有各種動作的,這些動作對應動畫,讓游戲的互動感覺更加優秀,這次使用cocos2d-xna中的CCAnimate來實現角色動畫。

首先我們要先准備游戲中所需要的動畫資源,按照設計,游戲中至少有這樣的角色和職業:主將、小兵(步兵、槍兵、騎兵、弓兵),它們的各種所扮演的角色因所屬勢力只是樣子不一樣。

角色的動畫禎按照一個規則命名,這樣就能方便的管理:

{id}_{n}表示的是正方向,而{id}f_{n}表示的是反方向。比如說

QQ截圖20121021222940

A1表示的是角色的id,按照編號的顯示動作分別為:

0-3:攻擊動作

4-5:行走動作

6-6:站立動作

7-8:死亡動作

在這個實驗游戲中,我們有兩個勢力對陣,分別為義軍和黃巾軍,按照這樣的規則制作了如下的資源:

A1:黃巾軍步兵

A2:黃巾軍槍兵

A3:黃巾軍騎兵

A4:黃巾軍弓兵

B1:義軍步兵

B2:義軍槍兵

B3:義軍騎兵

B4:義軍弓兵

另外還加了兩個英雄:

Hero02:關羽

Hero11:張角

然后將他們的各種幀制作完成並命名完畢(這里也許你需要美術的幫助,我已將其完成,可以在最終的文件中瀏覽),用TexturePackerGUI打包。

圖片包下載地址:http://files.cnblogs.com/nowpaper/SanguoCommander5_actors.rar

QQ截圖20121020192249

發布一下它,保存成一個plist和png圖,命名為ActorsPack1,以后也許有Pack2,所以單獨分開保存:

QQ截圖20121020192410

最終的plist文件放入工程Content下,可以建立一個例如plist的文件夾,並且將.plist文件的內容管線設置正確:

QQ截圖20121020192600

這張圖並不正確,你應該建立子文件夾Images,並將ActorsPack1.png放入其中。

好了,下一步就可以開始對工程進行代碼編寫了。

從游戲本身的設計經驗而言,最好的方式是數據驅動邏輯,所以,當我們描寫一個角色動畫類的時候,最先有一個比較明確的數據類,這里我們可以將游戲底層角色的復雜數據包含,並且做處理,例如角色最基本的特性、職業、類型等等:

enum ActorType
{
    None, Soldier,Hero
}
enum ActorPro
{
    None, Infantry, Pikeman, Cavalvy, Archer
}
enum ActorDir
{
    None, Right, Left
}
class ActorData
{
    public ActorData(string id)
    {
        ActorID = id;
    }
    //演員id
    public string ActorID { get; private set; }
    //演員分組
    public string GroupID { get; private set; }
    //類型
    public ActorType ActorType { get; private set; }
    //職業
    public ActorPro ActorPro { get; private set; }
    //獲得一個數據
    public static ActorData getActorData(string id, string groupid, ActorType type, ActorPro pro)
    {
        ActorData data = new ActorData(id);
        data.GroupID = groupid;
        data.ActorType = type;
        data.ActorPro = pro;
        return data;
    }
}

在最上面,我們定義了三個枚舉,分別來表示類型、職業和方向,方向是用來做動畫用的,而類型和職業則是基本數據的需求,大家可以注意到,設計了一個getActorData靜態方法,用來方便的制作基本測試數據,因為按照游戲數據的處理慣例而言,這些數據都是通過表單的方式配置,程序讀取配置解析成為程序數據,方便游戲設計師隨時調整。

下一步就是建立動畫類了,我們使用類繼承的方式抽象動畫類,建立ActorBase類,這個類只是管理角色最基本的動畫,繼承自CCSprite,通過CCAnimate行為來控制動畫:

class ActorBase : CCSprite
{
    public ActorData ActorData { get; private set; }
    private CCAnimate _action_attack;
    private CCAnimate _action_attack_flip;
    private CCAnimate _action_run;
    private CCAnimate _action_run_flip;
    private CCAnimate _action_stand;
    private CCAnimate _action_stand_flip;
    private CCAnimate _action_dead;
    private CCAnimate _action_dead_flip;
    public ActorDir ActorDir { get; set; }
    public ActorBase(ActorData data)
    {
        ActorData = data;
        //創建攻擊動畫
        List _attackFrames = new List();
        List _attackFrames_flip = new List();
        for (int i = 0; i < 4; i++)
        {
            _attackFrames.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "_" + i + ".png"));
            _attackFrames_flip.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "f_" + i + ".png"));
        }
        _action_attack = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_attackFrames, 0.1f));
        _action_attack_flip = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_attackFrames_flip, 0.1f));
        //創建行走動畫
        List _runFrames = new List();
        List _runFrames_flip = new List();
        for (int i = 4; i < 6; i++)
        {
            _runFrames.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "_" + i + ".png"));
            _runFrames_flip.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "f_" + i + ".png"));
        }
        _action_run = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_runFrames, 0.1f));
        _action_run_flip = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_runFrames_flip, 0.1f));
        //創建站立動畫
        List _standFrames = new List();
        List _standFrames_flip = new List();
        for (int i = 6; i < 7; i++)
        {
            _standFrames.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "_" + i + ".png"));
            _standFrames_flip.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "f_" + i + ".png"));
        }
        _action_stand = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_standFrames, 0.2f));
        _action_stand_flip = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_standFrames_flip, 0.2f));
        //創建死亡動畫
        List _deadFrames = new List();
        List _deadFrames_flip = new List();
        for (int i = 7; i < 9; i++)
        {
            _deadFrames.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "_" + i + ".png"));
            _deadFrames_flip.Add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(data.ActorID + "f_" + i + ".png"));
        }
        _action_dead = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_deadFrames, 0.3f));
        _action_dead_flip = CCAnimate.actionWithAnimation(CCAnimation.animationWithFrames(_deadFrames_flip, 0.3f));
        //初始化默認幀
        base.initWithSpriteFrame(_standFrames[0]);
    }
        
    CCAction _currentAnimateAction;
    public void StateToRun()
    {
        if(ActorDir == Roles.ActorDir.Left)
            RunAnimateAction_RepeatForever(_action_run);
        else
            RunAnimateAction_RepeatForever(_action_run_flip);
    }
    //攻擊狀態
    public void StateToAttack()
    {
        currentAnimateActionStop();
        if (ActorDir == Roles.ActorDir.Left)
            RunAnimateAction_RepeatForever(_action_attack);
        else
            RunAnimateAction_RepeatForever(_action_attack_flip);
    }
    //死亡動畫
    public void StateToDead()
    {
        currentAnimateActionStop();
        if (ActorDir == Roles.ActorDir.Left)
            _currentAnimateAction = runAction(_action_dead);
        else
            _currentAnimateAction = runAction(_action_dead_flip);
    }
    //站立動畫
    public void StateToStand()
    {
        if (ActorDir == Roles.ActorDir.Left)
            RunAnimateAction_RepeatForever(_action_stand);
        else
            RunAnimateAction_RepeatForever(_action_stand_flip);
            
            
    }
    //停止當前的動畫
    private void currentAnimateActionStop()
    {
        if (_currentAnimateAction != null)
            this.stopAction(_currentAnimateAction);
    }
    //播放循環動畫的統一方法
    private void RunAnimateAction_RepeatForever(CCAnimate action)
    {
        currentAnimateActionStop();
        _currentAnimateAction = runAction(CCRepeatForever.actionWithAction(action));
    }
}

上述代碼加了一些注釋,希望能夠幫助你的閱讀,動畫行為的制作流程是這樣的:

首先我們要知道有那些幀,它們形成的集合變成CCAnimation的處理類,然后CCAnimate將其加載並形成特定的動畫行為,有興趣的配有可以看cocos2d的底層代碼,CCSprite實際上是帶了一個CCTexture來表示圖像,CCAnimate是按照邏輯變化CCTexture。

在下面的代碼中:StateToRun、StateToAttack、StateToDead、StateToStand等方法都是用來處理角色狀態的動畫,例如對應到當攻擊的時候調用StateToAttack方法。

currentAnimateActionStop和RunAnimateAction_RepeatForever是為了方便處理動畫的特定狀態,因為諸如行走、站立、攻擊,這一類的動畫都是循環性質,統一代碼比較方便。

那么我們就測試一下上面的代碼看看直接的效果,還是為了方便,在本節中,我們直接將動畫放在了開始界面這樣瀏覽很方便了,打開SceneStart.cs,在構造函數中寫如下代碼:

//測試動畫的角色
List id_buff = new List()
{
    "B1","B2","B3","B4","Hero02","A1","A2","A3","A4","Hero11"
};
for (int i = 0; i < 2; i++)
{

    for (int j = 0; j < 5; j++)
    {
        var actor = new ActorBase(new ActorData(id_buff[i*5 + j]));
        actor.ActorDir = (ActorDir)(i + 1);
        actor.position = new CCPoint(64 + i * 64, 64 + j * 64);
        if(j % 2 ==1)
            actor.StateToRun();
        else
            actor.StateToAttack();
        this.addChild(actor);
    }
}
//

上面代碼里,用一個List結構id_buff來描述ID,其實就是角色的前綴名,然后按照順序排成兩列,並且按照單數為行走動畫,雙數為攻擊動畫的形式顯示。

QQ截圖20121021231721

上面是運行測試的效果,后面我們會將它們加到游戲界面中,讓游戲真正的可以玩起來。

本篇例子工程:https://github.com/Nowpaper/SanguoCommander_cocos2dxna_Sample
本例子項目名為:SanguoCommander6


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM