1. 程式人生 > >Unity3D課程學習筆記(五)

Unity3D課程學習筆記(五)

打飛碟遊戲改進版----動作與物理分離版

本次作業主要是要新增一個管理物理動作的動作管理器CCPhysicsActionManager,並且通過新增按鈕來決定選擇哪一種創造飛碟的方式。因此在遊戲的初始階段有2中選擇,一種是飛碟的運動將以運動的方式形成,另外一種是飛碟會以物理的方式形成,採用介面卡模式,因此需要新建一個公共介面IActionManager,用來確定遊戲開始的時候選擇哪一種動作管理器。

遊戲的初始介面效果圖如下所示:


介面卡模式的UML圖的設計如下所示:


接下來是各個相關類修改後的方法,及其程式碼,因為程式碼和之前的設計有很多相似的地方,所以這裡只分析修改過和新新增的程式碼,不做重複,相關可以參考上一次的作業:

Unity3D課程學習筆記(四)

下面是一個公共的介面IActionManager,是CCActionManager和CCPhysicsActionManager需要實現的,在場景控制器中通過定義IActionManager和選擇相應的按鈕,可以選擇使用運動學方式還是物理學方式:

public interface IActionManager
{
    int getDiskNumber();
    void setDiskNumber(int _diskNumber);
    void StartThrow(Queue<GameObject> diskQueue);
}

然後是CCActionManager的程式碼,基本上跟上次的沒有什麼改變,但是在這個類的程式碼裡面添加了一個延遲執行Start的方法,因為我需要在開始的時候通過按鈕來選擇相應的模式,但是又不能在Update中時刻檢測這個狀態碼的改變,所以只好採用這種方式,具體的程式碼如下所示:

public class CCActionManager : SSActionManager, ISSActionCallback, IActionManager
{
    public FirstSceneController sceneController;
    public List<CCFlyAction> Fly;
    private int DiskNumber = 0;
    //private bool isFirstEnter = true;

    private List<SSAction> Used = new List<SSAction>();
    private List<SSAction> Free = new List<SSAction>();

    public void setDiskNumber(int _diskNumber)
    {
        DiskNumber = _diskNumber;
    }

    public int getDiskNumber()
    {
        return DiskNumber;
    }

    //GetSSAction,首先從工廠裡面找,如果沒有的話就創造
    SSAction GetSSAction()
    {
        SSAction action = null;
        if (Free.Count > 0)
        {
            action = Free[0];
            Free.Remove(Free[0]);
        }
        else
        {
            action = ScriptableObject.Instantiate<CCFlyAction>(Fly[0]);
        }

        Used.Add(action);
        return action;
    }

    //FreeSSAction
    public void FreeSSAction(SSAction action)
    {
        SSAction temp = null;
        foreach (SSAction disk in Used)
        {
            if (action.GetInstanceID() == disk.GetInstanceID())
            {
                temp = disk;
            }
        }
        if (temp != null)
        {
            temp.reset();
            Free.Add(temp);
            Used.Remove(temp);
        }
    }

    protected new void Start()
    {
        StartCoroutine(PlayerAttack());
    }

    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Competeted,
        int Param = 0,
        string strParam = null,
        UnityEngine.Object objectParam = null)
    {
        if (source is CCFlyAction)
        {
            DiskNumber--;
            DiskFactory factory = Singleton<DiskFactory>.Instance;
            factory.FreeDisk(source.gameobject);
            FreeSSAction(source);
        }
    }

    public void StartThrow(Queue<GameObject> diskQueue)
    {
        foreach (GameObject temp in diskQueue)
        {
            //if (GetSSAction() != null)
            RunAction(temp, GetSSAction(), (ISSActionCallback)this);
        }
    }

    IEnumerator PlayerAttack()
    {
        yield return new WaitForSeconds(3.0f);
        Debug.Log("CCActionManager!");
        sceneController = (FirstSceneController)Director.getInstance().currentSceneController;
        if (sceneController.getActionState() == ActionState.MOVING)
        {
            Debug.Log("CCActionManager! Set actionManager!");
            sceneController.actionManager = this;
        }
        Fly.Add(CCFlyAction.GetSSAction());
    }
}

接著是CCPhysicsActionManager的程式碼,這個類跟CCActionManager的程式碼非常相似,只是我新建了一個CCPhysicsAction的基本動作來定義物理動作的生成,所以感覺有點冗餘,但是這樣做又是最方便的,所以就先採取這種方法。

public class CCPhysicsActionManager : SSActionManager, ISSActionCallback, IActionManager
{
    public FirstSceneController sceneController;
    public List<CCPhysicsFlyAction> Fly;
    private int DiskNumber = 0;

    private List<SSAction> Used = new List<SSAction>();
    private List<SSAction> Free = new List<SSAction>();

    public void setDiskNumber(int _diskNumber)
    {
        DiskNumber = _diskNumber;
    }

    public int getDiskNumber()
    {
        return DiskNumber;
    }

    //GetSSAction,首先從工廠裡面找,如果沒有的話就創造
    SSAction GetSSAction()
    {
        SSAction action = null;
        if (Free.Count > 0)
        {
            action = Free[0];
            Free.Remove(Free[0]);
        }
        else
        {
            action = ScriptableObject.Instantiate<CCPhysicsFlyAction>(Fly[0]);
        }

        Used.Add(action);
        return action;
    }
    //FreeSSAction
    public void FreeSSAction(SSAction action)
    {
        SSAction temp = null;
        foreach (SSAction disk in Used)
        {
            if (action.GetInstanceID() == disk.GetInstanceID())
            {
                temp = disk;
            }
        }
        if (temp != null)
        {
            temp.reset();
            Free.Add(temp);
            Used.Remove(temp);
        }
    }

    protected new void Start()
    {
        StartCoroutine(PlayerAttack());
    }

    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Competeted,
        int Param = 0,
        string strParam = null,
        UnityEngine.Object objectParam = null)
    {
        if (source is CCPhysicsFlyAction)
        {
            DiskNumber--;
            DiskFactory factory = Singleton<DiskFactory>.Instance;
            factory.FreeDisk(source.gameobject);
            FreeSSAction(source);
        }
    }

    public void StartThrow(Queue<GameObject> diskQueue)
    {
        foreach (GameObject temp in diskQueue)
        {
            //if (GetSSAction() != null)
            RunAction(temp, GetSSAction(), (ISSActionCallback)this);
        }
    }

    IEnumerator PlayerAttack()
    {
        //Start After 3 seconds.
        yield return new WaitForSeconds(3.0f);
        Debug.Log("CCActionManager!");
        sceneController = (FirstSceneController)Director.getInstance().currentSceneController;
        if (sceneController.getActionState() == ActionState.PHYSICS)
        {
            Debug.Log("PhysicsActionManager! Set actionManager!");
            sceneController.actionManager = this;
        }
        Fly.Add(CCPhysicsFlyAction.GetSSAction());
    }
}

在工廠中創造飛碟的時候,需要注意的是,如果選擇的是物理狀態,那麼就需要給飛碟新增一個剛體屬性,這樣他的物理特性才能夠體現出來,並且在CCPhysicsActionManager中應該要注意把相關的內容放在FixedUpdate中,一下是這兩個類的程式碼

CCPhysicsActionManager

public class CCPhysicsFlyAction : SSAction
{
    //動作基本引數:加速度,垂直速度,邊界,飛行時間,飛行方向
    public ScoreRecorder scoreRecorder { get; set; }
    private float acceleration = 3.0f;
    private float horizontalSpeed;
    private float lowerBound = -4;
    private float flyTime;
    private Vector3 direction;
    Rigidbody rigidbody;

    public override void Start()
    {
        enable = true;
        flyTime = 0;
        horizontalSpeed = gameobject.GetComponent<DiskData>().getDiskSpeed();
        direction = gameobject.GetComponent<DiskData>().getDiskDirection();
        scoreRecorder = Singleton<ScoreRecorder>.Instance;
        rigidbody = this.gameobject.GetComponent<Rigidbody>();
        if (rigidbody)
        {
            Debug.Log("AddForce");
            rigidbody.velocity = horizontalSpeed * direction * 3;
        }
    }

    public override void Update()
    {
        if (gameobject.activeSelf)
        {
            if (checkWhetherShouldRecycle())
            {
                scoreRecorder.MinRecord();
            }
        }
    }

    public override void FixedUpdate()
    {
        Debug.Log("Physics!");
        if (gameobject.activeSelf)
        {
            if (checkWhetherShouldRecycle())
            {
                scoreRecorder.MinRecord();
            }
        }
    }

    //如果飛碟的高度低於最低限度,則回收該飛碟
    private bool checkWhetherShouldRecycle()
    {
        if (this.transform.position.y < lowerBound)
        {
            this.destroy = true;
            this.enable = false;
            this.callback.SSActionEvent(this);
            return true;
        }
        return false;
    }

    public static CCPhysicsFlyAction GetSSAction()
    {
        CCPhysicsFlyAction action = ScriptableObject.CreateInstance<CCPhysicsFlyAction>();
        return action;
    }
}

DiskFactory類

public class DiskFactory : MonoBehaviour
{
    public GameObject Disk_Product;
    public FirstSceneController sceneController;

    //Used用來儲存被啟用的飛碟,Free用來儲存空閒的飛碟
    private List<DiskData> Used = new List<DiskData>();
    private List<DiskData> Free = new List<DiskData>();

    //Awake
    private void Awake()
    {
        sceneController = (FirstSceneController)Director.getInstance().currentSceneController;
        Disk_Product = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/DiskModel"),
            Vector3.zero, Quaternion.identity);
        Disk_Product.SetActive(false);
    }

    //GetDisk
    public GameObject GetDisk(int Game_Round)
    {
        GameObject NewDiskProduct = null;
        if (Free.Count > 0)
        {
            NewDiskProduct = Free[0].gameObject;
            Free.Remove(Free[0]);
        }
        else
        {
            NewDiskProduct = GameObject.Instantiate<GameObject>(Disk_Product, Vector3.zero,
                Quaternion.identity);
            NewDiskProduct.AddComponent<DiskData>();
        }

        //控制飛碟產生的頻率
        int From = 0;
        if (Game_Round == 1)
        {
            From = 100;
        }
        if (Game_Round == 2)
        {
            From = 250;
        }
        int TheDiskColor = Random.Range(From, Game_Round * 499);

        if (TheDiskColor > 500)
        {
            Game_Round = 2;
        }
        else if (TheDiskColor > 300)
        {
            Game_Round = 1;
        }
        else
        {
            Game_Round = 0;
        }

        //根據回合控制生成飛碟的屬性
        if (Game_Round == 0)
        {
            setDiskProp(NewDiskProduct, Color.yellow, 2.0f);
        }
        else if (Game_Round == 1)
        {
            setDiskProp(NewDiskProduct, Color.red, 3.0f);
        }
        else if (Game_Round == 2)
        {
            setDiskProp(NewDiskProduct, Color.black, 4.0f);
        }
        Debug.Log("Produce!");
        if (sceneController.getActionState() == ActionState.PHYSICS)
        {
            Debug.Log("Check!");
            NewDiskProduct.AddComponent<Rigidbody>();
        }

        Used.Add(NewDiskProduct.GetComponent<DiskData>());
        NewDiskProduct.name = NewDiskProduct.GetInstanceID().ToString();
        return NewDiskProduct;
    }

    //setDiskProp.
    public void setDiskProp(GameObject Disk, Color _Color, float _Speed)
    {
        Disk.GetComponent<DiskData>().setDiskColor(_Color);
        Disk.GetComponent<DiskData>().setDiskSpeed(_Speed);
        float RanX = UnityEngine.Random.Range(-1.0f, 1.0f) < 0 ? -1 : 1;
        Disk.GetComponent<DiskData>().setDiskDirection(new Vector3(RanX, 1, 0));
        Disk.GetComponent<Renderer>().material.color = _Color;
    }

    //FreeDisk.
    public void FreeDisk(GameObject Disk)
    {
        DiskData temp = null;
        foreach (DiskData disk in Used)
        {
            if (Disk.GetInstanceID() == disk.gameObject.GetInstanceID())
            {
                temp = disk;
            }
        }
        if (temp != null)
        {
            temp.gameObject.SetActive(false);
            Free.Add(temp);
            Used.Remove(temp);
        }
    }
}

至於其他的類,都和上次作業差不多,在這裡就不再多敘述了,如果想檢視相應的程式碼,可以到我的Github

最後附上一張效果圖: