Unity3d學習之路-簡單打飛碟(介面卡模式)
阿新 • • 發佈:2018-11-15
簡單打飛碟(介面卡模式)
遊戲要求
- 按adapter模式設計圖修改之前的簡單打飛碟遊戲
- 使它同時支援物理運動與運動學(變換)運動
介面卡模式
- 介面卡模式是將某個類的介面轉換成我們所期望的另一個介面,將兩個不相容的類組合在一起使用。介面卡起到一種轉換和包裝的作用。介面卡模式主要分為三類:類介面卡模式、物件的介面卡模式、介面的介面卡模式。對於這三種模式的更多介紹,我找到一個比較簡單易懂的部落格: 傳送門。
- 本次遊戲是使用物件的介面卡模式,該種介面卡模式在本次遊戲的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的時候相隔的時間是不一定的。
- FixedUpdate
SSActionManager呼叫SSAction的
FixedUpdate
和Update
,從而實現運動學動作和物理學動作相容,下面是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
來實現物理和運動學的切換
小結
在最後整理程式碼的時候發現,有些地方的實現有些冗餘,對於各個運動管理器的實現可能不是那麼完善。
完整專案請點選傳送門,Assets/Scenes/中的myScene是本次遊戲場景