Unity3D下 仙劍奇俠傳三 人物移動及視角控制的嘗試實現及補充

一直很喜歡仙劍系列遊戲,覺得仙劍的人物控制也很順手,學習了Unity後就很想做一個類似於仙劍的人物控制方案。

又特地重玩了仙劍三,現將仙劍三的人物控制操作羅列如下:


鍵盤A:面向Y軸的順時針旋轉鏡頭,觀察人物;(我這裏的面向Y軸,就是指的從上向下看)

鍵盤D:面向Y軸的逆時針旋轉鏡頭,觀察人物;

鍵盤↑、↓、←、→:前後左右移動人物(需要注意的是:這裏的前後左右是相對於鏡頭當前朝向方向的前後左右,其實就是在鏡頭所在的座標軸下進行移動)

雙擊上述按鍵:跑

鼠標滾軸順時針滾動(從鼠標右側觀察的順時針):效果等同鍵盤A;

鼠標滾軸逆時針滾動(從鼠標右側觀察的逆時針):效果等同鍵盤D;

鼠標左鍵單擊:地圖選點人物向選點走動;

鼠標左鍵雙擊:地圖選點人物向選點跑動;


我對於以上操作做了如下改動:

1.定義W/D來控制鏡頭向下和鏡頭向上轉;

2.用Shitf來代替鍵盤雙擊與鼠標雙擊進行的跑動控制

3.代碼中只考慮人物在X-Z軸平面上的移動,也就是說,人物只會繞着Y軸進行旋轉

以下是兩部分的代碼,所有需要注意的內容都在註釋中也明瞭。

綁定到mainCamera上的代碼:

using UnityEngine;
using System.Collections;

public class mainCameraForXianjian : MonoBehaviour {
	//遊戲對象-主角色
	private GameObject target = null;
	//鏡頭距離主角的距離
	public float distance = 2.0f;
	//縱向最大觀察角度(相當於限定了俯視的最大角度)
	private float maxAngleInXAxis = 80;
	//縱向最小觀察角度(相當於限定了仰視的最大角度)
	private float minAngleInXAxis = -20;
	//橫向鏡頭角度
	private float x = 0;
	//縱向鏡頭角度
	private float y = 0;
	// Use this for initialization
	void Start () {
		//查找主角
		target = GameObject.Find ("rule");
		if (target == null) {
			return;
		}
		//初始化鏡頭角度
		x = target.transform.eulerAngles.y;
		y = target.transform.eulerAngles.x;
		
	}

	// Update is called once per frame
	void LateUpdate () {
		if (target == null) {
			return;
		}
		//定義:Ctrl + 鼠標滾軸 爲調整鏡頭距主角距離(從鼠標右側觀察鼠標,滾軸順時針拉近鏡頭,滾軸逆時針拉遠鏡頭)
		if (Input.GetKey (KeyCode.LeftControl) || Input.GetKey (KeyCode.RightControl)) {
			distance -= Input.GetAxis ("Mouse ScrollWheel") * 2.0f;//從鼠標右側觀察鼠標,滾軸順時針值爲正,逆時針值爲負,之所以“-=”就是想順時針拉近鏡頭
		}

		//x -= Input.GetAxis ("Mouse X") * 4.0f;
		//y += Input.GetAxis ("Mouse Y") * 1.5f;
		//定義:DirectikonHorizontal爲方向橫向輸入量,W/A/S/D分別代表鏡頭繞着角色看角色上面、左邊、下面、右邊
		x -= Input.GetAxis ("DirectionHorizontal");
		y -= Input.GetAxis ("DirectionVertical");
		//定義:鼠標滾軸,爲從鼠標右側觀察鼠標,滾軸順時針繞着角色觀察角色左邊
		if (!Input.GetKey (KeyCode.LeftControl) && !Input.GetKey (KeyCode.RightControl)) {
			x += Input.GetAxis ("Mouse ScrollWheel") * 10;
		}

		//限定縱向觀察角色的視覺範圍
		y = ClaimValue(y, maxAngleInXAxis, minAngleInXAxis);
		Quaternion rotation = Quaternion.Euler(new Vector3 (y, x, 0));
		//計算鏡頭最新位置
		Vector3 position = Quaternion.Euler (y, x, 0) * (new Vector3 (0, 0, -distance)) + target.transform.position;
		gameObject.transform.rotation = rotation;
		gameObject.transform.position = position;
	}
	float ClaimValue(float value, float maxValue, float minValue)
	{
		if (value < -360) {
			value += 360;
		}
		if (value > 360) {
			value -= 360;
		}
		return Mathf.Clamp (value, minValue, maxValue);
	}
}


綁定在主角上的代碼:

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {
	//角色移動方向
	public const int HERO_UP = 0;
	public const int HERO_RIGHT = 1;
	public const int HERO_DOWN = 2;
	public const int HERO_LEFT = 3;

	public int state = 0;
	//角色跑動時的速度
	public int runSpeed = 15;
	//角色走動時的速度
	public int moveSpeed = 10;
	//當前角色移動速度(默認爲走動)
	private int curSpeed = 10;
	//當前角色動畫名稱
	private string animState = "walk";
	//當前角色是否在進行鼠標點選移動
	private bool bMouseMove = false;
	//鼠標點選的目標位置
	private Vector3 targetPos = Vector3.zero;
	public void Awake()
	{
		state = HERO_UP;
	}
	// Use this for initialization
	void Start () {
	
	}

	// Update is called once per frame
	void Update () {

		//定義:Shift 代表移動模式爲run
		if (Input.GetKey (KeyCode.LeftShift) || Input.GetKey (KeyCode.RightShift)) {
			animState = "run";
			curSpeed = runSpeed;
		} else {
			animState = "walk";	
			curSpeed = moveSpeed;
		}

		/*******************************************************************************************/
		/**********************************鼠標點選移動模式*******************************************/
		//鼠標左鍵:角色向鼠標指向位置移動
		if (Input.GetMouseButton(0)) {
		
			Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
			RaycastHit hit;
			if (Physics.Raycast (ray, out hit)) {
				//設置當前爲鼠標點選移動模式
				bMouseMove = true;
				targetPos = new Vector3 (hit.point.x, gameObject.transform.position.y, hit.point.z);
				//調整角色面向目的地
				gameObject.transform.LookAt (targetPos);
				
			}
		}
		//在鼠標點選移動模式下,角色不停地向目的地前進
		if (bMouseMove) {	
			//播放動畫
			gameObject.GetComponent<Animation> ().Play (animState);
			//如果角色臨界目的地,則移動角色到目的地,並停止移動
			if ((Vector3.Distance (gameObject.transform.position, targetPos)) <= curSpeed * Time.deltaTime) { //Vector3.Distance(gameObject.transform.position, targetPos) < (float)curSpeed
				gameObject.transform.position = targetPos;
				bMouseMove = false;
			} else {
				//由於人物方向已經確定好了,所以以自身座標系向目的地前進
				gameObject.transform.Translate (Vector3.forward * curSpeed * Time.deltaTime, Space.Self);
			}
		}

		/*******************************************************************************************/
		/**********************************鍵盤移動模式**********************************************/

		//定義:MoveVertical爲UP/DOWN鍵;MoveHorizontal爲RIGHT/LEFT鍵
		float KeyVertical = Input.GetAxis ("MoveVertical");
		float KeyHorizontal = Input.GetAxis ("MoveHorizontal");

		if (KeyVertical == 1) {
			setHeroState (HERO_DOWN);
		} else if (KeyVertical == -1) {
			setHeroState(HERO_UP);
		}
		if (KeyHorizontal == 1) {
			setHeroState (HERO_RIGHT);
		} else if (KeyHorizontal == -1) {
			setHeroState(HERO_LEFT);
		}

		if ((KeyVertical == 0 && KeyHorizontal == 0) && !bMouseMove) {
			gameObject.GetComponent<Animation>().Play ();
		}
	}
	//控制角色移動一次
	public void setHeroState(int newState)
	{
		bMouseMove = false;
		//gameObject.transform.rotation = Quaternion.Euler(0, Camera.main.transform.eulerAngles.y, 0) ;
		gameObject.transform.rotation = Quaternion.Euler(0, Camera.main.transform.eulerAngles.y + (int)newState * 90, 0) ;
		int rotateValue = 0;
		Vector3 transformValue = new Vector3 ();

		gameObject.GetComponent<Animation> ().Play (animState);

		//獲取當前鏡頭的歐拉角
		Vector3 newVector3 = Camera.main.transform.rotation.eulerAngles;
		//將歐拉角的x/z置空,因爲這裏只考慮角色在Y軸上的旋轉
		newVector3.x = 0;
		newVector3.z = 0;
		//將角色旋轉的歐拉角轉換爲四元數,以便對目標向量進行旋轉
		Quaternion quter = Quaternion.Euler (newVector3);


		switch (newState) {
		case HERO_UP:
			//quter乘以Vector3.forward的目的是:將角色行進方向旋轉到鏡頭的方向
			transformValue = quter * Vector3.forward * Time.deltaTime;
			//rotateValue = 0;
			break;
		case HERO_DOWN:
			transformValue = quter * (-Vector3.forward) * Time.deltaTime;
			//rotateValue = 180;
			break;
		case HERO_LEFT:
			transformValue = quter * Vector3.left * Time.deltaTime;
			//rotateValue = 270;
			break;
		case HERO_RIGHT:
			transformValue = quter * (-Vector3.left) * Time.deltaTime;
			//rotateValue = 90;
			break;
		}
		/* 我當是本來想用以下這部分代碼的,但是由於通過Camera.main.transform.TransformDirection得到的方向中還可能含有沿X/Z軸的旋轉量還需要處理,比較麻煩
		 * 簡單來說,舉個例子:如果鏡頭是在X軸上有個30度的旋轉量,那麼當控制人物向前走的時候,人物就會向前下方30°走
		switch (newState) {
		case HERO_UP:
			//quter乘以Vector3.forward的目的是:將角色行進方向旋轉到鏡頭的方向
			transformValue = Camera.main.transform.TransformDirection(Vector3.forward) *  Time.deltaTime;
			//rotateValue = 0;
			break;
		case HERO_DOWN:
			transformValue = Camera.main.transform.TransformDirection(-Vector3.forward) *  Time.deltaTime;
			//rotateValue = 180;
			break;
		case HERO_LEFT:
			transformValue = Camera.main.transform.TransformDirection(Vector3.left) *  Time.deltaTime;
			//rotateValue = 270;
			break;
		case HERO_RIGHT:
			transformValue = Camera.main.transform.TransformDirection(-Vector3.left) *  Time.deltaTime;
			//rotateValue = 90;
			break;
		}
		*/
		//gameObject.GetComponent<Transform> ().Rotate (Vector3.up, rotateValue);
		//將角色在指定方向在世界座標下位移
		gameObject.GetComponent<Transform> ().Translate (transformValue * curSpeed, Space.World);
		state = newState;
	}
}



發佈了61 篇原創文章 · 獲贊 34 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章