跳到主要内容
  1. Blogs/
  2. 文档/

Unity中的角色运动控制

·1507 字·4 分钟·

记录 Unity 中控制角色与物体的移动,旋转,插值操作。

角色控制 #

在 Unity 中,角色控制的主要方法有两种,一个是使用 Character Controller,另一个是使用 RigidBody。

Character Controller 包装了一个基本的角色控制器,使用简单,包含移动,地面检测等功能,能够精确移动。但是无法进行物理交互。

RigidBody 可以进行物理交互,如摩擦,弹性等,但是所有功能都需要自己实现。

CharacterController #


常用的包装好的功能

  • Move

    Move这个方法它的实际作用和Transform组件中的translate几乎一样,它计算速度是以帧计算的。

  • SimpleMove

    具备了“重力”,而且移动的时候,它时以秒为单位的。

  • velocity

  • isGround,判断是否处于地面,不推荐使用(连官方的案例都不用这个)。

Rigidbody #


Rigidbody 等物理相关的调用在 FixedUpdate 中,否则会出现帧率问题,因为逻辑帧每帧时间不一定,而物理帧时间每帧固定为 0.02s。

改变速度有两种形式,一是给刚体添加力,二是直接改变刚体速度。根据不同的游戏去使用不同的控制方式,不要死板的使用一种。如果你需要对速度有把控,那么使用 velocity,如果像是 AngryBird 这种或者实现玩家在太空中的一种感觉,那么使用 AddForce。

GMTK 中的 PlatformToolkitDemo 就很巧妙地改变速度,但同时也能让 AddForce 运作。他的做法是先读取 Velocity 然后在速度地基础上做运算。相当于修改加速度,而非速度本身。

DawnosaurDev 中的移动 platformer-movement 则是直接使用 AddForce 。

Velocity #

可以直接修改或者获取物体的速度矢量。

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));

Move相关 #

public void Move(Vector3 position, Quaternion rotation);
// 将物体移动到指定位置并旋转到指定方向
// 瞬间移动, 瞬间旋转

//-------- 其他类似函数
//MovePosition
public void MovePosition(Vector3 position);
// 瞬间移动

//MoveRotation
public void MoveRotation(Quaternion rot);
// 瞬间旋转

运动变换 #

插值与移动 #


Lerp #

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 #

Slerp 是球面线性插值

插值的角速度是匀速的,弧度则是按照百分比。

MoveTowards #

匀速变换,当前值移动到指定值,

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);

SmoothDamp #

固定时间内的平滑运动

transform.Translate #

transform.position #

直接修改物体位置,或者获取物体位置

协程+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 的方向

插值算法 —— Lerp, NLerp, SLerp

Controller vs Rigidbody

物体移动的几种方式与表现