3D遊戲:五、與遊戲世界互動

XXXT805發表於2020-10-27

編寫一個簡單的滑鼠打飛碟(Hit UFO)遊戲

遊戲內容要求:

  • 遊戲有 n 個 round,每個 round 都包括10 次 trial;
  • 每個 trial 的飛碟的色彩、大小、發射位置、速度、角度、同時出現的個數都可能不同。它們由該 round 的 ruler 控制;
  • 每個 trial 的飛碟有隨機性,總體難度隨 round 上升;
  • 滑鼠點中得分,得分規則按色彩、大小、速度不同計算,規則可自由設定。

遊戲的要求:

  • 使用帶快取的工廠模式管理不同飛碟的生產與回收,該工廠必須是場景單例項的!具體實現見參考資源 Singleton 模板類
  • 儘可能使用前面 MVC 結構實現人機互動與遊戲模型分離

UML:
在這裡插入圖片描述
虛擬碼:

getDisk(ruler) 
BEGIN
	IF (free list has disk) THEN
	a_disk = remove one from list
	ELSE
	a_disk = clone from Prefabs
	ENDIF
	Set DiskData of a_disk with the ruler
	Add a_disk to used list
	Return a_disk
END
FreeDisk(disk)
BEGIN
	Find disk in used list
	IF (not found) THEN THROW exception
	Move disk from used to free list
END

程式碼實現:
在這裡插入圖片描述

  • DiskData:包括射擊飛碟的得分、飛碟顏色、初始的位置、大小。
public class DiskData : MonoBehaviour{
    public int score = 1;                               
    public Color color = Color.white;                   
    public Vector3 direction;                          
    public Vector3 scale = new Vector3( 1 ,0.25f, 1);   
}
  • 飛碟管理員DiskFactory:負責接收SSDirector的請求,使用模板模式根據預製和規則製作飛碟、回收飛碟。
    維護兩個List,分別儲存正在使用的飛碟和空閒的飛碟:
    public GameObject disk_prefab = null;   //飛碟預製體
    private List<DiskData> used = new List<DiskData>(); //正在被使用的飛碟列表
    private List<DiskData> free = new List<DiskData>(); //空閒的飛碟列表

GetDisk:根據規則在空閒列表中選擇飛碟,若空閒列表中沒有要選擇的飛碟,則重新例項化飛碟。

public GameObject GetDisk(int round){
        int choice = 0;
        int scope1 = 1, scope2 = 4, scope3 = 7;           //隨機的範圍
        float start_y = -10f;                             //剛例項化時的飛碟的豎直位置
        string name;
        disk_prefab = null;
        //根據回合,隨機選擇要飛出的飛碟
        if (round == 1) choice = Random.Range(0, scope1);
        else if(round == 2) choice = Random.Range(0, scope2);
        else choice = Random.Range(0, scope3);
        
        //將要選擇的飛碟的name
        if(choice <= scope1) name = "disk1";
        else if(choice <= scope2 && choice > scope1) name = "disk2";
        else name = "disk3";
	//尋找相同name的空閒飛碟
        for(int i=0;i<free.Count;i++) if(free[i].name == name){
            disk_prefab = free[i].gameObject;
            free.Remove(free[i]);
            break;
        }
        //如果空閒列表中沒有,則重新例項化飛碟
        if(disk_prefab == null){
            if (name == "disk1") disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk1"), new Vector3(0, start_y, 0), Quaternion.identity);
            else if (name == "disk2") disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk2"), new Vector3(0, start_y, 0), Quaternion.identity);
            else disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk3"), new Vector3(0, start_y, 0), Quaternion.identity);
            
            //給新例項化的飛碟賦予其他屬性
            float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
            disk_prefab.GetComponent<Renderer>().material.color = disk_prefab.GetComponent<DiskData>().color;
            disk_prefab.GetComponent<DiskData>().direction = new Vector3(ran_x, start_y, 0);
            disk_prefab.transform.localScale = disk_prefab.GetComponent<DiskData>().scale;
        }
        //新增到使用列表中
        used.Add(disk_prefab.GetComponent<DiskData>());
        return disk_prefab;
    }

FreeDisk:回收飛碟。

 public void FreeDisk(GameObject disk){
        for(int i = 0;i < used.Count; i++) if (disk.GetInstanceID() == used[i].gameObject.GetInstanceID()){
            used[i].gameObject.SetActive(false);
            free.Add(used[i]);
            used.Remove(used[i]);
            break;
        }
    }
  • Singleton.cs
ublic class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    protected static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = (T)FindObjectOfType(typeof(T));
                if (instance == null)
                {
                    Debug.LogError("An instance of " + typeof(T)
                        + " is needed in the scene, but there is none.");
                }
            }
            return instance;
        }
    }
}
  • 記分員ScoreRecorder:start初始化分數為0,每擊落一個飛碟加上對應的分數,重新開始時重置分數。
public class ScoreRecorder : MonoBehaviour{
    public int score;                   //分數
    void Start (){
        score = 0;
    }
    //記錄分數
    public void Record(GameObject disk){
        int temp = disk.GetComponent<DiskData>().score;
        score = temp + score;
    }
    //重置分數
    public void Reset(){
        score = 0;
    }
}
  • FlyDisk:丟擲飛碟,繼承SSAction實現。
 public static UFOFlyAction GetSSAction(Vector3 direction, float angle, float power){
        //初始化物體將要運動的初速度向量
        UFOFlyAction action = CreateInstance<UFOFlyAction>();
        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;
        return action;
    }
    
    public override void Update(){
        //計算物體的向下的速度,v=at
        time += Time.fixedDeltaTime;
        gravity_vector.y = gravity * time;

	//位移模擬
        transform.position += (start_vector + gravity_vector) * Time.fixedDeltaTime;
        current_angle.z = Mathf.Atan((start_vector.y + gravity_vector.y) / start_vector.x) * Mathf.Rad2Deg;
        transform.eulerAngles = current_angle;
	
	//如果物體y座標小於-10,動作就做完了
        if (this.transform.position.y < -10){
            this.destroy = true;
            this.callback.SSActionEvent(this);      
        }
    }	

執行結果:
在這裡插入圖片描述
在這裡插入圖片描述
擊中爆炸效果:在這裡插入圖片描述
生命值為0時,遊戲結束:在這裡插入圖片描述

相關文章