让物体动起来,Unity的几种移动方式

一、前言

在大部分的Unity游戏开发中,移动是极其重要的一部分,移动的手感决定着游戏的成败,一个优秀的移动手感无疑可以给游戏带来非常舒服的体验。而Unity中有多种移动方法,使用Transform,使用刚体Rigidbody,使用CharacterController,使用NavMesh导航系统等等等等。当然,对于新手来说,最常见的莫过于使用Transform和Rigidbody这两种组件的移动方案。所以,这篇文章将就这两种移动方案进行分析讲解。

注意!!!以下代码均为2D场景,3D同理

二、Transform

Transform组件是GameObject的变换组件,可以操纵GameObject的位置(Position),大小(Scale),旋转(Rotation)等等。所以,使用Transform组件进行物体移动是一个非常不错的选择,以下是通过Transform组件实现的几种移动方式,以及对应的场景。

1.Translate

使用Transform的Translate函数可以在GameObject的本地坐标系下进行平移。可以传入一个位移向量作为参数,指定平移的方向和距离。
[SerializeField] private float moveSpeed; private void Update() { //自动向右移动 transform.Translate(Vector2.right * moveSpeed * Time.deltaTime); } //物体沿向量指向方向移动 //Vector2.right 向右移动向量,也可以写成自己定义的 //moveSpeed 移动速度,通常为float型

Translate方法在游戏中可以用作物体移动,适用简单的移动方式,例如箱子在平面上自动移动等等。

2.MoveTowards

使用Transform的MoveTowards函数可以实现直线移动到目标位置。可以传入当前位置、目标位置和移动速度来控制移动的速度和到达目标位置。

MoveTowards函数对应的三个参数分别为(当前位置,目标位置,移动速度),前两个为Vector类型,最后一个为float类型,也可以写成整型等。

例如:将物体移动到(5,5)的位置
[SerializeField] private float moveSpeed; private void Update() { transform.position = Vector2.MoveTowards(transform.position,new Vector2(5,5),moveSpeed); }

3.Lerp

使用Transform的Lerp函数可以实现平滑插值移动。可以传入起始位置、目标位置和插值比例来控制移动的过渡效果。

Lerp函数对应的三个参数分别为(当前位置,目标位置,插值比例),前两个为Vector类型,最后一个为float型=类型,插值比例范围是[0,1],当lerp取0时,物体不移动,lerp取1时,物体直接移动到目标位置,lerp取值越大,物体移动越快。

例如:将物体移动到(5,5)
[SerializeField] private float moveSpeed; [SerializeField] private float lerp; private void Update() { transform.position = Vector3.Lerp(transform.position, new Vector2(5,5), lerp); }

接下来,将讲解一下Lerp函数的移动原理:

插值系数lerp本质上是物体每次移动距离与物体当前位置到目标位置的比值,物体每次移动后,都会重新重置下一步移动距离,但是比例不变,也就是说,物体朝目标点移动,每次移动的距离都会变短。听起来非常绕口对吧,下面我们用一幅图来讲解一下这个原理。

红色竖线为第一次移动到的位置,那么它的移动距离L1=S1lerp,蓝色竖线第二次移动到的位置,那么第二次移动的距离L2=S2 lerp,同理,L3=S3lerp。由图可知,物体每次移动的距离都在缩短,但是,它们每次移动的距离与当前位置到目标位置的距离的比值不变。并且,我们也可以发现,lerp值越大,单次移动距离越大,即速度越快,相反,lerp越小,单词移动距离也就越小。最后,我们不难发现,在Lerp函数中,物体移动的距离永远是当前位置到目标位置的距离 lerp,也就是说,物体永远不可能到达目标位置,只会无限接近目标位置。所以,为了使物体可以到达目标位置,我们可以添加一个if条件,当物体的目标位置的距离小于某一值时,物体位置变为目标位置。
if (Vector2.Distance(transform.position, new Vector2(5, 5))<0.1f) { transform.position = new Vector2(5, 5); }

以上便是使用Transform移动物体的几种方案,当然使用Transform组件移动物体的方案有很多种形式,具体可以自行探索。

当然,使用Transform组件移动物体有时会出现一个小小的bug,我们将在Rigidbody中说明。

三、Rigidbody

Rigidbody,刚体组件,在这个组件中,我们可以使用物理学的定义进行物体移动等操作。并且,这也是最经常用的操控玩家移动的组件,。当然刚体组件不仅仅只用来移动GameObject,还有很多操作,在这里,我们只讲移动方面的使用。

上文说了,Transform有一个小小的bug,那就是会引起穿模,也就是说,物体在进行移动时,碰到障碍物继续移动,会导致穿过障碍物,这是一个致命的bug。但是,刚体组件就可以很好的解决这个bug。在这里,我查阅了一些资料,大致便是,Transform组件是位置的改变,也就是一次一次的发生位置变化,也就是相当于每次移动都是一段瞬移闪现,第一时间在一个位置,下一时间又瞬移到下一个位置,这样的话,在和障碍物进行挤压时,就极其容易导致物体和障碍物发生交叉,导致碰撞体检测出现异常,从而导致穿模,而刚体组件相当于拉着物体移动,就不存在这样的bug。对这方面感兴趣可以查阅相关资料。

下面继续讲解Rigidbody组件控制GameObject移动。

1.AddForce

使用AddForce函数给刚体施加力来移动物体,想要朝哪个方向移动,就在哪个方向添加力。

AddForce函数的参数为AddForce(方向向量 * 力的大小);
[SerializeField] private float force; private Rigidbody2D rigidbody2D; private void Start() { //获取挂载脚本的物体的刚体组件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //向上施加一个大小为force的力 rigidbody2D.AddForce(Vector2.up * force); }

2.MovePosition

MovePosition函数可以直接设置物体的位置。

MovePosition函数的参数为MovePosition(位置(例如tramsform.position))

下面的代码是物体每次向右闪现/瞬移speed的长度,注意,这个方法也有可能导致穿模
[SerializeField] private float speed; private Rigidbody2D rigidbody2D; private void Start() { //获取挂载脚本的物体的刚体组件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //向右移动,2D中为向右/前,X轴正方向 rigidbody2D.MovePosition(transform.position + Vector3.right * speed * Time.deltaTime); }

当然,也可以直接只填入目标位置,使得物体闪现到指定目标位置
[SerializeField] private Transform targetTransform; private Rigidbody2D rigidbody2D; private void Start() { //获取挂载脚本的物体的刚体组件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //传送到targetTransform的位置 rigidbody2D.MovePosition(targetTransform.position); }

3.velocity

首先说明的是velocity不是函数,而是一个参数,也就是物体的速度。

所以我们通过对物体将要移动的方向上添加速度,也就可使物体超指定方向以固定的速度进行移动。
[SerializeField] private float moveSpeed_X; [SerializeField] private float moveSpeed_Y; private Rigidbody2D rigidbody2D; private void Start() { //获取挂载脚本的物体的刚体组件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //水平方向 float horizontal = Input.GetAxis("Horizontal"); //竖直方向 float vertical = Input.GetAxis("Vertical"); rigidbody2D.velocity=new Vector2 (horizontal*moveSpeed_X*Time.deltaTime, vertical* moveSpeed_Y * Time.deltaTime); //也可以只改变x或y的值 rigidbody2D.velocity = new Vector2(horizontal * moveSpeed_X * Time.deltaTime, rigidbody2D.velocity.y); rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, vertical * moveSpeed_Y * Time.deltaTime); }

四、结尾

以上便是几种简单的物体移动方式,当然使物体移动的方法有很多种,这里只列举了几种,感兴趣的小伙伴可以深究一下。