Unity3D課程學習筆記(五)
打飛碟遊戲改進版----動作與物理分離版
本次作業主要是要新增一個管理物理動作的動作管理器CCPhysicsActionManager,並且通過新增按鈕來決定選擇哪一種創造飛碟的方式。因此在遊戲的初始階段有2中選擇,一種是飛碟的運動將以運動的方式形成,另外一種是飛碟會以物理的方式形成,採用介面卡模式,因此需要新建一個公共介面IActionManager,用來確定遊戲開始的時候選擇哪一種動作管理器。
遊戲的初始介面效果圖如下所示:
介面卡模式的UML圖的設計如下所示:
接下來是各個相關類修改後的方法,及其程式碼,因為程式碼和之前的設計有很多相似的地方,所以這裡只分析修改過和新新增的程式碼,不做重複,相關可以參考上一次的作業:
下面是一個公共的介面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
最後附上一張效果圖: