Unity实现第三⼈称视⾓
最近耍了⼀波巫师3,突然想到能不能做个简单的第三⼈称视⾓的摄像机控制。不过这个摄像机控制⽬前还没有被卡视⾓的功能(就是那种根据地形调整摄像机距离)准备在后⾯⼏天实现卡视⾓功能。不过先来做个简单的第三⼈称视⾓控制吧。开冲!
在场景中⽤Cube搭个简易的⼩⼈(插个标志标明⼩⼈的前⽅向),以⼀个Plane为地⾯,⽤⼀个Cube与⼀个Sphere作为标识,容易辨别视⾓移动。
新建⼀个C#脚本CameraMove,拖动到MainCamera上
在脚本中定义如下变量:
素描植物
//player info
private GameObject player;
林州大峡谷private Transform playerTF;
//distance vector between player and camera
private Vector3 dirVector;
public float distance;
//mou move
private float fMouX;
private float fMouY;
public float speed;
public float bottomLimitAngle;//the limit angle
private float bottomLimit;//the cos value
其中player就是玩家⼈物需要拖动赋值,playerTF是通过player得到的transform。dirVector是从player指向camera的⼀个⽅向向量。distance是指player与camera之间的距离,在Inspector中赋值。fMouX与fMouY是⿏标的移动量,speed是视⾓灵敏度(⿏标移动时,视⾓的变化速度),bottomLimitAngle是⽤于限制camera可以向下移动到什么程度,bottomLimit是通过bottomLimit得到的Cos 值。
void Start () {
//**System ttings**
Cursor.visible = fal;
Cursor.lockState = CursorLockMode.Locked;
//**assignment initial**
//player assignment
player = GameObject.FindGameObjectWithTag ("Player");
playerTF = ansform;
//**value initial**
//distanceVector initial
dirVector = Vector3.Normalize(playerTF.position - transform.position);
不安的灵魂transform.position = playerTF.position + distance * (-dirVector);
//mou move initial
种豆子fMouX = 0;
fMouY = 0;豆腐小白菜
bottomLimit = Mathf.Cos (bottomLimitAngle / 180 * Mathf.PI);
}
初始化就不需要多说了,很容易理解,不过要注意把Player的Tag改为“Player”。
下⾯介绍Update部分的核⼼代码:
1.保持摄像机看向玩家。
//Update Camera
transform.LookAt (playerTF);
2.获取⿏标变化量,并根据⿏标移动来改变视⾓⾓度。在视⾓移动之前先监测是否已经到达的最下⽅,如果已经到达,且⽤户仍试图向下移动视⾓(fMou>0),则令fMouY为0。下⼀步要进⾏视⾓移动与旋转,注意!视⾓的⽔平旋转与垂直旋转是不⼀样的!(下⾯会介绍),最后更新⼀下⽅向向量。
//Camera Move
fMouX = Input.GetAxis ("Mou X");
fMouY = Input.GetAxis ("Mou Y");
//avoid dithering
if (Vector3.Dot (-alized, -alized) > bottomLimit) {
if (fMouY > 0) {
fMouY = 0;
};
}
//two types of parameters;手机微店货源
//(axis,value)is rotate around the axis of the transform's position;
// (position, axis, value)is rotate around the axis of the specific position;
//Rotate Horizontal
transform.RotateAround(playerTF.position ,playerTF.up, speed * fMouX);
//Rotate Vertical
transform.RotateAround (playerTF.position, -VerticalRotateAxis(dirVector),speed * fMouY);
//distance Control
dirVector = Vector3.Normalize(playerTF.position - transform.position);
那么视⾓的⽔平旋转与垂直旋转到底有什么不同呢?如下图。⽔平旋转是绕着playerTF.up即玩家的向上的轴旋转的。⽽垂直旋转是绕着玩家的中⼼旋转的,如果垂直旋转也像⽔平旋转⼀样操作,⽐如绕着playerTF.right来旋转,则会造成⼀种错误的现象:当我们不停地下移⿏标时,视⾓并没有移下去,⽽是⼀直在绕⼀个⼩圈。所以对于垂直旋转,还需要计算⼀个法线向量。
下⾯介绍计算垂直旋转的法线向量的⽅法。如上图中我们画出的垂直旋转的圆,它与xz平⾯相交处的切线⼀定是垂直于xz平⾯的,所以我们可以确定,这个法线向量⼀定是在xz平⾯中的。设为normal(a,0,c);与旋转另⼀个相关的属性是dirVector,因为旋转⾯就是由dirVector绕normal 旋转⼀圈 形成的。设它为dirVector(x,y,z)。
则由normal与dirVector的点乘积为0得出ax+cz=0,得出a =(-z/x)*c,令a^2+c^2=1得出a^2 = z^2 / (x^2 + z^2);c^2 = x^2 / (x^2 + z^2)。
这样就确定了normal的a与c的绝对值是多少,那么怎么来确定其正负值呢,我们⼀致设定 在⽔平轴为x,纵轴为z的⼆维坐标系中(如下图) dirVector在normal的顺时针旋转90度的位置。由此得出:以下情况:
dir:x>0,z>0在第⼀象限。 normal:a<0,c>0在第⼆象限
dir:x>0,z<0在第四象限。 normal:a>0,c>0在第⼀象限
dir:x<0,z<0在第三象限。 normal:a>0,c<0在第四象限
dir:x<0,z>0在第⼆象限。 normal:a<0,c<0在第⼀象限
代码实现如下:
Vector3 VerticalRotateAxis(Vector3 dirVector){
Vector3 player2Camera = -alized;
float x = player2Camera.x;
float z = player2Camera.z;
Vector3 rotateAxis = ;
rotateAxis.z = Mathf.Sqrt (x * x / (x * x + z * z));
rotateAxis.x = Mathf.Sqrt (z * z / (x * x + z * z));
if (x >= 0) {
if (z >= 0) {
rotateAxis.x = -rotateAxis.x;
}
} el {
if (z >= 0) {
rotateAxis.x = -rotateAxis.x;
rotateAxis.z = -rotateAxis.z;
} el {
rotateAxis.z = -rotateAxis.z;
}
}
Debug.Log (rotateAxis);
return rotateAxis;
}
过⼏天后 ⼩菜鸡我会更新根据地形变化来改变摄像机距离,希望和各位道友⼀起努⼒
黄瓜豆腐汤
现在该来实现摄像机距离根据地形变化了,话不多说,上代码:
//distance Control
dirVector = Vector3.Normalize(playerTF.position - transform.position);
Ray cameraRay = new Ray(playerTF.position, -dirVector);
钟欣桐陈冠希RaycastHit hitinfo;
if (Physics.Raycast (cameraRay, out hitinfo, distance*10/9, LayerMask.GetMask("Terrain"))) {
actualDistance = hitinfo.distance * 9/10;
transform.position = playerTF.position + actualDistance * (-dirVector);
} el {
actualDistance = distance;
transform.position = playerTF.position + actualDistance * (-dirVector);
}
更新Update中的distance Control模块中的代码,从玩家中⼼发射⼀条长度为 预设摄像机距离* 10/9 的射线,然后根据射线是否检测带地表,更新摄像机与玩家之间的实际距离actualDistance,乘10/9与乘9/10是我在测试时使⽤的两个参数,为了避免摄像机贴着地⾯移动 及 在射线检测到地⾯的边界处出现突变导致摄像机进⼊地表中,可以根据需要改动。