一:简易第一人称视角控制
private Transform cameraTransform;//相机
[SerializeField]
private Transform characterTransform;//人物
private Vector3 cameraRotation;
public float MouseSensitivity;//5
public Vector2 MaxminAngle;//(-65,65)
private void Start()
{
cameraTransform = transform;
}
void Update()
{
var tmp_MouseX = Input.GetAxis("Mouse X");
var tmp_MouseY = Input.GetAxis("Mouse Y");
cameraRotation.x -= tmp_MouseY * MouseSensitivity;
cameraRotation.y += tmp_MouseX * MouseSensitivity;
cameraRotation.x = Mathf.Clamp(cameraRotation.x, MaxminAngle.x, MaxminAngle.y);//限制人物的俯仰角度
characterTransform.rotation = Quaternion.Euler(0, cameraRotation.y, 0);//人物旋转跟随鼠标
cameraTransform.rotation = Quaternion.Euler(cameraRotation.x, cameraRotation.y, 0); //控制相机的旋转和人物的旋转保持一致
二:第一人称移动控制
(1):常规控制移动方法:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FPSMoveMovement : MonoBehaviour
{
public float Speed;
public float JumpHeight;
public float gravity;
private Transform characterTransform;
private Rigidbody characterRigidbody;
private bool isGrounded;
// Start is called before the first frame update
void Start()
{
characterTransform = transform;
characterRigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
if (isGrounded)
{
var tmp_Horizontal = Input.GetAxis("Horizontal");
var tmp_Vertical = Input.GetAxis("Vertical");
var tmp_CurrentDirection = new Vector3(tmp_Horizontal, 0, tmp_Vertical);
tmp_CurrentDirection = characterTransform.TransformDirection(tmp_CurrentDirection);//将角色的局部坐标转化为世界坐标
tmp_CurrentDirection *= Speed;
var tmp_CurrentVelocity = characterRigidbody.velocity;
var tmp_VelocityChange = tmp_CurrentDirection - tmp_CurrentVelocity;
tmp_VelocityChange.y = 0;
//控制角色的移动
characterRigidbody.AddForce(tmp_VelocityChange, ForceMode.VelocityChange);
//控制角色的跳跃
if (Input.GetButtonDown("Jump"))
{
characterRigidbody.velocity=new Vector3(tmp_CurrentVelocity.x,CalculateJumpHeightSpeed(),
tmp_CurrentVelocity.z);
}
}
characterRigidbody.AddForce(new Vector3(0, -gravity*characterRigidbody.mass, 0)); //控制角色的下落速度
}
//控制角色的跳跃高度
private float CalculateJumpHeightSpeed()
{
return Mathf.Sqrt(2 * gravity * JumpHeight);
}
private void OnCollisionStay(Collision collision)
{
isGrounded = true;
}
private void OnCollisionExit(Collision collision)
{
isGrounded = false;
}
}
(2):通过CharaCterController控制人物的移动方法
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
public class FPCharacterControllerMovement : MonoBehaviour
{
private CharacterController characterController;
private Animator characterAnimator;
private Vector3 movementDirection;
private Transform characterTransform;
private float velocity;
private bool isCrouched;
private float originHeight;
public float SprintingSpeed;
public float WalkSpeed;
public float SprintingSpeedWhenCrouched;
public float WalkSpeedWhenCrouched;
public float Gravity = 9.8f;
public float JumpHeight;
public float CrouchHeight = 1f;
private void Start()
{
characterController = GetComponent<CharacterController>();
characterAnimator = GetComponentInChildren<Animator>();
characterTransform = transform;
originHeight = characterController.height;
}
private void Update()
{
float tmp_CurrentSpeed = WalkSpeed;
if (characterController.isGrounded)//CharacterController是否在最后一次移动接触地面
{
var tmp_Horizontal = Input.GetAxis("Horizontal");
var tmp_Vertical = Input.GetAxis("Vertical");
movementDirection =
characterTransform.TransformDirection(new Vector3(tmp_Horizontal, 0, tmp_Vertical));//将局部坐标转换为世界坐标
//控制人物的跳跃
if (Input.GetButtonDown("Jump"))
{
movementDirection.y = JumpHeight;
}
//控制人物的下蹲
if (Input.GetKeyDown(KeyCode.C))
{
var tmp_CurrentHeight = isCrouched ? originHeight : CrouchHeight;
StartCoroutine(DoCrouch(tmp_CurrentHeight));
isCrouched = !isCrouched;
}
//控制人物下蹲、站立的移动速度
if (isCrouched)
{
tmp_CurrentSpeed = Input.GetKey(KeyCode.LeftShift) ? SprintingSpeedWhenCrouched : WalkSpeedWhenCrouched;
}
else
{
tmp_CurrentSpeed = Input.GetKey(KeyCode.LeftShift) ? SprintingSpeed : WalkSpeed;
}
var tmp_Velocity = characterController.velocity;
tmp_Velocity.y = 0;
velocity = tmp_Velocity.magnitude;//返回角色的移动速度
characterAnimator.SetFloat("Velocity", velocity, 0.25f, Time.deltaTime);//控制人物动画的播放
}
movementDirection.y -= Gravity * Time.deltaTime;//角色受重力下降的效果
characterController.Move(tmp_CurrentSpeed * Time.deltaTime * movementDirection);//控制角色的移动
}
/// <summary>
/// 下蹲方法
/// </summary>
/// <param name="_target"></param>
/// <returns></returns>
private IEnumerator DoCrouch(float _target)
{
float tmp_CurrentHeight = 0;
while (Mathf.Abs(characterController.height - _target) > 0.1f)
{
yield return null;
characterController.height =
Mathf.SmoothDamp(characterController.height, _target,
ref tmp_CurrentHeight, Time.deltaTime * 5);
}
}
}
人物行走状态的动画BlendTree
三:第三人称角色移动控制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class M_PlayerController : MonoBehaviour
{
private bool m_IsGrounded;//是否在地面上
private float m_GroundCheckDistance = 1.33f;//射线检测地面的距离 从射线发射位置到地面的距离再加0.1f(具体数值自行测试)
public float MoveSpeed = 10.0f; //移动速度
public float RotateSpeed = 10.0f; //旋转速度
private Rigidbody rig; //角色的刚体组件
private float h, v;
Vector3 c;//指向玩家单位向量
public Camera Camera_self;//相机
void Start()
{
rig = transform.GetComponent<Rigidbody>();
c = (transform.position - Camera_self.transform.position).normalized;
}
private void Update()
{
CheckGroundStatus();
Move();
if (Input.GetKeyDown(KeyCode.Space) && m_IsGrounded)//跳跃
{
rig.velocity = new Vector3(0, 5, 0);
}
}
/// <summary>
/// 人物移动
/// </summary>
public void Move()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
float inputMotionValue = Mathf.Max(Mathf.Abs(h), Mathf.Abs(v));
c = (transform.position - Camera_self.transform.position).normalized;//将角色的方向和相机的方向关联起来 从而达到旋转相机 人物也跟随旋转
string s = transform.localEulerAngles.ToString();//欧拉角
string[] sarr = s.Split(',');
sarr = sarr[0].Split('(');
float anglex = float.Parse(sarr[1]);//rotation的x值
c.y = Mathf.Tan((360 - anglex) * Mathf.Deg2Rad);//正切(弧度)
Vector3 cc = Quaternion.AngleAxis(90, Vector3.up) * c;//c绕y轴旋转90度
Vector3 forward = c * v + cc * h;
if (inputMotionValue > 0)
{
Quaternion lookRot = Quaternion.LookRotation(forward);
transform.rotation = Quaternion.Slerp(transform.rotation, lookRot, RotateSpeed * inputMotionValue * Time.deltaTime);
transform.Translate(Vector3.forward * inputMotionValue * MoveSpeed * Time.deltaTime);
}
}
/// <summary>
/// 检测地面
/// </summary>
void CheckGroundStatus()
{
RaycastHit hitInfo;
Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));
if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance))
{
m_IsGrounded = true;
}
else
{
m_IsGrounded = false;
}
}
}
四:第三人称相机控制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class M_CameraController : MonoBehaviour
{
public enum RotateType
{
UableRotate,
HorizontalOnly,
VerticalOnly,
Both
}
public enum UpdateType
{
LateUpdate,
FixedUpdate,
Update
}
public Transform target;
public Vector3 headOffset = new Vector3(0, 1.7f, 0);
public LayerMask ignoreLayer;
public UpdateType updateType;
public bool smooth;
[Range(0.1f, 1f)] public float smoothLevel = 0.5f;
public RotateType rotateType;
public float rotateSpeed = 10.0f;//旋转速度
public float zoomSpeed = 5f;//缩放速度
public float minDistance = 3.0f;
public float maxDistance = 15.0f;
public float maxLookUpAngle = 70.0f;
public float maxLookDownAngle = 30.0f;
float OriginDistance;
float currentDistance;
Vector3 direction;
Vector3 disOffset;
Vector3 visibleEuler;
// Use this for initialization
void Start()
{
ResetView();
}
// Update is called once per frame
void Update()
{
if (updateType == UpdateType.Update)
{
visibleEuler = ConvertEulerAngle(transform.rotation.eulerAngles);
float h = Input.GetAxis("Mouse X");
float v = Input.GetAxis("Mouse Y");
float zoom = Input.GetAxis("Mouse ScrollWheel");
Translate(zoom);
Rotate(h, v);
}
}
private void LateUpdate()
{
if (updateType == UpdateType.LateUpdate)
{
visibleEuler = ConvertEulerAngle(transform.rotation.eulerAngles);
float h = Input.GetAxis("Mouse X");
float v = Input.GetAxis("Mouse Y");
float zoom = Input.GetAxis("Mouse ScrollWheel");
Translate(zoom);
Rotate(h, v);
}
}
private void FixedUpdate()
{
if (updateType == UpdateType.FixedUpdate)
{
visibleEuler = ConvertEulerAngle(transform.rotation.eulerAngles);
float h = Input.GetAxis("Mouse X");
float v = Input.GetAxis("Mouse Y");
float zoom = Input.GetAxis("Mouse ScrollWheel");
Translate(zoom);
Rotate(h, v);
}
}
/// <summary>
/// 控制相机的移动
/// </summary>
/// <param name="zoom"></param>
void Translate(float zoom)
{
OriginDistance -= zoom * zoomSpeed;
OriginDistance = Mathf.Clamp(OriginDistance, minDistance, maxDistance);//限制缩放的范围
RaycastHit hit;
if (Physics.Raycast(target.position + headOffset, transform.position - target.position - headOffset, out hit, OriginDistance,
~ignoreLayer, QueryTriggerInteraction.Ignore))//检测层依据需求更改
{
currentDistance = hit.distance;
}
else
{
currentDistance = OriginDistance;
}
disOffset = direction.normalized * currentDistance;
if (smooth && rotateType == RotateType.Both)//平滑移动,但累计差会导致相机逐渐回到某个特定视角,且伴随有轻微抖动
transform.position = Vector3.Lerp(transform.position, target.position + headOffset + disOffset, smoothLevel);
else
transform.position = target.position + headOffset + disOffset;//旋转时伴随轻微卡顿
transform.LookAt(target.position + headOffset);
disOffset = Vector3.zero;
}
/// <summary>
/// 控制相机绕角色旋转
/// </summary>
/// <param name="h">鼠标左右方向移动数值</param>
/// <param name="v">鼠标前后方向移动数值</param>
void Rotate(float h, float v)
{
if (rotateType != RotateType.UableRotate)
{
float finallyRotateV = v * rotateSpeed;
if (finallyRotateV + visibleEuler.x >= maxLookUpAngle)
{
finallyRotateV = visibleEuler.x + finallyRotateV - maxLookUpAngle;
}
else if (finallyRotateV + visibleEuler.x <= -maxLookDownAngle)
{
finallyRotateV = visibleEuler.x + finallyRotateV + maxLookDownAngle;
}
if (rotateType == RotateType.Both)
{
//左右旋转
transform.RotateAround(target.position + headOffset, transform.up, h * rotateSpeed);
//上下旋转
transform.RotateAround(target.position + headOffset, transform.right, -finallyRotateV);
}
else if (rotateType == RotateType.HorizontalOnly)
{
transform.RotateAround(target.position + headOffset, Vector3.up, h * rotateSpeed);
}
else if (rotateType == RotateType.VerticalOnly)
{
transform.RotateAround(target.position + headOffset, transform.right, -finallyRotateV);
}
transform.LookAt(target.position + headOffset);//相机一直朝着主角望去
direction = (transform.position - target.position - headOffset).normalized;
}
}
public float ConvertAngle(float value)
{
float angle = value - 180;
if (angle > 0)
return angle - 180;
return angle + 180;
}
public Vector3 ConvertEulerAngle(Vector3 euler)
{
return new Vector3(ConvertAngle(euler.x), ConvertAngle(euler.y), ConvertAngle(euler.z));
}
/// <summary>
/// 初始化相机位置
/// </summary>
public void ResetView()
{
if (minDistance > maxDistance)//如果最小距离大于最大距离 则将最小最大距离互换
{
maxDistance += minDistance;
minDistance = maxDistance - minDistance;
maxDistance -= minDistance;
}
if (rotateType == RotateType.VerticalOnly || rotateType == RotateType.Both) direction = -target.forward;
else direction = new Vector3(1, 1, -1).normalized;
disOffset = direction * (minDistance + maxDistance) / 2;
transform.position = target.position + headOffset + disOffset;
OriginDistance = disOffset.magnitude;
disOffset = Vector3.zero;
}
public void SetTarget(Transform new_target)
{
target = new_target;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawLine(target.position + headOffset, transform.position);
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(target.position + headOffset, minDistance);
Gizmos.DrawLine(target.position + headOffset + target.forward * minDistance, target.position + headOffset + target.forward * maxDistance);
Gizmos.DrawLine(target.position + headOffset - target.forward * minDistance, target.position + headOffset - target.forward * maxDistance);
Gizmos.DrawLine(target.position + headOffset + target.up * minDistance, target.position + headOffset + target.up * maxDistance);
Gizmos.DrawLine(target.position + headOffset - target.up * minDistance, target.position + headOffset - target.up * maxDistance);
Gizmos.DrawLine(target.position + headOffset + target.right * minDistance, target.position + headOffset + target.right * maxDistance);
Gizmos.DrawLine(target.position + headOffset - target.right * minDistance, target.position + headOffset - target.right * maxDistance);
Gizmos.DrawWireSphere(target.position + headOffset, maxDistance);
}
}
五: 第三人称相机跟随方法二
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyFollowCamera : MonoBehaviour
{
public float distanceAway=10; // z轴上的距离
public float distanceUp=5; // 高度距离
public float smooth=5; // 移动平滑值
private GameObject hovercraft; // to store the hovercraft
private Vector3 targetPosition; // 相机目标位置
Transform follow;//跟随物体
void Start()
{
//需要跟随的物体
follow = GameObject.FindWithTag("Player").transform;
}
void LateUpdate()
{
//目标位置,由跟随物体的坐标+设置的高度+z轴的距离
//减去follow.forward * distanceAway 为距离物体后背的距离 关于follow.forward的用法可以查看Transform.forward文章
targetPosition = follow.position + Vector3.up * distanceUp - follow.forward * distanceAway;
//从当前位置到目标位置 平滑过渡
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * smooth);
//相机朝向
transform.LookAt(follow);
}
}