博客,对程序员来说应该是个特殊、特别的词汇,尤其是对于俺----这只只知道默默的呆在教室的一角,偶尔45度望望天空,装装文艺的小程序猿,不只是为了把程序运行成功后的那份激动分享出来,更希望能在这茫茫人海中寻得一二位情深潭水、志趣相投的好友,举杯畅饮,起舞邀月,所谓梦想用在,踏歌长行嘛!一直有动笔写博客的冲动,可总是在一个令人欲哭无泪的懒惰面前,只道一句洗洗睡吧,热情便所剩无几了,最终还是迟迟未能落笔,最近接了个小小的项目,为了能更好的与小组内的伙伴间交流,最终下定决心,开敲!


    实现第三人称无锁定需要人物和摄像机的配合,人物脚本只需要完成移动,摄像机脚本负责摄像机的旋转、移动、距离的缩放、以及对人物旋转。

    下面是我的人物的脚本:只实现移动,用的是新版本的动画组件Animator

<pre name="code" class="csharp">using UnityEngine;
using System.Collections;

public class TEST : MonoBehaviour 
{
	public float mSpeed=1.5f;
	private Animator animator=null;
	private CharacterController chaCon;
	
	void Start () 
	{
		animator = GetComponent<Animator>();
		chaCon = GetComponent<CharacterController>();

	}

	void Update () 
	{
		Vector3 mDir = Vector3.zero;


		float v = Input.GetAxis("Vertical");
		float h = Input.GetAxis("Horizontal");

		animator.SetFloat ("valY", v, 0.25f, Time.deltaTime);
		animator.SetFloat ("valX", h, 0.25f, Time.deltaTime);


		if (v < -0.2) 
		{
			//transform.Translate (-Vector3.forward * Time.deltaTime * mSpeed);
			mDir=-Vector3.forward * Time.deltaTime * mSpeed;  
		}
		else if (v > 0.2)
		{
			mDir=Vector3.forward*Time.deltaTime*mSpeed;  
		}

		if (h< -0.2)
		{
			mDir = Vector3.left*Time.deltaTime*mSpeed;
		}
		else if (h > 0.2)
		{
			mDir = Vector3.right*Time.deltaTime*mSpeed;
		}
		//考虑重力因素  
	        mDir=transform.TransformDirection(mDir);  
		float y=mDir.y-Gravity *Time.deltaTime;  
		mDir=new Vector3(mDir.x,y,mDir.z);  
		chaCon.Move(mDir);
}
}
</pre><pre class="csharp" name="code">
接下来就是摄像机的代码了,相对于人物仅仅只实现移动,摄像机做的工作要复杂一点,总结来看,共有三点:
1.右键控制摄像机旋转,包括水平、竖直两个方向。
2.鼠标滑轮控制摄像机距人物的距离
3.控制角色的旋转
需要注意的是,旋转范围,水平无限制,竖直方向上比较合适的范围是5-45度,5度是为了不让摄像机透过水平地面,45度是为了防止摄像机移过人头
具体代码如下:
<pre name="code" class="csharp">using UnityEngine;
using System.Collections;

public class FreeView_Camera : MonoBehaviour {

	//目标
	public Transform target;

	//距离
	public float distance = 5f;

	//右键旋转控制部分
	//旋转速度
	private float speedX = 240;
	private float speedY = 120;
	//Y轴旋转的角度限制
	private float minLimitY = 5;
	private float maxLimitY = 45;
	//旋转角度
	private float mX = 0.0f;
	private float mY = 0.0f;

	//鼠标缩放控制部分
	//鼠标缩放距离最值
	private float maxDistance = 10;
	private float minDistance = 1.5f;
	//鼠标缩放速度
	private float zoomSpeed = 2f;

	//差值控制部分
	//是否启用差值计算
	public bool isNeedDamping = false;
	//差值速度
	public float dampingSpeed = 10f;

	private Quaternion mRotation;
	private Vector3 mPosition;

	void Start()
	{
		//初始化旋转角度
		mX = transform.eulerAngles.x;
		mY = transform.eulerAngles.y;
	}

	void LateUpdate()
	{
		//第一步(功能一):右键控制相机旋转部分
		if (target != null && Input.GetMouseButton (1)) 
		{
			//1.获取鼠标输入
			mX += Input.GetAxis("Mouse X")*speedX*0.02f;
			mY -= Input.GetAxis("Mouse Y")*speedY*0.02f;

			//1.1Y轴角度范围限制
			//mY = ClampAngle(mY,minLimitY,maxLimitY);
			mY = Mathf.Clamp(mY,minLimitY,maxLimitY);

			//1.2计算旋转,转化成欧拉角
			mRotation = Quaternion.Euler(mY,mX,0);

			//2.旋转相机
			//根据是否差值采取不同的角度计算方式,角度旋转的时候不用差值更自然
			//if(isNeedDamping)
				//transform.rotation = Quaternion.Lerp(transform.rotation,mRotation,Time.deltaTime*dampingSpeed);
			//else 
				transform.rotation = mRotation;

			//3.对目标物体的状态限定。不是所有的状态都可以旋转的,比如轻功时、战斗时就不可以旋转(去除物体本身旋转的可能)
			target.rotation = Quaternion.Euler(new Vector3(0,mX,0));  //注意,这里mx中的X是针对鼠标的,实际上,在X平面上是绕着Y轴旋转
		}

		//第二步(功能二):鼠标滚轮缩放部分控制
		distance -= Input.GetAxis ("Mouse ScrollWheel") * zoomSpeed;
		distance = Mathf.Clamp (distance, minDistance, maxDistance);

		//第三步(功能三):计算相机位置并进行设定
		//这里出了个小问题,计算位置的时候,Y轴还是先别动了
		mPosition = mRotation * new Vector3 (0.0f, 0.0f, -distance) + target.position;
		if (isNeedDamping) 
		{
			transform.position = Vector3.Lerp (transform.position, mPosition, Time.deltaTime * dampingSpeed);
		} 
		else 
		{
			transform.position = mPosition;
		}

	}
	/*private float ClampAngle(float angle,float min,float max)
	{
		if (angle < 5)
						angle += 5;
		if (angle > 180)
						angle -= 90;
		return Mathf.Clamp (angle, min, max);//数值限制
	}*/
}


/*Unity中Update和Lateupdate的区别。Lateupdate和Update每一祯都被执行,但是执行顺序不一样,先执行Updatee然后执行lateUpdate。

如果你有两个脚本JS1、JS2,两个脚本中都有Update()函数, 在JS1中有 lateUpdate ,JS2中没有。那么 lateUpdate 函数会等待JS1、JS2两个脚本的Update()函数 都执行完后才执行。也就是说, 如果现在有100个脚本,分别有100个 Update()函数,其中只有一个LateUpdate,那么在同一祯中,等待100个Update()执行完后,才执行这一个lateUpdate()。

这就是Unity这款引擎精妙之处,系统帮我们封装了多线程。 想一想曾经的Java单击游戏。 游戏主线程都是需要我们自己开。如果想实现Update LateUpdate 都需要自己去实现。官方这样做无疑是为开发好。就这个问题而言。Unity后台主线程将 Update LateUpdate 制作成两个多线程先去执行Update的线程,等Update 执行完毕后 在去执行LateUpdate线程。
*/