鼠标旋转视角,摄像机跟随围绕主角,人物移动

草地+风场+UnityChan卡通渲染场景(一)

目前效果:
鼠标旋转视角,wasd控制移动
在这里插入图片描述
重写了UnityChan的控制代码,因为自带的UnityChanControlScriptWithRgidBody的操作很蹩脚,AD键不是向着左右两边走,而是旋转 ==|| 这。。
在这里插入图片描述
可能各有所好?我最喜欢的巫师3:狂猎和塞尔达传说:荒野之息都是按W向着屏幕的正前方走,AD向着屏幕左边缘和有边缘方向走,通过旋转鼠标来确定方向,所以我也打算这么做。
首先,写了一个相机跟随+转向脚本,思路很简单,
根据鼠标滑动方向旋转摄像机,根据一个设置好的距离调整摄像机到Chan的距离,
可以设置X轴的最大最小旋转角度,
因为总是卡到地下或者墙里,所以用射线检测来检测摄像机是否能继续旋转,比如:摄像机的高度已经很接近地面了,我们就不能让它在向下移动了。
在这里插入图片描述
相机跟随+转向脚本如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Camerafollow : MonoBehaviour {
    public Transform Chan;
    [Range(0,180)]
    public float max_X_Euler;
    [Range(-180, 0)]
    public float min_X_Euler;
    [Range(0, 60)]
    public float X_rotateSpeed;
    [Range(0, 60)]
    public float Y_rotateSpeed;
    [Range(1,10)]
    public float XZ_dist;
    [Range(1, 10)]
    public float Y_dist;
    private float current_rotation_X;
    private float current_rotation_Y;
    private float last_rotation_X;
    private float last_rotation_Y;
    private bool[] Xobstacle;
    private bool[] Yobstacle;
	// Use this for initialization
	void Start () {
        Xobstacle = new bool[2];
        Yobstacle = new bool[2];
    }
	
	// Update is called once per frame
	void LateUpdate () {
        current_rotation_X += Input.GetAxis("Mouse Y") * X_rotateSpeed;
        current_rotation_Y+= Input.GetAxis("Mouse X") * Y_rotateSpeed;
        Xobstacle[0] = Physics.Linecast(transform.position, transform.position + new Vector3(0, -1.5f, 0));
        Xobstacle[1] = Physics.Linecast(transform.position, transform.position + new Vector3(0, -1.5f, 0));
        Yobstacle[0] = Physics.Linecast(transform.position, transform.position + transform.TransformDirection(new Vector3(1, 0, 0)));
        Yobstacle[1] = Physics.Linecast(transform.position, transform.position + transform.TransformDirection(new Vector3(-1, 0, 0)));
        Physics.Linecast(transform.position, transform.position + new Vector3(1, 0, 0));
        current_rotation_X = Mathf.Clamp(current_rotation_X, min_X_Euler, max_X_Euler);
        if (Xobstacle[0] && current_rotation_X > last_rotation_X)
        {
            current_rotation_X = last_rotation_X;
        }
        else if(Xobstacle[1]&& current_rotation_X < last_rotation_X)
        {
            current_rotation_X = last_rotation_X;
        }
        if (Yobstacle[0]&&current_rotation_Y<last_rotation_Y)
        {
            current_rotation_Y = last_rotation_Y;
        }
        else if (Yobstacle[1] && current_rotation_Y > last_rotation_Y)
        {
            current_rotation_Y = last_rotation_Y;
        }
        transform.localEulerAngles = new Vector3(-current_rotation_X, current_rotation_Y, 0f);
        transform.position = Chan.position;
        transform.Translate(Vector3.back * XZ_dist, Space.Self);
        transform.Translate(Vector3.up * Y_dist, Space.World);
        last_rotation_X = current_rotation_X;
        last_rotation_Y = current_rotation_Y;
    }
}

我模仿了巫师3的人物移动,效果如下:
在这里插入图片描述
w:摄像机的前方;a:摄像机的左边方向;aw:摄像机的左前方。以此类推。
首先需要计算出人物需要旋转的角度,因为Chan需要先转身,再朝着那个方向移动,
核心语句:

 angle = (Vector3.Dot(Chan.right, direction[1]) > 0 ? 1 : -1)
        * Vector2.Angle(new Vector2(direction[0].x, direction[0].z), new Vector2(direction[1].x, direction[1].z));

通过向量点乘判断角度的正负,direction[0]是chan的朝向,direction[1]存放的是chan将要朝向的方向,正方向都好说通过forward和right可以方便得到,类似左前方这样的,我是乘了一个旋转矩阵,比如左前方就是a*[45°的旋转矩阵]得到的:
在这里插入图片描述

 private Matrix4x4 rotate45 = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 45, 0), Vector3.one);
 direction[1] = rotate45.MultiplyPoint3x4(-CameraforChan.right);

Chan的全部移动代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Movewithrigidbody2 : MonoBehaviour {
    [Range(0.1f, 10f)]
    public float moveSpeed;
    [Range(.1f, 2.66f)]
    public float jumpHeight;
    [Range(10, 360f)]
    public float rotationSpeed;
    private Animator An;
    private Rigidbody Ri;
    private Transform Chan;
    public Transform CameraforChan;
    private Vector3[] direction;
    private float angle;
    private bool rotating;
    private Matrix4x4 rotate45 = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 45, 0), Vector3.one);
    private AnimatorStateInfo currentBaseState;
    static int jumpState = Animator.StringToHash("Base Layer.Jump");
    void Start()
    {
        An = this.GetComponent<Animator>();
        Ri = this.GetComponent<Rigidbody>();
        Chan = this.transform;
        direction = new Vector3[2];
        rotating = false;
    }
    private void LateUpdate()
    {
        Movepos(Input.GetAxis("Horizontal"),Input.GetAxis("Vertical"));
        Jump(Input.GetAxis("Jump"));
    }
    private void Jump(float var)
    {
        An.SetBool("Jump", false);
        if (var > 0 && !An.IsInTransition(0))
        {
            An.SetBool("Jump", true);
            Ri.velocity = new Vector3(0, Mathf.Sqrt(2 * 9.8f * jumpHeight), 0);
        }
    }
    private void Movepos(float LR,float FB)
    {
        float var = FB != 0 ? FB : LR;
        currentBaseState = An.GetCurrentAnimatorStateInfo(0);
        if (currentBaseState.fullPathHash != jumpState)
        {
            An.SetFloat("Speed",Mathf.Abs(var));
            if (var!=0)
            {
                Calculation(FB>0&&LR<0?4: FB > 0 && LR > 0 ? 5:FB < 0 && LR < 0 ?6: FB < 0 && LR > 0 ?7
                    : FB > 0 ? 0 : FB < 0 ? 1 : LR < 0 ? 2 : 3);
                if (!rotating)
                {
                    Ri.MovePosition(Chan.position + direction[1] * Time.fixedDeltaTime * moveSpeed);
                }
            }
        }
    }
    private void Calculation(int LRFB)
    {
        direction[0] = Chan.forward;
        switch (LRFB) {
            case 4:
                direction[1] = rotate45.MultiplyPoint3x4(-CameraforChan.right);
                break;
            case 5:
                direction[1] = rotate45.MultiplyPoint3x4(CameraforChan.forward);
                break;
            case 6:
                direction[1] = rotate45.MultiplyPoint3x4(-CameraforChan.forward);
                break;
            case 7:
                direction[1] = rotate45.MultiplyPoint3x4(CameraforChan.right);
                break;
            case 0:
                direction[1] = CameraforChan.forward;
                break;
            case 1:
                direction[1] = -CameraforChan.forward;
                break;
            case 2:
                direction[1] = -CameraforChan.right;
                break;
            case 3:
                direction[1] = CameraforChan.right;
                break;
        }
        angle = (Vector3.Dot(Chan.right, direction[1]) > 0 ? 1 : -1)
                    * Vector2.Angle(new Vector2(direction[0].x, direction[0].z), new Vector2(direction[1].x, direction[1].z));
        if (Mathf.Abs(angle) <90)
        {
            rotating = false;
        }
        else
        {
            rotating = true;
        }
        if (Mathf.Abs(angle) > 10)
        {
            Ri.MoveRotation(Quaternion.Euler(Chan.rotation.eulerAngles+new Vector3(0, angle * rotationSpeed * Time.fixedDeltaTime, 0)));
        }
    }
}

以上两个脚本配合,人物控制类似巫师3

========================================================================

和高中同学聊了聊,好像龙腾世纪3是左右旋转的设定,我之前轻改了一下UnityChanControlScriptWithRgidBody,把一些没用的去掉了,也不是说不能用,只是我感觉有点蹩脚。。。代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Movewithrigidbody : MonoBehaviour {
    [Range(0.1f,10f)]
    public float moveSpeedfront;
    [Range(0.1f, 10f)]
    public float moveSpeedback;
    [Range(.1f, 2.66f)]
    public float jumpHeight;
    [Range(10, 360f)]
    public float rotationSpeed; 
    private Animator An;
    private Rigidbody Ri;
    //private Collider Co;
    private Transform Chan;
    private AnimatorStateInfo currentBaseState;
    static int jumpState = Animator.StringToHash("Base Layer.Jump");
    void Start () {
        An = this.GetComponent<Animator>();
        Ri = this.GetComponent<Rigidbody>();
        Chan = this.transform;
	}
    private void Update()
    {
        if(Input.GetMouseButtonDown(0))Cursor.visible = false;
    }
    void FixedUpdate () {
        currentBaseState = An.GetCurrentAnimatorStateInfo(0);//得到动画状态机Layers0的状态信息
        if (currentBaseState.fullPathHash != jumpState)
        {
            if (!An.IsInTransition(0))
            {
                An.SetBool("Jump", false);
            }
            Frontbackmove(Input.GetAxis("Vertical"));
            Jump(Input.GetButtonDown("Jump"));
            Rotatechange(Input.GetAxis("Horizontal"));
        }
        else
        {
            float ver = Input.GetAxis("Vertical");
            if (ver > 0)
            {
                Ri.AddForce(Chan.TransformDirection(new Vector3(0, 0, ver * moveSpeedfront * Time.fixedDeltaTime*36)),ForceMode.VelocityChange);
            }
        }
        
    }
    private void Frontbackmove(float ver)
    {
        if (Input.GetAxis("LSHIFT") > 0) ver *= .36f;
        An.SetFloat("Speed", ver);
        if (ver > 0.1f)
        {
            Ri.MovePosition(Chan.position+Chan.TransformDirection(new Vector3(0, 0, ver * moveSpeedfront*Time.fixedDeltaTime)));
        }
        else if(ver < -0.1f)
        {
            Ri.MovePosition(Chan.position+Chan.TransformDirection(new Vector3(0, 0, ver * moveSpeedback * Time.fixedDeltaTime)));
        }
    }
    private void Jump(bool ver)
    {
        if (ver)
        {
           An.SetBool("Jump", true);
           Ri.velocity = new Vector3(0, Mathf.Sqrt(2 * 9.8f * jumpHeight),0);
        }
    }
    public  void Rotatechange(float ver)
    {
        An.SetFloat("Direction", ver);
        if (ver != 0)
        {
            Ri.MoveRotation(Quaternion.Euler(Chan.rotation.eulerAngles+new Vector3(0,ver*rotationSpeed*Time.fixedDeltaTime,0)));
        }
    }
}

另外,帧数比较低的原因是unity自带terrain的草没有批合,打算用Mesh地形+gpuinstance来重做地形,感觉可控性高一点。

猜你喜欢

转载自blog.csdn.net/qq_33967521/article/details/84994837