這次第一個作業是一個打靶遊戲,要用到老師上課所講的物理運動,對於這次作業來說,總結一下就是剛體和碰撞的運用,先上一個效果成品圖。
如圖所示,射出去的箭插在了靶上,左上角顯示得分情況和風力大小,正值代表指向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 ();
}
好啦,到這裏遊戲差不多基本就做完了,這個遊戲在掌握了老師課上講的物理學運動後,還是比較好做的。我的建議是先做預置,然後做UserInterFace和GameModel,先確保箭能夠發射,在箭能夠發射後,增加工廠和回收機制,最後加上計分規則和風力和碰撞器。