当前位置: 首页 > news >正文

[Unity] 良好手感的人物移动速率计算

我们先来看这个主要的计算移动的函数

protected override void OnStep(Player player){// 获取当前输入的移动方向(相对于相机)var inputDirection = player.inputs.GetMovementCameraDirection();// 判断是否有输入if (inputDirection.sqrMagnitude > 0){// 获取输入移动方向相对于当前移动方向的偏移量var dot = Vector3.Dot(inputDirection, player.lateralVelocity);// 这里是触发制动状态的条件判断,下面画图的时候进行讲解if (dot >= player.stats.current.brakeThreshold){player.Accelerate(inputDirection);player.FaceDirectionSmooth(player.lateralVelocity);} else{player.states.Change<BrakePlayerState>();}}}

首先根据注释我们讲这个计算速率分成几个步骤

  1. 通过InputSystem获取Vecotr2输入值算出当前输入相对于摄像机的移动方向
  2. 在非制动状态下通过Accelerate(Vector3 direction)方法计算速率
  3. 平滑转向指定方向

进入函数

获取方向

// 这个函数的作用是:获取玩家的输入方向,并将其转换为相对于摄像机朝向的方向。
public virtual Vector3 GetMovementCameraDirection(){// 获取移动方向var direction = GetMovementDirection();// 由于direction返回的是一个Vector3,这里就取模长if (direction.sqrMagnitude > 0){// 这里计算出相机在Y轴上的偏航角度(Yaw)var rotation = Quaternion.AngleAxis(m_camera.transform.eulerAngles.y, Vector3.up);// 根据摄像机的朝向修改人物的移动方向,例如人物现在按W,摄像机看向相对右手边,此时人物不是绝对移动向前,而是向摄像机的偏航角方向移动direction = rotation * direction;// 归一化,只取方向direction = direction.normalized;}return direction;}
public virtual Vector3 GetMovementDirection(){if (Time.time < m_movementDirctionUnlock){return Vector3.zero;}// 从InputSystem里获取Vector2var value = m_movement.ReadValue<Vector2>();return GetAxisWithCrossDeadZone(value);}
public virtual Vector3 GetAxisWithCrossDeadZone(Vector2 axis){var deadZone = InputSystem.settings.defaultDeadzoneMin;// Abs仅仅判断输入的值是否大于死区的值axis.x = Mathf.Abs(axis.x) > deadZone ? RemapToDeadzone(axis.x, deadZone) : 0;axis.y = Mathf.Abs(axis.y) > deadZone ? RemapToDeadzone(axis.y, deadZone) : 0;// 这里仅控制平面移动return new Vector3(axis.x, 0, axis.y);}
// 死区的值如果设置成0.2,当ABS==0.21时,如果不调用这个方法则启动时输入的值就是0.21,会非常的突兀
// 这里除法的作用就是归一化,防止出现速度比预期少,例如输入value为1,没有除法的话最大速度就是(value - deadzone)
private float RemapToDeadzone(float value, float deadzone) => (value - deadzone) / (1 - deadzone);

计算速率

有了Vector3方向之后我们就可以进行速率的计算了

让我们进入Accelerate方法当中去

public virtual void Accelerate(Vector3 direction){// 转向时的摩擦力var turningDrag = isGrounded && inputs.GetRun()? stats.current.runningTurnningDrag: stats.current.turningDrag;// 速率var acceleration = isGrounded && inputs.GetRun()? stats.current.runningAcceleration: stats.current.acceleration;// 最快速度var topSpeed = inputs.GetRun()? stats.current.runningTopSpeed: stats.current.topSpeed;// 最终得到的速率var finalAcceleration = isGrounded ? acceleration : stats.current.airAcceleration;Accelerate(direction, turningDrag, finalAcceleration, topSpeed);}
public virtual void Accelerate(Vector3 direction, float turningDrag, float acceleration, float topSpeed){if (direction.sqrMagnitude > 0){// 代表“我当前的速度(lateralVelocity),有多‘顺着’我想去的新方向(direction)”。// 如果数字很大很正:非常顺路。// 如果数字是0:正好垂直,不顺路也不逆路。// 如果数字是负数:完全是反方向,在“开倒车”。// speed 的初始值,就是你当前速度 lateralVelocity 在你想去的新方向 direction 上的“贡献值”。var speed = Vector3.Dot(direction, lateralVelocity);// direction 是一个只有方向没有大小的“路标”,speed 是一个只有大小没有方向的“油门大小”。两者一乘,就得到了一个既有正确方向,又有合适大小				  的速度向量。这就是我们分解出的“好速度”。var velocity = direction * speed;// 坏速度,需要被摩擦力抵消的速度var turningVelocity = lateralVelocity - velocity;var turiningDelta = turningDrag * turningDragMultiplier * Time.deltaTime;// 当前能达到的最大移速var targetTopSpeed = topSpeed * topSpeedMultiplier;// 如果当前速度未达到最高移速或者速度小于0if (lateralVelocity.magnitude < targetTopSpeed || speed < 0){// 持续加速speed += acceleration * acclerationMultiplier * Time.deltaTime;// 限制速度,在当前可达到最大速度区间speed = Mathf.Clamp(speed, -targetTopSpeed, targetTopSpeed);}// 当前的移动向量(这里不仅需要方向,而且要有大小了)velocity = direction * speed;// 每次调用方法将当前的坏速率向量慢慢消除turningVelocity = Vector3.MoveTowards(turningVelocity, Vector3.zero, turiningDelta);// 将当前的速率更新为计算过后的速率lateralVelocity = velocity + turningVelocity;}}

这里Dot之前一直存在一个误区,单纯把Dot拿来当作判断位置的值,但其实dot值的大小还是受到模长的影响,并非只有[-1,1]

  • 它的正负号 依然告诉我们方向关系(前方(>0)还是后方(<0)或者是垂直(=0))。
  • 它的绝对值 告诉我们“有多顺路”或“有多逆路”,这个值的大小直接受到 lateralVelocity 模长的影响。
http://www.vanclimg.com/news/1793.html

相关文章:

  • 比特彗星常见问题-用户列表显示问题
  • 「补档」 像素帝的比特彗星教程
  • 《春王正月》
  • 数学积累(强基2 例65~82)
  • 新蛋白标注流程
  • 比特彗星常见问题-设置视频预览播放器
  • 开发AppleScript时查看程序UI元素的工具
  • Hyperlane框架的高级特性深度解析:从零拷贝到宏系统的完美融合(7601)
  • 实战项目:文件分块上传系统(2069)
  • Web服务器性能大比拼:谁才是真正的速度之王(0106)
  • Hyperlane性能调优秘籍:从毫秒级响应到百万QPS的优化之路(5333)
  • TCP连接优化的实战经验(0513)
  • 计算几何
  • JAVA语言学习总结(第28天)
  • GTMKubeJS轮子和技巧分享
  • OI集训 Day13
  • 格式化字符串
  • Java学习Day29
  • 待办
  • 20250729 沪锡
  • 内核模块支持
  • 最大公约数最小公倍数与周期
  • LLM的参数量是什么意思
  • 平衡树Splay - AC
  • 7.15-7.28软路由搭建
  • 2025.7.29
  • 电脑接入麦克风设置
  • Windows平台Microsoft Edge关闭指定快捷键方法
  • 20250729
  • 零代码、零门槛、零成本:企业数字化的五个新选择