unity 动画抖动2D人物持续移动时背景抖动怎么解决

最近研究Unity3d,2d寻路的实现。所以又一次涉及到了角色坐标位移的问题。系统的对于这个简单问题进行整理和总结。本来就是一个简单的几何问题,结果发现已经有两个小坑,顺便填上,这里做下总结。
需求:通过鼠标点击,控制2d角色移动,就是点哪里,角色向移动到哪里
问题分解:按照时间进行动画分解,鼠标输入(动画开始)、平移(动画进行)、移动结束(动画结束)
前提:这里前面的文章基本解决了一些基础的知识,比如IO获取(鼠标输入),移动的基本方式(Unity中的位置系统transform)
坑:1、平移中的平滑移动,2、如何确定移动了目标点,并使物体停止下来
坑1:平移中的平滑移动
补充知识,关于角色的平移和位置更新,Unity无非就几种方式
A、transform.Translate(new Vector3(1, 1, 1) * moveSpeed * Time.deltaTime); // Translate方法移动不会考虑刚体等碰撞(会直接穿过物体)
// 确保我们的速度不会超过maxDistanceDelta B、Vector3.MoveTowards(transform.position, targetPos.position, speed * Time.deltaTime);
// 速度会超过移动速度,像弹簧一样 C、Vector3.Lerp(transform.position, targetPos.position, speed * Time.deltaTime);
D、直接设置transform.Positon,最简单的方式
这个坑,真是坑了很多很多人,目前网上一半以上的教程,从严格意义上都是错误的,这里真的想吐槽一下(太他妈不负责了),这个问题我在群里问过一次,结果还被怀疑是菜鸟,其实焦点还是 我用红色标出的这个线性插值函数,其实简单的不得了,就是个直线方程。这里可以参考,以下这两篇文章
分析为什么&速度会超过移动速度,像弹簧一样&和 线性插值的函数,后来我仔细想了想,其实还是自己知识掌握的不够透彻,具体我们了解以后分析下,经典教程中的函数
&&& public float moveS&&& public float turnS
&&& private Vector3 moveD&&& // Use this for initialization&&& void Start () {&&&&&&& moveDirection = Vector3.&&& }&&& &&& // Update is called once per frame&&& void Update () {
&&&&&&& // 1&&&&&&& Vector3 currentPosition = transform.&&&&&&& // 2&&&&&&& if( Input.GetButton("Fire1") ) {&&&&&&&&&&& // 3&&&&&&&&&&& Vector3 moveToward = Camera.main.ScreenToWorldPoint( Input.mousePosition );&&&&&&&&&&& // 4&&&&&&&&&&& moveDirection = moveToward - currentP&&&&&&&&&&& moveDirection.z = 0; &&&&&&&&&&& moveDirection.Normalize();&&&&&&& }
&&&&&&& Vector3 target = moveDirection * moveSpeed + currentP&&&&&&& transform.position = Vector3.Lerp( currentPosition, target, Time.deltaTime );&&& }}
这里我们看红色部分的文字,这里之所以不会出现弹簧移动的效果,主要是每次插值都是当前点和这帧将要移动点的位置的插值,其实这里根本没有必要 ,直接设置 transform.position = moveDirection * moveSpeed*Time.deltaTime + currentPosition;(其实本身就是一个 基于时间的线性移动)
还有 本身 Vector3.Lerp(transform.position, targetPos.position, speed * Time.deltaTime); 这么用就有很大的问题
A、speed * Time.deltaTime 当speed设置很大而帧率很低的时候这个系数可能全是1,这样根本就是不插值,
B、当用UGUI时坐标系统是屏幕坐标值很大,这样插值会很不准(这也是我曾经问过的问题,不过没有人回答我)
至此第一个坑填上了,下面我列出使用不同方式来进行移动的相关代码
第一种,改进型插值移动
/// &summary&
/// 使用Vector3的插值进行更新位置
/// &/summary&
private void MoveByVector3Lerp()
//1、获得当前位置
Vector3 curenPosition = this.transform.
//2、获得方向
if (Input.GetButton("Fire1"))
Vector3 moveToward = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moveTowardPosition = moveT
moveTowardPosition.z = 0;
moveDirection = moveToward - curenP
moveDirection.z = 0;
moveDirection.Normalize();
var distance = Vector3.Distance(curenPosition, moveTowardPosition);
// Debug.Log(string.Format("curenPosition:{0}, moveTowardPosition{1},distance:{2},speed:{3}", curenPosition, moveTowardPosition, distance, speed * Time.deltaTime));
if (distance & 0.01f)
transform.position = moveTowardP
//3、插值移动
//目标位置方向加上速度移动
Vector3 target = moveDirection*speed*Time.deltaTime + curenP
target.z = 0;
transform.position =
第二种,MoveTowards进行移动更新
/// &summary&
/// 使用Vector3的MoveTowards 直接进行位置更新
/// &/summary&
private void MoveByVector3MoveTowards()
//1、获得当前位置
Vector3 curenPosition = this.transform.
//2、获得方向
if (Input.GetButton("Fire1"))
moveTowardPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moveTowardPosition.z =0;
if (Vector3.Distance(curenPosition, moveTowardPosition) & 0.01f)
transform.position = moveTowardP
//3、插值移动
//距离就等于 间隔时间乘以速度即可
float maxDistanceDelta = Time.deltaTime *
transform.position = Vector3.MoveTowards(curenPosition, moveTowardPosition, maxDistanceDelta);
第三种,transform.Translate
/// &summary&
/// 使用Vector3的Translate 直接进行位置更新
/// &/summary&
private void MoveByTransformTranslate()
//1、获得当前位置
Vector3 curenPosition = this.transform.
//2、获得方向
if (Input.GetButton("Fire1"))
moveTowardPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moveTowardPosition.z = 0;
moveDirection = moveTowardPosition - curenP
moveDirection.z = 0;
moveDirection.Normalize();
//3、插值移动
Vector3 target = moveDirection * speed * Time.deltaTime + curenP
target.z = 0;
if (Vector3.Distance(curenPosition, moveTowardPosition) & 0.01f)
transform.position = moveTowardP
transform.Translate(target - curenPosition);
坑2:如何确定移动了目标点,并使物体停止下来
补充知识:其实坑1中列出的三种平移方法,其实并不是什么套路,不是什么标准的动画移动方式,虽然他们也是基于时间的,只能归纳成一种简单的顺序帧移动,这里我查了很多资料还有一种基于时间线的移动方式。
问题描述:这里先说下坑2是怎么回事,就是我们希望角色移动到鼠标点击的点以后停下来,结果发现停不下来,通过调试日志主要的问题在这一行(这也是我以前提出过的一个问题,但无人解答)
if (Vector3.Distance(curenPosition, moveTowardPosition) & 0.01f)
实际上这行代码非常不靠谱,至少有两点
A、单位差异,UGUI中是屏幕坐标也是localPositon像素,Native中是Unit两个单位不同判断的这个距离常量不一样
B、由于speed * Time.deltaTime 每帧移动的距离是与速度和帧率有关的,这个常量(0.01)必须与之匹配需要设置合理的值
C、使用插值计算3维坐标误差会扩大,这里我用&第一种,改进型插值移动&,&第三种,transform.Translate&都出现了误差较大的情况,而&第二种,MoveTowards进行移动更新&,就很准确。
所以系统给出的函数
Vector3.MoveTowards(curenPosition, moveTowardPosition, maxDistanceDelta);
不是白给的,这也是很多人推荐使用这个函数的原因(但不告诉我们为什么)
最后给出我自己写的基于时间线的位移实现
/// &summary&
/// 鼠标点击移动,目标点
/// &/summary&
private Vector3 moveTowardPosition = Vector3.
private Vector3 moveStartPosition = Vector3.
private float totalTime = 0.0f;
private float costTime = 0.0f;
private float timePrecent = 0.0f;
private bool _isRuning =
/// &summary&
/// 是否正在移动
/// &/summary&
public bool IsRuning
get { return _isR }
set { _isRuning = }
private void MoveByTimeline()
* 获得移动的最终目标位置,根据移动速度获得一共需要移动的时间 totalTime
* 每一帧,
1、累加 已经逝去的时间,并得到costTime,并获得移动的百分比 precent = costTime/totalTime
2、获得当前精灵的位置,根据precent 进行位置插值,得到这一帧应该移动的位置
3、使用设置移动
4、通过precent判断是否&1 来判断是否移动到了目标位置
5、如果完成,则调用最后一次移动实现,终点移动误差,并置为一些标志位
//获得当前位置
Vector3 curenPosition = this.transform.
if (Input.GetButton("Fire1"))
moveStartPosition = curenP
//获得移动终点位置
moveTowardPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moveTowardPosition.z = 0;
costTime = 0.0f;
//计算记录
var subVector3 = moveTowardPosition - curenP
//计算需要移动的总时间
totalTime = subVector3.magnitude /
_isRuning =
//如果已经移动
if (_isRuning)
//如果时间百分比小于1 说明还没有移动到终点
if (timePrecent & 1)
//累加时间
costTime += Time.deltaT
timePrecent = costTime/totalT
Vector3 target = Vector3.Lerp(moveStartPosition, moveTowardPosition, timePrecent);
transform.position =
else //大于或者等于1 了说明是最后一次移动
transform.position = moveTowardP
_isRuning =
moveTowardPosition = Vector3.
timePrecent = 0.0f;
costTime = 0.0f;
这种方法基本排除了,移动到终点的位移误差问题,缺点是使用的临时变量较多(我不喜欢),而&第二种,MoveTowards进行移动更新&可以基本不使用临时变量。时间线动画实际上这也是一些小的平移组件及itween的核心原理(为什么,还需要进一步探索,也许扩展性更强)
反正被坑很不爽,不过也怪不了别人,还是自己才疏学浅(不是天才,就使劲干)。下一篇 继续探索角色的系列目标点的移动
阅读(...) 评论()unity2d/3d(21)
一般是添加box collider2D组件后 我们再给人物添加刚体组件,那么人物移动就用rigidbody2D.AddForce,撞击墙体是不会有抖动现象的。因为这种移动方式是带有物理特性的很难做到匀速移动,有时候我们移动是直接用transform.Translate,这是一种直接改变坐标的移动方式,当撞击collider的时候就产生了矛盾,一边要移动一边又受到collider影响要弹出来,所以就不停的抖动。
想到这样的解决方法
bool isWall=false
bool moveLeft=
bool moveRight=`
void FixedUpdate(){
if(Input.GetKey(KeyCode.D)){
force_move_right=300f;
moveLeft=false;
if(isWall&&moveRight){
force_move_right=0;
}else{transform.Translate(Vector2.right*Time.deltaTime*force_move_right);
moveRight=true;
if (Input.GetKey (KeyCode.A)) {
force_move_left=300f;
moveRight=false;
if(isWall&&moveLeft){
force_move_left=0;
}else{transform.Translate(-Vector2.right*Time.deltaTime*force_move_left);
moveLeft=true;
当然后面碰撞体也添加一下。
public void OnCollisionEnter2D(Collision2D tag){
if(tag.collider.tag=="Floor"){//这个是之前///做的
isGround =
if(tag.collider.tag=="Wall"){
public void OnCollisionExit2D(Collision2D tag){
if(tag.collider.tag=="Floor"){
isGround =
if(tag.collider.tag=="Wall"){
发现之后继续再怎么改动,还是没有办法完美的实现,总是会出现这样那样的问题,所以还是放弃了。
有老老实实的回到addforce
float h = Input.GetAxis ("Horizontal");
float v = Input.GetAxis ("Vertical");
if(h&0.05f){
rigidbody2D.AddForce(Vector2.right*force_move);
}else if(h&-0.05f){
rigidbody2D.AddForce(-Vector2.right*force_move);
if(v&0.05f){
rigidbody2D.AddForce(Vector2.up*force_move);
}else if(v&-0.05f){
rigidbody2D.AddForce(-Vector2.up*force_move);
一会在改进
最后改到这样,还算感觉良好了
public float force_move=300
//public float jump_move=10
//private Animator anim
public bool isWall=false
void Awake(){
//anim=GetComponent&Animator&():
void FixedUpdate(){
if(Input.GetKey(KeyCode.W)){
GetComponent&Rigidbody2D& ().drag = 6
rigidbody2D.AddForce(Vector2.up*force_move)
if(force_move&=8000)
force_move+=500
if(Input.GetKey(KeyCode.S)){
GetComponent&Rigidbody2D& ().drag = 6
rigidbody2D.AddForce(-Vector2.up*force_move)
if(force_move&=8000)
force_move+=500
if(Input.GetKey(KeyCode.A)){
GetComponent&Rigidbody2D& ().drag = 6
rigidbody2D.AddForce(-Vector2.right*force_move)
if(force_move&=8000)
force_move+=500
if(Input.GetKey(KeyCode.D)){
GetComponent&Rigidbody2D& ().drag = 6
rigidbody2D.AddForce(Vector2.right*force_move)
if(force_move&=8000)
force_move+=500
if(Input.GetKeyUp(KeyCode.W)||Input.GetKeyUp(KeyCode.S)||Input.GetKeyUp(KeyCode.A)||Input.GetKeyUp(KeyCode.D)){
force_move=200
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
(1)(3)(4)(33)话说用rigidbody.MovePosition移动角色,有时候会挤到缝里抖动。_unity3d吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:61,453贴子:
话说用rigidbody.MovePosition移动角色,有时候会挤到缝里抖动。收藏
新手表示亚历山大。。我想做个类似2D RPG游戏那种在平面上移动,按上下左右键就能朝上下左右移动的那种。话说这种一般用什么方法做移动比较好?。。我目前是添加了rigidbody然后用moveposition,结果发现人物有时能挤进collider之间的缝里,不停地抖动。。。有时候甚至直接完全挤进去了,然后又突然被弹出来,弹得老远。。这个应该怎样解决。。
们努力的方向:“世界电磁铁王国”-超诚电子,
擦,1楼gif居然不动= =总之就像这样。。
额外放置碰撞将小缝隙填平。或者多个箱子公用一个碰撞。这样小的缝隙既影响效率也容易抖动
你把collider的isTrigger选上呢
刚体有一个专门防止抖动的选项,但是貌似开起来的话很费处理器,你可以百度看一下,是有个选项,或开或关的,还有别开IsTrigger不然就没物理效果了
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或&&国之画&&&& &&
版权所有 京ICP备号-2
迷上了代码!

我要回帖

更多关于 unity3d 移动 抖动 的文章

 

随机推荐