首先基础的摄像机跟随写法是。
1摄像机简单跟随
初始化:向量A = 摄像机坐标- 需要跟随物的坐标;
update:摄像机坐标 = 向量A + 需要跟随物的坐标;
这能基础的得到我们想要的效果。
2摄像机的远近缩放
我们一般通过修改上述得到的:
向量A = 向量A.normalized *向量A.magnitude
3摄像机绕着物体旋转
一般我们借用RotateAround函数得到
摄像机.RotateAround(跟随物体坐标,世界朝上坐标up, 旋转幅度X轴幅度);
摄像机.RotateAround(跟随物体坐标,摄像机的Right, 旋转幅度Y轴幅度);
这里你可能会看到第二个参数不太一样,这是因为当随着X转的时候,如果还是按照世界坐标系的right来转就乱了哦,自己画图一下就明白了。
4摄像机和跟随物之间如果有遮挡物。
这里我们用了一条从角色射像摄像机的射线来做检测。
if(检测道遮挡){
向量A = 遮挡物的坐标-跟随坐标。
}
5让我们输入的方向和摄像的前后左右对应。
将我们的输入的方向 转入摄像机的坐标,接着将装入的坐标在标准世界坐标上进行投影,投影之后在传给我们的角色。
这里我们新建了一个CameraMessage的类,里面添加很多限制,比如缩进长短,旋转范围之类,不过注解很详细。
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 = 1;//向前向后靠近的速度
private float rotateSpeed = 2f;//转向速度
private Vector3 followVector;//需要注视的坐标。
bool isRotate = false;//是否出于旋转状态
Vector3 lestNewF;//在摄像机发生旋转的时候,我们不希望角色的方向转化到摄像机的坐标系下,继而使用没有转向时候的向量。
private Transform camareTr;//摄像机
private Transform followObject;//追随物
static public CameraMessage getInstance() {
if (cm == null) {
cm = GameObject.FindObjectOfType<CameraMessage>();
cm.init();
}
return cm;
}
void init() {
camareTr = transform;
cameraChangeData = new CameraChangeData();
}
private void cameraRay() {//如果中间隔着遮挡物。相机应该贴着遮挡物(看了塞尔达的视频,应该也是这么处理的。),如果一旦没有了遮挡物返回到之前设定的状态。
Vector3 vt = camareTr.position - followVector;
RaycastHit rayHit;
if (Physics.Raycast(followVector, vt, out rayHit, LayerMask.GetMask("IsGround"))) {
offSetPostion = rayHit.point - followVector;
}
IDrawGizmos.drawLine(followVector, camareTr.position, Color.red,3);
IDrawGizmos.drawLine(followVector, followObject.position+followObject.up*5,Color.red,4);
}
public void setInitOffsetPosittion(Transform followObject, Vector3 targetPos) {//设置相机跟随对象。
this.followObject = followObject;
offSetPostion = camareTr.position - targetPos;//计算由角色指向相机的向量
}
public void setUpdateFollowVector(Vector3 followVector) {
//如果是PC------
keyController();
this.followVector = followVector;//源源不断的获取人物坐标。
cameraRay();//放入射线
scrollview();//缩放信息在这一帧中起效
camareTr.position = followVector + offSetPostion;
rotateView();//转向消息在下一帧中起效
}
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() {//控制前后缩放
offSetPostionDistance = offSetPostion.magnitude;
offSetPostionDistance -= cameraChangeData.ScrollWheel;
if (offSetPostionDistance < 2 && cameraChangeData.ScrollWheel>0) {//当距离小于2就不能继续放大
return;
} else if (offSetPostionDistance > 12 && cameraChangeData.ScrollWheel<0) {
return;
}
Vector3 newOffSetPostion = offSetPostion.normalized * offSetPostionDistance;
offSetPostion = Vector3.Slerp(offSetPostion, newOffSetPostion, 0.3f);
}
private void rotateView() {//控制转向
if (isRotate) {
camareTr.RotateAround(followVector, Vector3.up, cameraChangeData.RotateX);//在以世界的UP转
if (Vector3.Angle(followObject.up, offSetPostion) < 30 && cameraChangeData.RotateY < 0) {//向上转的时候和人物的up不能超过30°
cameraChangeData.RotateY = 0;
} else if (Vector3.Angle(followObject.up, offSetPostion) > 160 && cameraChangeData.RotateY > 0) {//向下转的时候和人物的up不能超过160°,这里可以添加小姐姐捂裙子的操作- .-
cameraChangeData.RotateY = 0;
}
camareTr.RotateAround(followVector, camareTr.TransformDirection(Vector3.right), -cameraChangeData.RotateY);//以摄像机的right转,如果以世界的right转,会受到X轴的干扰,所有用角色right.
offSetPostion = camareTr.position - followVector;
}
}
public Vector3 DisplacementCoordinates(Vector3 targer) {//将控制方向从世界转到摄像机,即,WASD的移动都按照摄像机在标准世界坐标投影上的向量移动。
if (isRotate)//视角在发生旋转的时候 人物不应该跟着摄像机走。
return lestNewF;
Vector3 ct = camareTr.TransformDirection(targer);//将输入操作转到摄像机坐标
Vector3 f = Vector3.Project(ct, Vector3.forward);//找到在forward上的投影,
Vector3 r = Vector3.Project(ct, Vector3.right);//找到在right上的投影
Vector3 newF = f + r;//两者相加,就是新的面朝方向。
lestNewF = newF;
return newF;
}
}
接着回到我们的PlayerTestView进行调用。
CameraMessage cm;
public void init() {
rig = GetComponent<Rigidbody>();
pad = new PlayerAnimaId();
player = transform;
playerAnimatorInit();
capsuleCollider = GetComponent<CapsuleCollider>();
radius = capsuleCollider.radius - 0.01f;
cm = CameraMessage.getInstance();//新增
if (cm != null) {
cm.setInitOffsetPosittion(player,player.position+ player.up* capsuleCollider.height/2);//新增
}
}
void animaBlendAction(float right, float forward, bool run) {//设置转向以及速度
Vector3 vec = (right * Vector3.right + forward * Vector3.forward);//设置方向
controlDistance = vec.magnitude;
if (controlDistance < Vector3.kEpsilon) {//停下
controlDistance = 0;
lerpSpeed = Mathf.Lerp(lerpSpeed, 0, 0.2f);//速度逐渐归零。
} else {//开始行动
vec = cm.DisplacementCoordinates(vec);
player.forward = Vector3.Slerp(player.forward, vec, 0.2f);//设置转向
float upperLimit = run ? playerBaseData.runSpeed : playerBaseData.walkSpeed;
lerpSpeed = Mathf.Lerp(lerpSpeed, upperLimit, 0.1f);//速度逐步上升。
}
animator_ChangeIdle();//播放Idle
animator_PlayBaseAniam();//播放动画
moveDirection = Vector3.Project(moveDirection, Vector3.up);
moveDirection += player.forward * lerpSpeed * addSpeed;
}
public void cameraFollow() {//新增
if (cm != null)
cm.setUpdateFollowVector(player.position + player.up * capsuleCollider.height / 2);
}
接着在我们的PlayerTestMediator中进行调用。
public override void OnRegister() {//绑定成功之后会调用这个API
Debug.Log("OnRegister");
playerView.init();
UpdateManges.add_playerEventList_(keyController);
UpdateManges.add_playerEventList_(playerAction);
UpdateManges.add_playerEventList_Fix(accurateDetection);
UpdateManges.add_playerEventList_Late(cameraContorller);//新增
}
public override void OnRemove() {//解除绑定之后调用这个API
Debug.Log("OnRemove");
UpdateManges.sub_playerEventList_(keyController);
UpdateManges.sub_playerEventList_(playerAction);
UpdateManges.sub_playerEventList_(accurateDetection);
UpdateManges.sub_playerEventList_Late(cameraContorller);//新增
}
void cameraContorller() {//新增
playerView.cameraFollow();
}
效果
网友评论