好久没有敲Blog了,谢谢大家的留言、关注、私信等支持,但是我好像已经没有办法让自己继续写以前的博客系列了,因为我发现网上关于unity3D的内容太少了,所以我无法自拔地想写U3D相关的文章!!!
第三人称视角
第三人称视角是什么?很简单,CS就是一种第一人称视角游戏,玩家没有办法看到自己的角色形象,只能观察除开自己之外的游戏内容。第三人称视角那么就明显是能够看到玩家所控制的角色的一种视角。
而且大部分游戏的镜头不能固定不动,肯定是要跟随猪脚,能看到猪脚,但是保持一定的高度和距离,这样的视角才是最好的。
U3D中使用镜头来呈现游戏画面,一般来说是Main Camera。官方有一个使用JS书写的 第三人称镜头跟随脚本和猪脚控制脚本,但是鉴于U3D的js语法太过于诡异,于是我想自己去搞明白怎样来写一个自己用起来爽的第三人称镜头跟随脚本。
==去机场接个人,回来再继续写==
OK,下面分步骤完成这个脚本。关于添加猪脚的gameObject 以及添加CharacterController Component这里就不做解释了,前提是猪脚能够在场景中前后左右上下移动并且不会穿越障碍物然后我们继续往下写。
也就是我们的摄像机现在是固定视角,猪脚可以随意移动和跳跃,如图:
1.创建一个脚本文件CameraFollow.cs
2.接下来,分析我们最想要的效果:
a,摄像机保持和猪脚一定的高度差(y轴固定差值)和
b.距离差(x,z轴向量的合量相对固定);
c,面向猪脚的正前方
先完成目的a:
//摄像机固定在猪脚上方10米高度
public float camera_height=10.0f;
//摄像机离猪脚大概10米的水平距离
public float camera_distance=10.0f;
//摄像机和猪脚的transform属性
private Transform player;
private Transform camera;
// Use this for initialization
void Start () {
//初始化
player=GameObject.FindGameObjectWithTag("Player").transform;
camera = Camera.main.transform;
}
// Update is called once per frame
void Update () {
//每一帧都改变摄像机的高度
camera.position = new Vector3 (player.position.x, player.position.y+ camera_height, player.position.z );
}
效果:
我们在脚本中将高度距离两个变量暴露为public,可以在Inspector中修改,先改一个大概满意的高度
目前我们看不到猪脚,原因是摄像机的位置是取的猪脚的X,Z ,只是Y轴加上固定高度。
所以我们需要计算摄像机的X,Z坐标与猪脚的X,Z坐标是一个什么样的关系?
如果单纯的 取猪脚的X,Z坐标然后分别减去distance,是不行的,这样我们只有在某一个方向才能正好在猪脚后方
修改update中的赋值语句为:
那么我们运行游戏,选择到mainCamera,修改他的Y轴旋转度,直到镜头正好时刻在猪脚后方,发现这个角度一直是45°:
所以不难明白,当且仅当摄像机的Y轴偏离45°的时候,x,z各取地平面某一点的值,摄像机的位置在这个x,z坐标基础上减去一个相同的值,摄像机可以正好看到这个点在正中间。
这是为什么?我来画个图示意一下:
忽略掉摄像机的高度差,我们的猪脚在点O(α,0,α)
摄像机在O一撇(0,0,0)
要让摄像机正好看到猪脚在正中间且保持一定距离,那么本来摄像机的正前方是这个坐标系的Z轴正方向,现在改为朝射线O' O方向,那么偏离量很明显由于是个正方形的对角线,(这是透视下的正方形),角度为45°。
问题来了,我们难道要一直保持45°去望着猪脚吗,别忘了我们的摄像机随时要旋转保持正对猪脚的,那么这个位置到底该是多少呢?
我们 先把C完成:随时保证面向猪脚的正前方:
添加这两行代码之后,我们再看看问题出在哪儿:
很明显,摄像机的朝向是与猪脚的正前方朝向一致的,但是位置呢,当切仅当猪脚Y轴旋转度偏离Z轴正方向45°的时候,位置在猪脚正后方,小于45°偏左,大于45°偏右,180°+45°直接跑到猪脚正前面去了。
所以说,我们想要的情况是:
45°时,x 与z各减去 distance
180°+45°=225°时,x与z各加上distance
0°的时候,x不变,z-distance/sin45° (为什么,因为实际上45°时候线段O'O的长度其实不是distance而是distance/sin45°,看我画的那个图,勾股定理)
90°的时候,z不变,x-distance/sin45°
所以其实我们这里的定义的distance参数名字和他代表的不一样,实际距离应该是distance/sin45°
所以不难归纳,我们以角度β为变量,得出x与β的函数关系为:
x-=distance*sinβ
z-=distance*cosβ
正好满足我们上面列的4个条件
由于我们代码中需要的三角函数参数是弧度而不是角度,所以这里把角度替换为弧度:
x-=distance*sin(β*π/180)
z-=distance*cos(β*π/180)
所以我们的代码应该修改为:
//看向猪脚
//camera.LookAt (player);
//与猪脚的正前方为正前方(只取Y轴的旋转度)
camera.eulerAngles =new Vector3(camera.eulerAngles.x,
player.eulerAngles.y,
camera.eulerAngles.z);
//获取当前的镜头的Y轴旋转度
float angle = camera.eulerAngles.y;
//计算x轴的距离差:
float deltaX = camera_distance * Mathf.Sin(angle * Mathf.PI /180 );
float deltaZ = camera_distance * Mathf.Cos (angle * Mathf.PI / 180);
//每一帧都改变摄像机的高度
camera.position = new Vector3 (player.position.x-deltaX,
player.position.y+ camera_height,
player.position.z-deltaZ);
这个lookAt已经不需要了,因为我们的镜头旋转到猪脚的正前方并且位置在他的正后方,那么我们镜头必然已经在看他了
最后的效果是无论猪脚怎样走,我们的镜头都在完美地跟随:
完整脚本代码(比官方的例子精简吧):
using UnityEngine;
using System.Collections;
//添加脚本到component菜单
[AddComponentMenu ("CameraControl/Follow")]
public class CameraFollow : MonoBehaviour {
//摄像机固定在猪脚上方10米高度
public float camera_height=10.0f;
//摄像机离猪脚大概10米的水平距离
public float camera_distance=10.0f;
//摄像机和猪脚的transform属性
private Transform player;
private Transform camera;
// Use this for initialization
void Start () {
//初始化
player=GameObject.FindGameObjectWithTag("Player").transform;
camera = Camera.main.transform;
}
// Update is called once per frame
void Update () {
//看向猪脚
//camera.LookAt (player);
//与猪脚的正前方为正前方(只取Y轴的旋转度)
camera.eulerAngles =new Vector3(camera.eulerAngles.x,
player.eulerAngles.y,
camera.eulerAngles.z);
//获取当前的镜头的Y轴旋转度
float angle = camera.eulerAngles.y;
//计算x轴的距离差:
float deltaX = camera_distance * Mathf.Sin(angle * Mathf.PI /180 );
float deltaZ = camera_distance * Mathf.Cos (angle * Mathf.PI / 180);
//每一帧都改变摄像机的高度
camera.position = new Vector3 (player.position.x-deltaX,
player.position.y+ camera_height,
player.position.z-deltaZ);
Debug.Log("angle:"+angle+",deltax:"+deltaX+",deltaZ:"+deltaZ);
}
}