unity打靶遊戲的製作

 這次第一個作業是一個打靶遊戲,要用到老師上課所講的物理運動,對於這次作業來說,總結一下就是剛體和碰撞的運用,先上一個效果成品圖。


如圖所示,射出去的箭插在了靶上,左上角顯示得分情況和風力大小,正值代表指向x軸正方向的風力,負值代表指向x軸負方向的風力。接下來我來講一下靶和箭的製作。

首先是靶,靶就是五個不同大小的非常薄的圓柱,將它們拼接在一起即可。一下是靶的參數:


讓五個圓柱按照一定的位置疊放好後,一個簡單的靶就做成了,老師上課也講了組合物理物體的製作,對於這個靶來說,就是將這個整體設置成剛體,並且把靶的重力給去掉,每個環不設置剛體,但爲每一個環添加一個網格碰撞起。

接下來就是箭的製作,箭也是組成體,分爲箭靶和箭頭兩個部分


接下來是箭的屬性


箭因爲和靶進行碰撞,所以組合體的屬性稍有不同,這裏我選擇把組合體設置爲剛體且爲其增加網格碰撞器,而箭靶和箭身不設置剛體,只增加一下各自的碰撞器。調整好攝像機的位置後,接下來就是寫腳本了。

這是我這次的uml類圖:


sceneController裏負責實例化各個類,射箭和設置風力等動作類統一放到GameModel裏來進行,因爲這次動作比較少,就不設置actionmanger了,統一放到遊戲模型裏來控制。ArrowFactory負責箭的生產和箭的回收,ScoreRecorder裏負責計分,並掌握計分規則。ArrowCollider只負責實現與箭碰撞相關的細節,並將其掛到預置中的箭上。因爲這次類的關係比較簡單,我這裏就不用藉口了。接下來我來詳細解釋一下各類代碼。

一:UserInterface

private sceneController _scene;
	public  Camera cam;
	public Text scoreText;
	public Text windText;

	// Use this for initialization
	void Start () {
		_scene = sceneController.getInstance ();
	}

	// Update is called once per frame
	void Update () {
		scoreText.text = "Score: " + _scene.getPoint().ToString();
		windText.text = "Wind: " + _scene.getWind ().ToString ();
		if (Input.GetMouseButtonDown (0)) {
			Ray ray = cam.ScreenPointToRay (Input.mousePosition);
			_scene.shootArrow (ray.direction);
		}
	}

核心代碼如上,這裏要記錄一下射線的方向,用來控制角度。

二:SceneController

namespace Com.mygame{
	public class sceneController : System.Object {
		private static sceneController _instance;
		private GameModel _gameModel;
		private ArrowFactory _arrowFactory;
		private ScoreRecorder _scoreRecorder;
		public static sceneController getInstance()  {
			if (_instance == null) {
				_instance = new sceneController ();
			}
			return _instance;
		}
		public void setGameModel(GameModel obj) {
			_gameModel = obj;
		}
		public GameModel getGameModel() {
			return _gameModel;
		}
		public void shootArrow(Vector3 dir) {
			_gameModel.shootArrow (dir);
		}
		public void setArrowFactory(ArrowFactory obj) {
			_arrowFactory = obj;
		}
		public ArrowFactory getArrowFactory() {
			return _arrowFactory;
		}
		public void setScoreRecorder(ScoreRecorder obj) {
			_scoreRecorder = obj;
		}
		public ScoreRecorder getScoreRecorder() {
			return _scoreRecorder;
		}
		public int getPoint() {
			return _scoreRecorder.getPoint ();
		}
		public float getWind() {
			return _gameModel.getWind ();
		}
	}
}

將其定義到命名空間裏,並在裏面實例化一個場記

三:GameModel

public class GameModel : MonoBehaviour {
	private sceneController scene;
	private float speed = 30f;
	private GameObject arrow;
	private float windForce = 0f;
	void Start() {
		scene = sceneController.getInstance ();
		scene.setGameModel (this);
	}
	public void shootArrow(Vector3 dir) {
		arrow = sceneController.getInstance ().getArrowFactory().getArrow();
		arrow.transform.position = new Vector3 (0, 0, -10);
		arrow.transform.up = dir;
		arrow.GetComponent<Rigidbody> ().velocity = dir * speed;
		setWindToArrow (arrow);
		windForce = Random.Range (-100, 100);
	}
	public void setWindToArrow(GameObject arrow) {
		arrow.GetComponent<Rigidbody> ().AddForce (new Vector3 (windForce, 0, 0), ForceMode.Force);
	}
	public float getWind() {
		return windForce;
	}
	void Update () {
		
	}
}

這裏將箭射出,併爲射出去的箭添加風力,射箭時,首先從工廠中獲得一支箭,爲其設置好transform屬性後,爲其添加剛體速度和併爲其添加風力。

四:ArrowFactory

sceneController scene;
	private List<GameObject> usedArrow = new List<GameObject>();
	private List<GameObject> unUsedArrow = new List<GameObject> ();
	private List<float> existTime = new List<float> (); 
	private float timeStart = 0;
	private GameObject arrow;
	public static float timeEmit = 4;
	void Awake () {
		arrow = Instantiate (Resources.Load ("Prefabs/arrow")) as GameObject;
		arrow.SetActive (false);
		scene = sceneController.getInstance ();
		scene.setArrowFactory (this);
	}
	public GameObject getArrow() {
		GameObject newArrow;
		if (unUsedArrow.Count == 0) {
			newArrow = GameObject.Instantiate (arrow) as GameObject;
			newArrow.SetActive (true);
		} else {
			newArrow = unUsedArrow [0];
			newArrow.SetActive (true);
			if (newArrow.GetComponent<Rigidbody> () == null) {
				newArrow.AddComponent<Rigidbody> ();
			}
			Component[] comp = newArrow.GetComponentsInChildren<CapsuleCollider> ();
			foreach (CapsuleCollider i in comp) {
				i.enabled = true;
			}
			newArrow.GetComponent<MeshCollider> ().isTrigger = true;
			unUsedArrow.RemoveAt (0);
		}
		usedArrow.Add (newArrow);
		existTime.Add (timeStart);
		return newArrow;
	}
	public void freeArrow() {
		for (int i = 0; i <  usedArrow.Count; i++) {
			GameObject temp;
			if (!usedArrow [i].activeInHierarchy) {
				temp = usedArrow [i];
				usedArrow.RemoveAt (i);
				unUsedArrow.Add (temp);
				existTime.RemoveAt (i);
				temp.GetComponent<ArrowCollider> ().setWhichTarget ();
			}
		}
	}
	void Update () {
		for (int i = 0; i < existTime.Count; i++) {
			existTime [i] += Time.deltaTime;
			if (existTime [i] >= timeEmit) {
				usedArrow [i].SetActive (false);
			}
		}
		freeArrow ();
	}

兩個List,一個代表未使用的箭,一個代表使用了的箭,還有一個List代表這個箭存在的時間,當這個箭插在箭靶上存在超過一定時間後,將其回收,回收的時候還有將另一個類中的一個變量,也就是setWhichTarget變量,也就是打中哪一個環變量,將這個變量置爲空,否則的話這個箭打中哪一個環的屬性還在,影響計分。注意這裏生產箭的時候,如果這個箭的剛體屬性爲空的話,要爲這個箭同時加上剛體屬性,因爲在接下來的碰撞器事件中,爲了使箭插在靶子上,在一碰撞時去掉了箭的剛體屬性。

五:ScoreRecorder

private sceneController scene;
	public int  point = 0;
	void Awake () {
		scene = sceneController.getInstance ();
		scene.setScoreRecorder (this);
	}
	public int getPoint() {
		return point;
	}
	public void countPoint(string s) {
		if (s == "loop1") {
			point += 10;
		}
		if (s == "loop2") {
			point += 8;
		}
		if (s == "loop3") {
			point += 6;
		}
		if (s == "loop4") {
			point += 4;
		}
		if (s == "loop5") {
			point += 2;
		}
	}

保留分數這個變量和一些簡單的計分規則。

六:ArrowCollider

這個類就是爲設置碰撞事件所用,當碰撞所觸發的時候,爲了使箭插在靶上,立即去掉箭的剛體屬性,同時記錄箭第一次觸碰到的環的名字,因爲靶使用圓柱疊加起來的,打到內環時可能會觸碰到不止一個環,因此這裏只保留第一次觸碰到的環的名字。觸碰發生後,同時將箭的isTRigger屬性置爲false,防止多次觸發。

	private string whichTarget;
	private sceneController scene;
	void OnTriggerEnter(Collider other) {
		if (whichTarget == "") {
			whichTarget = other.gameObject.name;
			Debug.Log (whichTarget);
		}
		Destroy (GetComponent<Rigidbody> ());
		Component[] comp = GetComponentsInChildren<CapsuleCollider> ();
		foreach (CapsuleCollider i in comp) {
			i.enabled = false;
		} 
		GetComponent<MeshCollider> ().isTrigger = false;
		scene.getScoreRecorder ().countPoint (whichTarget);
	}
	public void setWhichTarget() {
		whichTarget = "";
	}
	// Use this for initialization
	void Awake () {
		whichTarget = "";
		scene = sceneController.getInstance ();
	}

好啦,到這裏遊戲差不多基本就做完了,這個遊戲在掌握了老師課上講的物理學運動後,還是比較好做的。我的建議是先做預置,然後做UserInterFaceGameModel,先確保箭能夠發射,在箭能夠發射後,增加工廠和回收機制,最後加上計分規則和風力和碰撞器。








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章