3d遊戲第三次作業

直言不會發表於2020-10-05

1、簡答並用程式驗證【建議做】

遊戲物件運動的本質是什麼?

是遊戲物體隨著時間的變化而引起空間的變化。具體來說就是當重新整理幀的時候,遊戲物體transorm引數變化,具體包括position和rotation的變化。

請用三種方法以上方法,實現物體的拋物線運動。(如,修改Transform屬性,使用向量Vector3的方法…)

拋物線運動是水平勻速,豎直方向加速。第一種方法可以直接在update修改每一幀的時候直接更新transform的屬性:


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{
    private float speedx = 5;
    private float speedy = 0;
    private float g = 10;
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {      
        transform.position += Vector3.right * speedx * Time.deltaTime;
        transform.position += Vector3.down * speedy * Time.deltaTime;
        speedy += g * Time.deltaTime;
    }
}

也可以使用Vector3來間接修改(在上面程式碼基礎上修改如下程式碼段):

void Update()
    {
        speedy += g * Time.deltaTime;
        Vector3 temp = new Vector3(speedx * Time.deltaTime, -speedy * Time.deltaTime, 0);
        transform.position += temp;
    }

還可以修改物體的重力屬性來實現:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{
    void Start()
    {
        gameObject.AddComponent<Rigidbody>();
        gameObject.GetComponent<Rigidbody>().velocity = new Vector3(1, 0, 0);
    }

    void Update() { }
}

寫一個程式,實現一個完整的太陽系, 其他星球圍繞太陽的轉速必須不一樣,且不在一個法平面上。

建立太陽系:
create solarSystem
指令碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class solarSystem : MonoBehaviour
{
    public Transform sun;
    public Transform mercury;
    public Transform venus;
    public Transform earth;
    public Transform mars;
    public Transform jupiter;
    public Transform saturn;
    public Transform uranus;
    public Transform neptune;


    void Start() { }

    void Update()
    {

        sun.Rotate(Vector3.up,  Time.deltaTime * 10);

        mercury.RotateAround(this.transform.position, new Vector3(1, 10, 0), 50 * Time.deltaTime);
        mercury.Rotate(Vector3.up,  Time.deltaTime * 100);

        venus.RotateAround(this.transform.position, new Vector3(1, 13, 0), 40 * Time.deltaTime);
        venus.Rotate(Vector3.up,  Time.deltaTime * 100);

        earth.RotateAround(this.transform.position, new Vector3(1, 15, 0), 35 * Time.deltaTime);
        earth.Rotate(Vector3.up,  Time.deltaTime * 100);

        mars.RotateAround(this.transform.position, new Vector3(2, 10, 0), 30 * Time.deltaTime);
        mars.Rotate(Vector3.up,  Time.deltaTime * 100);

        jupiter.RotateAround(this.transform.position, new Vector3(2, 13, 0), 20 * Time.deltaTime);
        jupiter.Rotate(Vector3.up,  Time.deltaTime * 100);

        saturn.RotateAround(this.transform.position, new Vector3(2, 15, 0), 15 * Time.deltaTime);
        saturn.Rotate(Vector3.up,  Time.deltaTime * 100);

        uranus.RotateAround(this.transform.position, new Vector3(3, 10, 0), 10 * Time.deltaTime);
        uranus.Rotate(Vector3.up,  Time.deltaTime * 100);

        neptune.RotateAround(this.transform.position, new Vector3(3, 15, 0), 9 * Time.deltaTime);
        neptune.Rotate(Vector3.up,  Time.deltaTime * 100);
    }
}

執行效果

2、程式設計實踐

閱讀以下游戲指令碼

Priests and Devils
Priests
and Devils is a puzzle game in which you will help the Priests and Devils to cross the river within the time limit. There are 3 priests and 3 devils at one side of the river. They all want to get to the other side of this river, but there is only one boat and this boat can only carry two persons each time. And there must be one person steering the boat from one side to the other side. In the flash game, you can click on them to move them and click the go button to move the boat to the other direction. If the priests are out numbered by the devils on either side of the river, they get killed and the game is over. You can try it in many > ways. Keep all priests alive! Good luck!

程式需要滿足的要求:

  • play the game ( http://www.flash-game.net/game/2535/priests-and-devils.html )
  • 列出遊戲中提及的事物(Objects)
  • 用表格列出玩家動作表(規則表),注意,動作越少越好
  • 請將遊戲中物件做成預製
  • 在場景控制器 LoadResources 方法中載入並初始化 長方形、正方形、球 及其色彩代表遊戲中的物件。
  • 使用 C# 集合型別 有效組織物件
  • 整個遊戲僅 主攝像機 和 一個 Empty 物件, 其他物件必須程式碼動態生成!!! 。 整個遊戲不許出現 Find 遊戲物件, SendMessage 這類突破程式結構的 通訊耦合 語句。 違背本條準則,不給分
  • 請使用課件架構圖程式設計,不接受非 MVC 結構程式
  • 注意細節,例如:船未靠岸,牧師與魔鬼上下船運動中,均不能接受使用者事件!

遊戲中的物件:牧師、魔鬼、小船、河岸、河水
玩家動作表(規則表)

動作條件結果
點選岸上的牧師或者魔鬼船未滿並且在岸邊點選的牧師或者魔鬼上船
點選小船船上有牧師或者魔鬼移動到另一岸
點選船上的牧師或魔鬼船在岸邊牧師或者魔鬼上岸

建立預製,將牧師做成淺黃方塊,惡魔做成深紅方塊
預製
見上圖,資源被整合成材料、預製和指令碼三個資料夾。

  • 執行前的遊戲物件
    執行前
  • 執行後的遊戲物件
    在這裡插入圖片描述
    使用MVC架構:
  • 模型(Model):資料物件及關係
    所有的GameObject
  • 控制器(Controller):接受使用者事件,控制模型的變化
    Director類,FirstController類,Move類,RolesController類,CoastController類,BoatController類
  • 介面(View):顯示模型,將人機互動事件交給控制器處理
    UserGUI和ClickGUI指令碼

主要的一些類的解釋:

Director

獲取當前遊戲的場景
控制場景執行、切換、入棧與出棧
暫停、恢復、退出
管理遊戲全域性狀態
設定遊戲的配置
設定遊戲全域性檢視

	public class Director : System.Object {
		private static Director _instance;
		public SceneController currentSceneController { get; set; }

		public static Director getInstance() {
			if (_instance == null) {
				_instance = new Director ();
			}
			return _instance;
		}
	}

可以看到無論何時呼叫getInstance()都會得到同一個例項,這保證了導演類的唯一性,也能保證導演類控制場記的作用。

四個控制類

Moveable
 控制遊戲物體的移動
RolesController
 控制牧師或者魔鬼
CoastController
 控制河岸
BoatController
 控制小船

	public class Moveable: MonoBehaviour {
		
		readonly float move_speed = 30;

		int moving_status;	// 0->not moving, 1->moving end middle, 2->moving end dest
		Vector3 dest;
		Vector3 middle;

		void Update() {
			if (moving_status == 1) {
				transform.position = Vector3.MoveTowards (transform.position, middle, move_speed * Time.deltaTime);
				if (transform.position == middle) {
					moving_status = 2;
				}
			} else if (moving_status == 2) {
				transform.position = Vector3.MoveTowards (transform.position, dest, move_speed * Time.deltaTime);
				if (transform.position == dest) {
					moving_status = 0;
				}
			}
		}
		public void setDestination(Vector3 _dest) {
			dest = _dest;
			middle = _dest;
			if (_dest.y == transform.position.y)
				moving_status = 2;
		
			else if (_dest.y < transform.position.y) 
				middle.y = transform.position.y;
			else 						
				middle.x = transform.position.x;
			
			moving_status = 1;
		}

		public void reset() {
			moving_status = 0;
		}
	}


	public class RolesController {
		readonly GameObject character;
		readonly Moveable moveableScript;
		readonly ClickGUI clickGUI;
		readonly int RoleType;	// 0->priest, 1->devil

		// change frequently
		bool _isOnBoat;
		CoastController coastController;


		public RolesController(string which_character) {
			
			if (which_character == "priest") {
				character = Object.Instantiate (Resources.Load ("Perfabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
				RoleType = 0;
			} else {
				character = Object.Instantiate (Resources.Load ("Perfabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
				RoleType = 1;
			}
			moveableScript = character.AddComponent (typeof(Moveable)) as Moveable;

			clickGUI = character.AddComponent (typeof(ClickGUI)) as ClickGUI;
			clickGUI.setController (this);
		}

		public void setName(string name) {
			character.name = name;
		}

		public void setPosition(Vector3 pos) {
			character.transform.position = pos;
		}

		public void moveToPosition(Vector3 destination) {
			moveableScript.setDestination(destination);
		}

		public int getType() {	// 0->priest, 1->devil
			return RoleType;
		}

		public string getName() {
			return character.name;
		}

		public void getOnBoat(BoatController boatCtrl) {
			coastController = null;
			character.transform.parent = boatCtrl.getGameobj().transform;
			_isOnBoat = true;
		}

		public void getOnCoast(CoastController coastCtrl) {
			coastController = coastCtrl;
			character.transform.parent = null;
			_isOnBoat = false;
		}

		public bool isOnBoat() {
			return _isOnBoat;
		}

		public CoastController getCoastController() {
			return coastController;
		}

		public void reset() {
			moveableScript.reset ();
			coastController = (Director.getInstance ().currentSceneController as FirstController).bankbegin;
			getOnCoast (coastController);
			setPosition (coastController.getEmptyPosition ());
			coastController.getOnCoast (this);
		}
	}


	public class CoastController {
		readonly GameObject coast;
		readonly Vector3 from_pos = new Vector3(9,1,0);
		readonly Vector3 to_pos = new Vector3(-9,1,0);
		readonly Vector3[] positions;
		readonly int postion;	// end->-1, begin->1

		// change frequently
		RolesController[] passengerPlaner;

		public CoastController(string _postion) {
			positions = new Vector3[] {
                new Vector3(6.5F,2.25F,0), 
                new Vector3(7.5F,2.25F,0), 
                new Vector3(8.5F,2.25F,0), 
				new Vector3(9.5F,2.25F,0), 
                new Vector3(10.5F,2.25F,0), 
                new Vector3(11.5F,2.25F,0)};

			passengerPlaner = new RolesController[6];

			if (_postion == "begin") {
				coast = Object.Instantiate (Resources.Load ("Perfabs/Bank", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;
				coast.name = "begin";
				postion = 1;
			} else {
				coast = Object.Instantiate (Resources.Load ("Perfabs/Bank", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;
				coast.name = "end";
				postion = -1;
			}
		}

		public int getEmptyIndex() {
			for (int i = 0; i < passengerPlaner.Length; i++) 
				if (passengerPlaner [i] == null) 
					return i;
			return -1;
		}

		public Vector3 getEmptyPosition() {
			Vector3 pos = positions [getEmptyIndex ()];
			pos.x *= postion;
			return pos;
		}

		public void getOnCoast(RolesController characterCtrl) {
			int index = getEmptyIndex ();
			passengerPlaner [index] = characterCtrl;
		}

		public RolesController getOffCoast(string passenger_name) {	// 0->priest, 1->devil
			for (int i = 0; i < passengerPlaner.Length; i++) 
				if (passengerPlaner [i] != null && passengerPlaner [i].getName () == passenger_name) {
					RolesController charactorCtrl = passengerPlaner [i];
					passengerPlaner [i] = null;
					return charactorCtrl;
				}
			
			Debug.Log ("cant find passenger on coast: " + passenger_name);
			return null;
		}

		public int where() {
			return postion;
		}

		public int[] getRoleNum() {
			int[] count = {0, 0};
			for (int i = 0; i < passengerPlaner.Length; i++) {
				if (passengerPlaner [i] == null)
					continue;
				if (passengerPlaner [i].getType () == 0) 	// 0->priest, 1->devil
					count[0]++;
				else 
					count[1]++;
				
			}
			return count;
		}

		public void reset() {
			passengerPlaner = new RolesController[6];
		}
	}


	public class BoatController {
		readonly GameObject boat;
		readonly Moveable moveableScript;
		readonly Vector3 fromPosition = new Vector3 (5, 1, 0);
		readonly Vector3 toPosition = new Vector3 (-5, 1, 0);
		readonly Vector3[] beginPostion;
		readonly Vector3[] endPostion;

		// change frequently
		int postion; // end->-1; begin->1
		RolesController[] passenger = new RolesController[2];

		public BoatController() {
			postion = 1;

			beginPostion = new Vector3[] { new Vector3 (4.5F, 1.5F, 0), new Vector3 (5.5F, 1.5F, 0) };
			endPostion = new Vector3[] { new Vector3 (-5.5F, 1.5F, 0), new Vector3 (-4.5F, 1.5F, 0) };

			boat = Object.Instantiate (Resources.Load ("Perfabs/Boat", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;
			boat.name = "boat";

			moveableScript = boat.AddComponent (typeof(Moveable)) as Moveable;
			boat.AddComponent (typeof(ClickGUI));
		}


		public void Move() {
			if (postion == -1) {
				moveableScript.setDestination(fromPosition);
				postion = 1;
			} else {
				moveableScript.setDestination(toPosition);
				postion = -1;
			}
		}

		public int getEmptyIndex() {
			for (int i = 0; i < passenger.Length; i++) 
				if (passenger [i] == null) 
					return i;
			return -1;
		}

		public bool isEmpty() {
			for (int i = 0; i < passenger.Length; i++) 
				if (passenger [i] != null) 
					return false;
			return true;
		}

		public Vector3 getEmptyPosition() {
			Vector3 pos;
			int emptyIndex = getEmptyIndex ();
			if (postion == -1) 
				pos = endPostion[emptyIndex];
			else 
				pos = beginPostion[emptyIndex];
			return pos;
		}

		public void GetOnBoat(RolesController characterCtrl) {
			int index = getEmptyIndex ();
			passenger [index] = characterCtrl;
		}

		public RolesController GetOffBoat(string passenger_name) {
			for (int i = 0; i < passenger.Length; i++)
				if (passenger [i] != null && passenger [i].getName () == passenger_name) {
					RolesController charactorCtrl = passenger [i];
					passenger [i] = null;
					return charactorCtrl;
				}
			
			Debug.Log ("Cant find passenger in boat: " + passenger_name);
			return null;
		}

		public GameObject getGameobj() {
			return boat;
		}

		public int where() { // end->-1; begin->1
			return postion;
		}

		public int[] getRoleNum() {
			int[] count = {0, 0};
			for (int i = 0; i < passenger.Length; i++) {
				if (passenger [i] == null)
					continue;
				if (passenger [i].getType () == 0)	// 0->priest, 1->devil
					count[0]++;
				else
					count[1]++;
			}
			return count;
		}

		public void reset() {
			moveableScript.reset ();
			if (postion == -1)
				Move ();
			passenger = new RolesController[2];
		}
	}
}

ClickGUI

相應玩家的點選,將該動作傳給相應類實現操作

public class ClickGUI : MonoBehaviour {
	UserAction action;
	RolesController characterController;

	public void setController(RolesController rolectrl) {
		characterController = rolectrl;
	}

	void Start() {
		action = Director.getInstance ().currentSceneController as UserAction;
	}

	void OnMouseDown() {
		if (gameObject.name == "boat") {
			action.moveBoat ();
		} else {
			action.characterIsClicked (characterController);
		}
	}
}

執行效果
原始碼

執行遊戲的方法1:
下載asset替換自己新建的專案,並將兩個GUI指令碼掛載到空物件中,將firstconctroller指令碼掛載到攝影機上。

執行遊戲的方法2:
直接開啟asset中的priestAndDevil.unity。

3、思考題【選做】

使用向量與變換,實現並擴充套件 Tranform 提供的方法,如 Rotate、RotateAround 等

對於i、j、k本身的幾何意義可以理解為一種旋轉,其中i旋轉代表X軸與Y軸相交平面中X軸正向向Y軸正向的旋轉,j旋轉代表Z軸與X軸相交平面中Z軸正向向X軸正向的旋轉,k旋轉代表Y軸與Z軸相交平面中Y軸正向向Z軸正向的旋轉,見下圖:
四元數的旋轉
靜態方法 Quaternion.Euler(x,y,z),直觀上按 z、x、y 軸旋轉的一個序列。因為旋轉矩陣連乘得到複合旋轉,故 q = qy * qx * qz
Rotation

public void Rotate(Transform trans,float anglex, float angley, float anglez){
    Quaternion p1 = Quaternion.AngleAxis(anglez, Vector3.forward);
    Quaternion p2 = Quaternion.AngleAxis(anglex, Vector3.right);
    Quaternion p3 = Quaternion.AngleAxis(angley, Vector3.up);
    trans.rotation *= p3 * p2 * p1;
}

RotateAround
圍繞通過世界座標中點的軸旋轉變換角度

public void RotateAround(Transform trans,Vector3 point, Vector3 axis, float angle){
   	Quaternion p = Quaternion.AngleAxis(angle,axis);
	Vector3 distance = myTransform.position - point;
  	trans.position = p * myTransform.position;
  	trans.rotation *= p;  
}

相關文章