创作人 Leo
编辑时间 Fri Jul 29,2022 at 19:47
踩过的坑:
1. 关于使用character controller无法使用重力的问题
2. Unity position和localposition
相关资料:
1. 点积
2. Unity教程:详解Character Controller角色移动控制
需求:
1. 角色静止状态,滑动鼠标已用户为轴心旋转摄像头,使之能浏览角色
2. 当角色行走,立即将摄像头回到角色背后,并保持位置(角色向前走,摄像头始终保持在角色后方)
3. WASD控制行走时,摄像头位置保持不变,角色模型跟随方向键进行旋转展示
实现效果:
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterCtrl : MonoBehaviour
{
[SerializeField]
Camera mainCamera;
[SerializeField]
Transform mainRole;
CharacterController cc;
bool ctrlEnable = false;
Vector3 cameraDefaultPosition;
Quaternion cameraDefaultRotation;
// Start is called before the first frame update
void Start()
{
//mainCamera = Camera.main;
cc = GetComponent<CharacterController>();
cameraDefaultPosition = mainCamera.transform.localPosition;
cameraDefaultRotation = mainCamera.transform.localRotation;
}
// Update is called once per frame
void Update()
{
keyListen();
if (ctrlEnable)
update2();
}
void keyListen()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
ctrlEnable = true;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
if (Input.GetKeyDown(KeyCode.Escape))
{
ctrlEnable = false;
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
}
// 2. rotate character it self
void update2()
{
float horizontalInput = Input.GetAxis("Horizontal"); // 获取左右移动值
float verticalInput = Input.GetAxis("Vertical"); // 获取上下移动值
float mx = Input.GetAxis("Mouse X"); // 获取鼠标左右移动值
// 物体是否在地面上判断,修改 y 轴
float _vertSpeed = 0;
if (!cc.isGrounded)
{
_vertSpeed += -9.8f * 5 * Time.deltaTime;
if (_vertSpeed < -10.0f)
{
_vertSpeed = -10.0f;
}
}
// 角色原地不动,鼠标左右摆动为摄像机绕角色旋转,
if (horizontalInput == 0f && verticalInput == 0f)
{
mainCamera.transform.RotateAround(transform.position, Vector3.up, mx);
cc.Move((new Vector3(0f, _vertSpeed, 0f)) * 0.1f);
return;
}
// 纵坐标向上的向量
Vector2 norVecOrig = new Vector3(0f, 1.0f);
// 合并 x y 移动为一个向量
Vector2 norVec = new Vector3(horizontalInput, verticalInput);
// 在计算两个向量相交角度时需要先算两个向量的模,而取模时每个值都是被平方的,所以没有负数,结果没有负角度
// 所以它最大就是180度
// 二象限和三象限需要通过判断水平x方向输入值为正负来确定是向左侧转角还向右侧转角
float dir = 1.0f;
if (horizontalInput < 0)
{
dir = -1.0f;
}
// 计算参与角度计算的两个向量的模
float mVecOrig = Mathf.Sqrt(Mathf.Pow(norVecOrig.x, 2) + Mathf.Pow(norVecOrig.y, 2));
float mVec = Mathf.Sqrt(Mathf.Pow(norVec.x, 2) + Mathf.Pow(norVec.y, 2));
// 计算两向量角度:向前的基础向量 (0, 1) , 角色移动的方向向量 (inputX, inputY)
// cos(C) = (a * b) / (|a| * |b|)
// C = acos((a * b) / (|a| * |b|))
float angel = Mathf.Acos(Vector2.Dot(norVecOrig, norVec) / (mVecOrig * mVec));
// 旋转角色模型
mainRole.localRotation = Quaternion.Euler(0f, angel * Mathf.Rad2Deg * dir, 1f);
// 摄像头位置移动重置,处理角色站立时摄像头的移动
// TODO 应该是角色移动方向自动调整为摄像机看的方向
mainCamera.transform.localPosition = cameraDefaultPosition;
mainCamera.transform.localRotation = cameraDefaultRotation;
// 鼠标左右移动选择角色并带动摄像机旋转
transform.Rotate(Vector3.up, mx);
Vector3 newVec = transform.forward * verticalInput + transform.right * horizontalInput ;
newVec.y = _vertSpeed; // 角色始终落地
cc.Move(newVec*0.1f);
}
}