1. 程式人生 > >Unity設計模式之-Unity3d遊戲開發設計模式之子類沙盒模式

Unity設計模式之-Unity3d遊戲開發設計模式之子類沙盒模式

**

請關注微信公眾號獲取最新訊息

**
微信公眾號
原文連結:http://blog.csdn.net/u013108312/article/details/52208635
本篇文章我們來看下Unity3d遊戲開發設計模式之子類沙盒模式,下面開始,積累提供所有操作(的實現)來定義子類的行為,用一個最簡單的例子來講解這個模式玩家操縱的英雄也就是這個遊戲的主角會有許多技能,我們想定義許多不同的技能,來讓玩家使用。
首先我們定義一個skillBase類作為基類,我們所有技能的動作都在這裡實現。我們可以從這些基本元動作中組合出各種各樣的技能,甚至成百上千種,可以 設計一個doc文件來設計各種技能的操作,及操作順序。這就是之所以為什麼叫子類沙盒的原因,把實現技能的方法作為沙盒,向這個沙盒裡加入各種各樣的元動 作來組成各種各樣的技能。
以傳說系列的鳳凰天驅為例,如下圖(源自世界傳說-換裝迷宮2),這個技能分為,後移、跳躍、播放動畫、前衝、粒子效果、播放動畫等等元動作組成的

如果我們不使用子類沙盒模式,而一個一個寫技能的話會有如下缺點:
1. 會產生大量重複程式碼,造成程式碼冗餘,因為每個技能都有重複的地方,比如說播放聲音,播放動畫等。使用這種模式之後各個操作方法就像一個一個的元件一樣,隨意使用,不會有重複。
2. 每一個技能類都會與遊戲系統和遊戲引擎耦合,比如聲音、動畫播放,而如果把操作實現都寫在基類裡讓子類組合的話就只有基類與之耦合而已。這個原因這種模 式帶來的好處是,如果有某個操作增刪改的話,就不用每個技能類都增刪改一遍,而只改基類就好,方便簡單,簡約。而且重用性相當好,這些程式碼能用在大量遊戲 上(比如說傳說系列每個系列的傳承->魔神劍)
在基類skillBase中,我們需要實現一些技能的元動作,比如move移動,jump跳,playAnimation播放動畫,playSound播放聲音等等,我們把他們組合在一起實現各種技能,這些方法是受保護的。
然後我們需要一個virtual 方法action是最終的技能在子類中定義實現,這就是為什麼上面的原方動作是受保護的了,因為我們不需要呼叫這些元動作,只需要他們組合成的技能action()方法就好,所以只讓子類獲取元動作方法用protected標記。
於是乎我們做一些受保護的方法,在子類中拼裝實現一個組合在一起的整體。
我們要做這些技能類
1. 建立一個繼承於基類skillBase的技能類起名為skill1
2. 重寫skillBase的action方法
3. 把元動作在action中實現
基 類(skillBase)中提供了一個抽象的沙盒方法(action)和一些標記為protected的元動作操作(move、jump、 playSound等),這個類派生了一些沙盒子類,每個沙盒子類(skill1、skill2。。。)都實現了沙盒方法(action),沙盒方法包含 著各種元動作(move、jump、playSound等)。
當符合以下條件時可以使用子類沙盒模式
1. 一個基類和大量派生子類
2. 基類可以提供所有派生子類需要的操作
3. 子類之間的操作、方法有重複
4. 想要減少子類和遊戲系統、遊戲引擎等的耦合

注意:因為此時子類與基類密切相關,所以可能會產生brittle base class問題,也就是你看似安全的修改了基類,但是子類卻可能因此發生問題。

程式碼實現

程式碼如下:
在skillBase中簡單實現幾個元動作和抽象的沙盒方法:

   protected void act(int actID)
    {
        hero.Act(actID);//播放攻擊動畫
    }
    protected void playSound(int soundID)
    {
        audioSource.PlayOneShot(audio[soundID]);
    }

    protected
void move(Vector3 dir, float moveSpeed, bool isRun) { moveFunc.move(dir, moveSpeed, isRun); } protected void jump() { moveFunc.jump(); } protected void particalEffect() { //粒子效果 } virtual public void action()//沙盒方法 { }

再看skill1實現的沙盒方法,比如說我們想實現一個跳斬,就把這些元動作組合在一起

    override public void action()
    {
        playSound(0);
        act(0);
        jump();
        particalEffect();
    }

很簡單對吧
我們再來看看基類初始化的方法
基類的初始化
方法一:建構函式
通過建構函式傳參來賦值,但是這種有參建構函式必須要在每個沙盒子類中base一下,所以如果base類的建構函式這些引數有個增刪改,子類也得跟著全部增刪改。

public SkillBase(AudioSource _audioSource, AudioClip[] _audio, Move1 _moveFunc, Hero2 _hero, GameObject _heroObject)
{
    this.audio = _audio;
    this.audioSource = _audioSource;
    this.moveFunc = _moveFunc;
    this.hero = _hero;
    this.heroObject = _heroObject;
}

方法二:初始化函式init
不過記得要在一開始呼叫,否則小心遊戲崩潰

public void init(AudioSource _audioSource, AudioClip[] _audio, Move1 _moveFunc, Hero2 _hero, GameObject _heroObject)
{
     this.audio = _audio;
     this.audioSource = _audioSource;
     this.moveFunc = _moveFunc;
     this.hero = _hero;
     this.heroObject = _heroObject;
 }

方法三:靜態初始化方法init和類變數(靜態變數)

這樣所有沙盒子類也就是所有skill都共用一種變量了,有好處也有壞處

static private AudioSource audioSource;
static private AudioClip[] audio;
static private Move1 moveFunc;
static private Hero2 hero;
static protected GameObject heroObject;

 static public void init(AudioSource _audioSource, AudioClip[] _audio, Move1 _moveFunc, Hero2 _hero, GameObject _heroObject)
 {
     audio = _audio;
     audioSource = _audioSource;
     moveFunc = _moveFunc;
     hero = _hero;
     heroObject = _heroObject;
 }

總結
小模式,大用處。很簡單的一個設計模式,卻很有用處,成功的減少了沙盒子類與其他類之間的耦合,也增加了重用性,更重要的是這種組合的方式增加了多樣性,這正是遊戲所需要的。

請關注微信公眾號獲取最新訊息

**
微信公眾號