从零开始的RPG制作6.1-(摄像机跟随,缩放,旋转)

首先基础的摄像机跟随写法是。

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();
            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();
        pad = new PlayerAnimaId();
        player = transform;
        playerAnimatorInit();
        capsuleCollider = GetComponent();
        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();
    }
效果

这边的效果还没有很完整,还有一个地方没有设置,下一节中我们在提
上一节
下一节

你可能感兴趣的:(从零开始的RPG制作6.1-(摄像机跟随,缩放,旋转))