记录 Unity 中控制角色与物体的移动,旋转,插值操作。
在 Unity 中,角色控制的主要方法有两种,一个是使用 Character Controller,另一个是使用 RigidBody。
Character Controller 包装了一个基本的角色控制器,使用简单,包含移动,地面检测等功能,能够精确移动。但是无法进行物理交互。
RigidBody 可以进行物理交互,如摩擦,弹性等,但是所有功能都需要自己实现。
常用的包装好的功能
Move
Move这个方法它的实际作用和Transform组件中的translate几乎一样,它计算速度是以帧计算的。
SimpleMove
具备了“重力”,而且移动的时候,它时以秒为单位的。
velocity
isGround,判断是否处于地面,不推荐使用(连官方的案例都不用这个)。
Rigidbody 等物理相关的调用在 FixedUpdate
中,否则会出现帧率问题,因为逻辑帧每帧时间不一定,而物理帧时间每帧固定为 0.02s。
改变速度有两种形式,一是给刚体添加力,二是直接改变刚体速度。根据不同的游戏去使用不同的控制方式,不要死板的使用一种。如果你需要对速度有把控,那么使用 velocity,如果像是 AngryBird 这种或者实现玩家在太空中的一种感觉,那么使用 AddForce。
在
GMTK
中的PlatformToolkitDemo
就很巧妙地改变速度,但同时也能让 AddForce 运作。他的做法是先读取 Velocity 然后在速度地基础上做运算。相当于修改加速度,而非速度本身。
DawnosaurDev
中的移动 platformer-movement 则是直接使用 AddForce 。
可以直接修改或者获取物体的速度矢量。
给物体添加力,当已经计算好目标速度的加速度后,需要反推使用的力。由于 F = ma ,所以直接把力看成加速度即可。
public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force);
// 给物体添加某个方向的力
// 定义力的方式
/*
力的方式有四种:
1. ForceMode.Force
假设 deltaTime (DT) 等于仿真步长
将输入解释为力(以牛顿为单位),并通过 力的值 * DT / 质量 来改变速度
v += f * DT / m ,此公式符合 F = ma
2. ForceMode.Acceleration
将参数解释为加速度(以米/秒平方为单位),并通过 力 * DT 的值 更改速度
3. ForceMode.Impulse
瞬时力,将参数解释为脉冲(以牛顿每秒为单位),并通过 力/质量 值改变速度
4. ForceMode.VelocityChange
将参数解释为直接速度变化(以米/秒为单位),并通过 力值 更改速度
这四种方式的加速度都不变, 1 和 2 的区别是有没有考虑质量, 3 和 4同理。
对于第 4 个,不要被名字误导了,并不是直接改变速度,还是通过力直接改变加速度,相当于 F = a 。
*/
//-------- 其他类似函数
// 相比于 AddForce ,这个只是在本地坐标添加力的方向,而非世界坐标
public void AddRelativeForce(Vector3 force, ForceMode mode = ForceMode.Force);
// 在刚体表面?某个点上添加力,会影响到物体的受力和力矩
public void AddForceAtPosition(Vector3 force, Vector3 position,
ForceMode mode = ForceMode.Force);
// 爆炸力,只影响范围内的,并且受力逐渐衰减
public void AddExplosionForce(float explosionForce, Vector3 explosionPosition,
float explosionRadius, float upwardsModifier = 0.0f, ForceMode mode = ForceMode.Force));
public void Move(Vector3 position, Quaternion rotation);
// 将物体移动到指定位置并旋转到指定方向
// 瞬间移动, 瞬间旋转
//-------- 其他类似函数
//MovePosition
public void MovePosition(Vector3 position);
// 瞬间移动
//MoveRotation
public void MoveRotation(Quaternion rot);
// 瞬间旋转
Lerp 是线性插值
var test_f = 0f;
// 浮点数按照百分比插值
test_f = Mathf.Lerp(test_f, 10f, 0.1f);
var test_v = Vector3.right;
//向量按照每个维度进行百分比插值
test_v = Vector3.Lerp(test_v, Vector3.up, 0.1f);
Slerp 是球面线性插值
插值的角速度是匀速的,弧度则是按照百分比。
匀速变换,当前值移动到指定值,
var test_f = 0f;
// 浮点数按照增量插值
test_f = Mathf.MoveTowards(test_f, 10f, 0.1f);
var test_v = Vector3.right;
// 令: T向量= 目标向量(第二个参数) - 初始向量(初始值)
//向量按照 T向量,进行增量插值,每次增加 T向量*0.1f
test_v = Vector3.MoveTowards(test_v, Vector3.up, 0.1f);
固定时间内的平滑运动
直接修改物体位置,或者获取物体位置
协程+Lerp
transform.rotation 是物体旋转量的四元数。
在原来的角度基础上进行增量旋转
Vector3 inc_rot = new Vector3(20, 0, 0);
transform.Rotate(inc_rot); //在原来的角度上,再绕 x 轴旋转20度
使用左手坐标系
Vector3 target_rot = new Vector3(90, 0,0);
transform.rotation = Quaternion.Euler(target_rot); //以 x 轴为旋转轴,旋转 90 度
让物体的 z 轴朝向指定矢量方向
Vector3 target_dir = Vector3.one;
transform.forward = target_dir ;
使用四元数变换也可以实现同样的效果
Vector3 target_dir = Vector3.one;
transform.rotation = Quaternion.LookRotation(target_dir); //转到 target_dir 的方向