FPS游戏 | 实现移动功能
三种实现FPS Controller的区别
- Transform Translate 允许移动物理,但无物理碰撞
- Rigid body + Capsule Collider 符合物理学 不会鬼穿墙 无法滞空运动 可与Physics Object 交互
- Charactor Controller 不会鬼穿墙 提供的API相对多 无法与Physic objects互动 可以滞空运动(太空人) 提供多种运动效果例如 slopes steps 等
Rigid body + Capsule Collider
控制摄像头旋转 FPMouseLook.cs 跟main camera 绑定
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class FPMouseLook : MonoBehaviour
{
private Transform cameraTransform;
[SerializeField] private Transform characterTransform;
private Vector3 cameraRotation;
public float MouseSensitivity;
public Vector2 MaxMinAngle;
// Start is called before the first frame update
void Start()
{
cameraTransform = transform;
}
// Update is called once per frame
void Update()
{
var tmp_MouseX = Input.GetAxis("Mouse X");
var tmp_MouseY = Input.GetAxis("Mouse Y");
cameraRotation.x -= tmp_MouseY * MouseSensitivity;//MouseSensitivity=18
cameraRotation.y += tmp_MouseX * MouseSensitivity;//MouseSensitivity=18
cameraRotation.x = Mathf.Clamp(cameraRotation.x, MaxMinAngle.x, MaxMinAngle.y);
cameraTransform.rotation = Quaternion.Euler(cameraRotation.x, cameraRotation.y, 0);
characterTransform.rotation = Quaternion.Euler(0, cameraRotation.y, 0);
}
}
函数解析
Input.GetAxis
一、触屏类 1、Mouse X 鼠标沿屏幕X移动时触发 2、Mouse Y 鼠标沿屏幕Y移动时触发 3、Mouse ScrollWheel 鼠标滚轮滚动是触发 二、键盘类 1、Vertical 键盘按上或下键时触发 2、Horizontal 键盘按左或右键时触发
使用示例:
Input.GetAxis("Mouse Y");
返回值是一个数,正负代表方向
Mathf.Clamp
函数定义:
static function Clamp (value : float, min : float, max : float) : float
解释: 限制value
的值在min
和max
之间, 如果value
小于min
,返回min
。 如果value
大于max
,返回max
,否则返回value
控制降落 + 跳跃
FPMoveMent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FPMoveMent : MonoBehaviour
{
//自由落体速度
public float Speed;
public float grativty;
//跳跃
public float JumpHeight;
private Transform characterTransform;
private Rigidbody characterRigidbody;
private bool isGrounded = true;
// 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()
{
Debug.Log(isGrounded);
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);
Debug.Log("button: " + Input.GetButtonDown("Jump"));
if (Input.GetButtonDown("Jump"))
{
characterRigidbody.velocity = new Vector3(tmp_CurrentVelocity.x, CalculateJumpHeightSpeed(), tmp_CurrentVelocity.z);
}
}
characterRigidbody.AddForce(new Vector3(0, -grativty * characterRigidbody.mass,0));
}
private float CalculateJumpHeightSpeed()
{
return Mathf.Sqrt(2 * grativty * JumpHeight);
}
private void OnCollisionStay(Collision _other)
{
isGrounded = true;
}
private void OnCollisionExit(Collision _other)
{
isGrounded = false;
}
}
函数解析
OnCollisionStay()
OnCollisionStay与OnCollisionEnter的区别 OnCollisionStay();
- 两者之间处在碰撞状态下
- 两者之间有相对移动的情况下才触发。
FixedUpdate
MonoBehaviour.Update 更新 当MonoBehaviour启用时,其Update在每一帧被调用。
MonoBehaviour.FixedUpdate 固定更新
- 当MonoBehaviour启用时,其 FixedUpdate在每一帧被调用。
- 处理Rigidbody时,需要用FixedUpdate代替Update。例如:给刚体加一个作用力时,你必须应用作用力在FixedUpdate里的固定帧,而不是Update中的帧。(两者帧长不同)
- MonoBehaviour.LateUpdate 晚于更新
- 当Behaviour启用时,其LateUpdate在每一帧被调用。
- LateUpdate是在所有Update函数调用后被调用。这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。
- Update和FixedUpdate的区别:
- update跟当前平台的帧数有关,而FixedUpdate是真实时间,所以处理物理逻辑的时候要把代码放在FixedUpdate而不是Update.
- Update是在每次渲染新的一帧的时候才会调用,也就是说,这个函数的更新频率和设备的性能有关以及被渲染的物体(可以认为是三角形的数量)。在性能好的机器上可能fps 30,差的可能小些。这会导致同一个游戏在不同的机器上效果不一致,有的快有的慢。因为Update的执行间隔不一样了。
- FixedUpdate,是在固定的时间间隔执行,不受游戏帧率的影响。有点想Tick。所以处理Rigidbody的时候最好用FixedUpdate。
PS:FixedUpdate的时间间隔可以在项目设置中更改,Edit->ProjectSetting->time 找到Fixedtimestep。就可以修改了。
- Update和LateUpdate的区别
- 在圣典里LateUpdate被解释成一句话:LateUpdate是在所有Update函数调用后被调用。这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。这句我看了云里雾里的,后来看了别人的解释才明白过来。
- LateUpdate是晚于所有Update执行的。例如:游戏中有2个脚步,脚步1含有Update和LateUpdate,脚步2含有Update,那么当游戏执行时,每一帧都是把2个脚步中的Update执行完后才执行LateUpdate 。虽然是在同一帧中执行的,但是Update会先执行,LateUpdate会晚执行。
- 现在假设有2个不同的脚本同时在Update中控制一个物体,那么当其中一个脚本改变物体方位、旋转或者其他参数时,另一个脚步也在改变这些东西,那么这个物体的方位、旋转就会出现一定的反复。如果还有个物体在Update中跟随这个物体移动、旋转的话,那跟随的物体就会出现抖动。 如果是在LateUpdate中跟随的话就会只跟随所有Update执行完后的最后位置、旋转,这样就防止了抖动。
参考: physicalSystyme.md
移动 方案2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FPCharacterControllerMovement : MonoBehaviour
{
private CharacterController characterController;
private Transform characterTransForm;
private Vector3 movementDirection;
public float MovementSpeed;
public float Gravity = 9.8f;
public float JumpHeight;
private void Start()
{
characterController = GetComponent<CharacterController>();
// characterForm = GetComponent<Transform>();
characterTransForm = transform;
}
void Update()
{
Debug.Log(characterController.isGrounded);
//如果在地面 则返回 true
if (characterController.isGrounded)
{
var tmp_Horizontal = Input.GetAxis("Horizontal");
var tmp_Vertical = Input.GetAxis("Vertical");
movementDirection = characterTransForm.TransformDirection(new Vector3(tmp_Horizontal, 0, tmp_Vertical));
// characterTransForm.LookAt(characterTransForm.position + tmp_MovementDirection);
// characterController.SimpleMove(movementDirection * Time.deltaTime * MovementSpeed);
if (Input.GetButtonDown("Jump"))
{
movementDirection.y = JumpHeight;
}
}
movementDirection.y -= Gravity * Time.deltaTime;
characterController.Move(movementDirection * Time.deltaTime * MovementSpeed);//不具备重力算法
}
}
shift 奔跑 + 下蹲
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FPCharacterControllerMovement : MonoBehaviour
{
private CharacterController characterController;
private Transform characterTransForm;
private Vector3 movementDirection;
public float MovementSpeed; //移动速度
public float Gravity = 9.8f;
public float JumpHeight; //跳跃高度
public float SprintingSpeed = 8;
public float WalkSpeed = 4;
public float SprintingSpeedWhenCrouch = 4;//当下蹲时
public float WalkSpeedWhenCrouch = 2;//当下蹲时
public float CrouchHeight = 1f; //下蹲高度
private bool isCrouched; //是否下蹲
private float originHeight; //原来的高度
private void Start()
{
characterController = GetComponent<CharacterController>();
// characterForm = GetComponent<Transform>();
characterTransForm = transform;
originHeight = characterController.height;
isCrouched = false;
}
void Update()
{
Debug.Log(characterController.isGrounded);
float currentSpeed = WalkSpeed;
//如果在地面 则返回 true
if (characterController.isGrounded)
{
var tmp_Horizontal = Input.GetAxis("Horizontal");
var tmp_Vertical = Input.GetAxis("Vertical");
movementDirection = characterTransForm.TransformDirection(new Vector3(tmp_Horizontal, 0, tmp_Vertical));
// characterTransForm.LookAt(characterTransForm.position + tmp_MovementDirection);
// characterController.SimpleMove(movementDirection * Time.deltaTime * MovementSpeed);
if (Input.GetButtonDown("Jump"))
{
movementDirection.y = JumpHeight;
}
if (Input.GetKeyDown(KeyCode.C))
{
var tmpCrouchHeight = isCrouched?originHeight:CrouchHeight ;
StartCoroutine(DoCrouch(tmpCrouchHeight));
isCrouched = !isCrouched;
}
//shift奔跑
if (isCrouched)
{
currentSpeed = Input.GetKey(KeyCode.LeftShift) ? SprintingSpeedWhenCrouch : WalkSpeedWhenCrouch;
}
else
{
currentSpeed = Input.GetKey(KeyCode.LeftShift) ? SprintingSpeed : WalkSpeed;
}
}
movementDirection.y -= Gravity * Time.deltaTime;
characterController.Move(movementDirection * Time.deltaTime * currentSpeed);//不具备重力算法
}
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 );
}
}
}
--完--
- 原文作者: 留白
- 原文链接: https://zfunnily.github.io/2021/09/move/
- 更新时间:2024-04-16 01:01:05
- 本文声明:转载请标记原文作者及链接