首先確定功能。
1.如果攝像機和跟隨物體之間有東西夾著,攝像機自動移動到被夾著的物體的表面。
2.如果攝像機和和跟隨物體直接夾著的東西消失,攝像機自動返回到原來的縮放。
3.如果攝像機和跟隨物體之間有東西夾著,攝像機自動移動到被夾著的物體的表面,這時候攝像機還能進行朝前縮進,一單發(fā)生轉(zhuǎn)向,攝像機繼續(xù)回到原來的縮放大小,但是保持原有的方向。
下面提供一下我的思路:
offset = 攝像機向量-跟隨物體向量;
將offset 拆開,一份用于存儲方向,一份用于存儲距離。
轉(zhuǎn)向,人物移動,這部分我們來修改方向。
而攝像機朝前超后推,遇到阻礙物,來修改距離,最終統(tǒng)一合成一個新向量,接下來放出代碼,修改了許多版本終于感覺可以用了。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraMessage:MonoBehaviour {
struct CameraChangeData {
public float ScrollWheel;
public float RotateX;
public float RotateY;
}
CameraChangeData cameraChangeData;
static public CameraMessage cm;
private Vector3 offSetPostion;//從目標指向攝像機的向量
private float offSetPostionDistance;//offSetPostion的長度
private float scrollSpeed = 3.0f;//向前向后靠近的速度
private float rotateSpeed = 2f;//轉(zhuǎn)向速度
private Vector3 followVector;//需要注視的坐標。
bool isRotate = false;//是否出于旋轉(zhuǎn)狀態(tài)
Vector3 lestNewF;//在攝像機發(fā)生旋轉(zhuǎn)的時候,我們不希望角色的方向轉(zhuǎn)化到攝像機的坐標系下,繼而使用沒有轉(zhuǎn)向時候的向量。
private Transform camareTr;//攝像機
private Transform followObject;//追隨物
static public CameraMessage getInstance() {
static public CameraMessage getInstance() {
if (cm == null) {
cm = GameObject.FindObjectOfType<CameraMessage>();
cm.init();
}
return cm;
}
void init() {
camareTr = transform;
cameraChangeData = new CameraChangeData();
}
float? offsetMagnitud;//用于保存出現(xiàn)夾著的物體的時候,攝像機和跟隨物體的長度。
private bool cameraRay() {//如果中間隔著遮擋物。相機應(yīng)該貼著遮擋物(看了塞爾達的視頻,應(yīng)該也是這么處理的。),如果一旦沒有了遮擋物返回到之前設(shè)定的狀態(tài)。
Vector3 vt = camareTr.position - followVector;
RaycastHit rayHit;
bool rayGround = false;
if (Physics.Raycast(followVector, vt, out rayHit,100, LayerMask.GetMask("IsGround"))) {
Vector3 newOffSetPostion = rayHit.point - followVector;
if (newOffSetPostion.magnitude <offSetPostionDistance) {//如果被地面夾在中間
if (offsetMagnitud == null) {//如果這時候發(fā)現(xiàn)offSetPostion的長度為空,這就意味著第一次遇到遮擋物。
offsetMagnitud = offSetPostionDistance;
}
offSetPostionDistance = newOffSetPostion.magnitude;
rayGround = true;
}
} else {
if (offsetMagnitud != null) {
offSetPostionDistance = Mathf.Lerp(offSetPostionDistance, (float)offsetMagnitud, 0.2f);//漸漸的返回成第一次遇到遮擋物之前的長度。
if (Mathf.Abs(offSetPostionDistance - (float)offsetMagnitud) <= 0.01f) {
offsetMagnitud = null;
}
}
}
if (offsetMagnitud != null) {
rayGround = true;
}
IDrawGizmos.drawLine(followVector, camareTr.position, Color.red, 3);
IDrawGizmos.drawLine(followVector, followObject.position + followObject.up * 5, Color.red, 4);
return rayGround;
}
public void setInitOffsetPosittion(Transform followObject, Vector3 targetPos) {//設(shè)置相機跟隨對象。
this.followObject = followObject;
offSetPostion = camareTr.position - targetPos;//計算由角色指向相機的向量
offSetPostionDistance = offSetPostion.magnitude;
}
float lastmd;
public void setUpdateFollowVector(Vector3 followVector) {
//如果是PC------
keyController();
this.followVector = followVector;//源源不斷的獲取人物坐標。
bool rayGround = cameraRay();//放入射線
scrollview(rayGround);//縮放信息在這一幀中起效
camareTr.position = followVector + offSetPostion.normalized* offSetPostionDistance;
rotateView();//轉(zhuǎn)向消息在下一幀中起效
}
private void keyController() {//將鍵盤操作的部分獨立出來,將來說不定要換平臺呢。
cameraChangeData.ScrollWheel = Input.GetAxis("Mouse ScrollWheel") * scrollSpeed;
if (Input.GetMouseButtonDown(1)) {
isRotate = true;
} else if (Input.GetMouseButtonUp(1)) {
isRotate = false;
}
cameraChangeData.RotateX = Input.GetAxis("Mouse X") * rotateSpeed;
cameraChangeData.RotateY = Input.GetAxis("Mouse Y") * rotateSpeed;
}
private void scrollview(bool rayGround) {//控制前后縮放
float newOffSetPostionDistance = offSetPostionDistance - cameraChangeData.ScrollWheel;
if (newOffSetPostionDistance < 2 && cameraChangeData.ScrollWheel > 0) {//當(dāng)距離小于2就不能往前推攝像機
return;
} else if ((newOffSetPostionDistance > 12 && cameraChangeData.ScrollWheel < 0) || (cameraChangeData.ScrollWheel < 0 && rayGround)) {//當(dāng)距離大于12或者有遮擋物就不能往后推攝像機
return;
}
offSetPostionDistance = Mathf.Lerp(offSetPostionDistance, newOffSetPostionDistance, 0.2f);
}
private void rotateView() {//控制轉(zhuǎn)向
if (isRotate) {
camareTr.RotateAround(followVector, Vector3.up, cameraChangeData.RotateX);//在以世界的UP轉(zhuǎn)
if (Vector3.Angle(followObject.up, offSetPostion) < 30 && cameraChangeData.RotateY < 0) {//向上轉(zhuǎn)的時候和人物的up不能超過30°
cameraChangeData.RotateY = 0;
} else if (Vector3.Angle(followObject.up, offSetPostion) > 160 && cameraChangeData.RotateY > 0) {//向下轉(zhuǎn)的時候和人物的up不能超過160°,這里可以添加小姐姐捂裙子的操作- .-
cameraChangeData.RotateY = 0;
}
camareTr.RotateAround(followVector, camareTr.TransformDirection(Vector3.right), -cameraChangeData.RotateY);//以攝像機的right轉(zhuǎn),如果以世界的right轉(zhuǎn),會受到X軸的干擾,所有用角色right.
offSetPostion = camareTr.position - followVector;//需要重新改變方向
}
}
public Vector3 DisplacementCoordinates(Vector3 targer) {//將控制方向從世界轉(zhuǎn)到攝像機,即,WASD的移動都按照攝像機在標準世界坐標投影上的向量移動。
if (isRotate)//視角在發(fā)生旋轉(zhuǎn)的時候 人物不應(yīng)該跟著攝像機走。
return lestNewF;
Vector3 ct = camareTr.TransformDirection(targer);//將輸入操作轉(zhuǎn)到攝像機坐標
Vector3 f = Vector3.Project(ct, Vector3.forward);//找到在forward上的投影,
Vector3 r = Vector3.Project(ct, Vector3.right);//找到在right上的投影
Vector3 newF = f + r;//兩者相加,就是新的面朝方向。
lestNewF = newF;
return newF;
}
}

效果