Welcome to my blog garden🍺|

清风扶云雾

园龄:1年10个月粉丝:2关注:0

2023-10-27 18:56阅读: 3092评论: 2推荐: 0

让物体动起来,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=S2lerp,同理,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);
}

四、结尾

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

本文作者:清风扶云雾

本文链接:https://www.cnblogs.com/CloudAndMist/p/17793002.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   清风扶云雾  阅读(3092)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 起风了 买辣条也用券
  2. 2 年少有为 李荣浩
  3. 3 我爱你不问归期 白小白
  4. 4 我走后 小咪
  5. 5 热铁皮房顶的夏天 王赫野
  6. 6 Dear John 比莉
我爱你不问归期 - 白小白
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

我爱你不问归期

词:林华勇/周洁颖

曲:林华勇

演唱:白小白

编曲:陆泰名

制作人:林华勇

吉他:陆泰名

贝斯:楚小波

鼓:祁大为

监制:周晓明

混音:周晓明

是想念如你温柔过境

才发现原来花开都有声音

只要你在我生命途经

再不怕时光匆匆如旅

是幸福在我耳边低语

才忘了寒风不曾停下足迹

直到我走遍半生四季

才懂得风景都不及你

我爱你

就像风走了千万里从不问归期

像太阳升了落去无论朝夕

我爱你

就像云漂了千万里都不曾歇息

像白雪肆虐大地茫茫无际

我爱你

就像飞蛾扑火那样的无所畏惧

像故时黄花堆积风吹不去

我爱你

就像江水连绵不绝永不会停息

像荒原野草重生燃之不尽

是想念如你温柔过境

才发现原来花开都有声音

只要你在我生命途经

再不怕时光匆匆如旅

是幸福在我耳边低语

才忘了寒风不曾停下足迹

直到我走遍半生四季

才懂得风景都不及你

我爱你

就像风走了千万里从不问归期

像太阳升了落去无论朝夕

我爱你

就像云漂了千万里都不曾歇息

像白雪肆虐大地茫茫无际

我爱你

就像飞蛾扑火那样的无所畏惧

像故时黄花堆积风吹不去

我爱你

就像江水连绵不绝永不会停息

像荒原野草重生燃之不尽

我爱你

就像江水连绵不绝永不会停息

像荒原野草重生燃之不尽

未经许可,不得翻唱或使用