1. 程式人生 > >Unity3d學習之路-簡單打飛碟(介面卡模式)

Unity3d學習之路-簡單打飛碟(介面卡模式)

簡單打飛碟(介面卡模式)

遊戲要求

  • 按adapter模式設計圖修改之前的簡單打飛碟遊戲
  • 使它同時支援物理運動與運動學(變換)運動

介面卡模式

  • 介面卡模式是將某個類的介面轉換成我們所期望的另一個介面,將兩個不相容的類組合在一起使用。介面卡起到一種轉換和包裝的作用。介面卡模式主要分為三類:類介面卡模式、物件的介面卡模式、介面的介面卡模式。對於這三種模式的更多介紹,我找到一個比較簡單易懂的部落格: 傳送門
  • 本次遊戲是使用物件的介面卡模式,該種介面卡模式在本次遊戲的UML圖如下圖。

UML

遊戲實現

介面卡實現

  • IActionManager

    場景控制器與介面卡的介面,場景控制器通過這個介面告訴介面卡應該選擇哪種實現飛碟飛行動作的方式

public interface IActionManager
{
    void playDisk(GameObject disk, float angle, float power,bool isPhy);
}
  • ActionManagerAdapter

    動作管理介面卡,這個類繼承了IActionManager這個介面,具有物理動作管理器和運動學動作管理器兩個屬性,當IActionManager介面得知場景控制器需要哪種方式實現飛行的時候,通過一個bool變數來進行選擇使用哪個管理器。下面的isPhy如果是true則使用物理動作管理器,反之使用運動學動作管理器

public class ActionManagerAdapter : MonoBehaviour,IActionManager
{
    public FlyActionManager action_manager;
    public PhysisFlyActionManager phy_action_manager;
    public void playDisk(GameObject disk, float angle, float power,bool isPhy)
    {
        if(isPhy)
        {
            phy_action_manager.playDisk(disk, angle, power);
        }
        else
        {
            action_manager.playDisk(disk, angle, power);
        }
    }
    // Use this for initialization
    void Start ()
    {
        action_manager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
        phy_action_manager = gameObject.AddComponent<PhysisFlyActionManager>() as PhysisFlyActionManager;
    }
}

物理動作實現

  • PhysisFlyActionManager

    物理的動作管理器,與運動學管理器類似,但含有物理的飛碟飛行動作屬性。

public class PhysisFlyActionManager : SSActionManager
{

    public PhysisUFOFlyAction fly;                            //飛碟飛行的動作

    protected void Start()
    {
    }
    //飛碟飛行
    public void UFOFly(GameObject disk, float angle, float power)
    {
        fly = PhysisUFOFlyAction.GetSSAction(disk.GetComponent<DiskData>().direction, angle, power);
        this.RunAction(disk, fly, this);
    }
}
  • PhysisUFOFlyAction

    物理的飛碟飛行動作,使用Rigidbody給飛碟新增重力和給一個初速度向量,實現飛碟飛行

public class PhysisUFOFlyAction : SSAction
{
    private Vector3 start_vector;                              //初速度向量
    public float power;
    private PhysisUFOFlyAction() { }
    public static PhysisUFOFlyAction GetSSAction(Vector3 direction, float angle, float power)
    {
        //初始化物體將要運動的初速度向量
        PhysisUFOFlyAction action = CreateInstance<PhysisUFOFlyAction>();
        if (direction.x == -1)
        {
            action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
        }
        else
        {
            action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
        }
        action.power = power;
        return action;
    }

    public override void FixedUpdate()
    {
        //判斷是否超出範圍
        if (this.transform.position.y < -10)
        {
            this.destroy = true;
            this.callback.SSActionEvent(this);
        }
    }
    public override void Update() { }
    public override void Start()
    {
        //使用重力以及給一個初速度
        gameobject.GetComponent<Rigidbody>().velocity = power / 35 * start_vector;
        gameobject.GetComponent<Rigidbody>().useGravity = true;
    }
}
  • SSActionManager
    • FixedUpdate
      在固定的時間間隔內執行此方法,不受遊戲幀率的影響,所以處理Rigidbody的時候最好使用FixedUpdate。
    • Update
      在每一幀的時候呼叫,不同裝置渲染幀的時間不同,所以每次執行Update的時候相隔的時間是不一定的。

SSActionManager呼叫SSAction的FixedUpdateUpdate,從而實現運動學動作和物理學動作相容,下面是SSActionManager的部分程式碼

protected void Update()
{
    foreach (SSAction ac in waitingAdd)
    {
        actions[ac.GetInstanceID()] = ac;                                    
    }
    waitingAdd.Clear();

    foreach (KeyValuePair<int, SSAction> kv in actions)
    {
        SSAction ac = kv.Value;
        if (ac.destroy)         
        {
            waitingDelete.Add(ac.GetInstanceID());
        }
        else if (ac.enable)
        {
            //運動學運動更新
            ac.Update();
        }
    }

    foreach (int key in waitingDelete)
    {
        SSAction ac = actions[key];
        actions.Remove(key);
        DestroyObject(ac);
    }
    waitingDelete.Clear();
}
protected void FixedUpdate()
{
    foreach (SSAction ac in waitingAdd)
    {
        actions[ac.GetInstanceID()] = ac;
    }
    waitingAdd.Clear();

    foreach (KeyValuePair<int, SSAction> kv in actions)
    {
        SSAction ac = kv.Value;
        if (ac.destroy)
        {
            waitingDelete.Add(ac.GetInstanceID());
        }
        else if (ac.enable)
        {
            //物理運動更新
            ac.FixedUpdate();
        }
    }

    foreach (int key in waitingDelete)
    {
        SSAction ac = actions[key];
        actions.Remove(key);
        DestroyObject(ac);
    }
    waitingDelete.Clear();
}

場景控制器實現

  • FirstController

    在以前的場景控制器中新增public bool isPhy = false;屬性,這樣就可以在Inspector中設定使用哪一種動作管理器,下面是FirstController部分程式碼

 public bool isPhy = false;            //是否使用物理運動管理器
 public IActionManager action_manager; //運動管理器介面

 void Start()
{
    SSDirector director = SSDirector.GetInstance();
    director.CurrentScenceController = this;
    disk_factory = Singleton<DiskFactory>.Instance;
    score_recorder = Singleton<ScoreRecorder>.Instance;
    //初始化介面類
    action_manager = gameObject.AddComponent<ActionManagerAdapter>() as IActionManager;
    user_gui = gameObject.AddComponent<UserGUI>() as UserGUI;
}

private void SendDisk()
{
    float position_x = 16;
    if (disk_queue.Count != 0)
    {
        GameObject disk = disk_queue.Dequeue();
        disk_notshot.Add(disk);
        disk.SetActive(true);
        float ran_y = Random.Range(1f, 4f);
        float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
        disk.GetComponent<DiskData>().direction = new Vector3(ran_x, ran_y, 0);
        Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * position_x, ran_y, 0);
        disk.transform.position = position;
        float power = Random.Range(14f, 16f);
        float angle = Random.Range(20f, 25f);
        //使用介面呼叫對應方法
        action_manager.playDisk(disk, angle, power,isPhy);
    }

    for (int i = 0; i < disk_notshot.Count; i++)
    {
        GameObject temp = disk_notshot[i];
        if (temp.transform.position.y < -10 && temp.gameObject.activeSelf == true)
        {
            disk_factory.FreeDisk(disk_notshot[i]);
            disk_notshot.Remove(disk_notshot[i]);
            user_gui.ReduceBlood();
        }
    }
}

飛碟prefab實現

為飛碟預製體新增一個Rigidbody元件,並且不使用重力,在飛碟的Collider元件,勾選isTrigger,這樣兩個飛碟相撞的時候就不會撞飛了

遊戲實現結果

通過勾選FirstController的isPhy來實現物理和運動學的切換

Inspector

小結

在最後整理程式碼的時候發現,有些地方的實現有些冗餘,對於各個運動管理器的實現可能不是那麼完善。

完整專案請點選傳送門,Assets/Scenes/中的myScene是本次遊戲場景