博客,对程序员来说应该是个特殊、特别的词汇,尤其是对于俺----这只只知道默默的呆在教室的一角,偶尔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线程。
*/