首页 > 其他分享 >unity2d碰撞和移动的反弹问题

unity2d碰撞和移动的反弹问题

时间:2023-02-09 03:12:12浏览次数:53  
标签:hit unity2d Vector2 碰撞 collider 移动 inputX dir

使用rigidbody2d和collider2d,可以实现控制物体移动和碰撞。但是有一个问题让博主抓狂:代表玩家的小方块撞上障碍物时,会有一瞬间嵌入障碍物,然后被弹回去。

简单的控制代码如下:

        private void Update()
        {
            inputX = Input.GetAxisRaw("Horizontal");

            if (inputX != 0)
            {
                Vector2 dir = new Vector2(inputX, 0f);
                float dist = Mathf.Abs(speed * Time.deltaTime);
                
                rb.velocity = dir * dist;  // 使用了刚体
            }
            else
            {
                rb.velocity = Vector2.zero;
            }
        }

经实验,无论是换成AddForce也好,换成MovePosition也好,把Update换成FixedUpdate也好,改Material2D也好,都会有这烦人的抖一下。

如果不用刚体控制,改用transform.Translate,甚至还会造成连续抖动的效果。

根据【Unity物体移动碰撞抖动(原因和解决方法)】的说法,这是因为Update位于unity自身的物理处理之后,所以Update里面通过代码移动了位置,之后并不会经过unity自身的物理修正,就被渲染出来了,所以是嵌入墙里的;而下一帧的FixedUpdate修正了位置,就被弹出墙外了。但那样似乎没有解释为什么控制代码移到FixedUpdate后,还是会抖一下。

解决方法一:将Rigidbody2D的CollisionDetection属性从Discrete改为Continuous

但是很耗性能。

解决方法二:通过射线时刻检测“即将走的距离”上是否会发生碰撞,从而决定最终走多远的距离

这个方法甚至可以不挂刚体了,直接通过射线来获取碰撞。(但是OnCollisionEnter2D那些函数也不能用了)

但要注意的一点是,射线是会撞到玩家自己的碰撞体的,所以为了忽略自身,可以将想要检测的碰撞体放到同一Layer上,这样就可以进行过滤。

代码如下:

        private void Update()
        {
            inputX = Input.GetAxisRaw("Horizontal");

            if (inputX != 0)
            {
                Vector2 dir = new Vector2(inputX, 0f);
                float dist = Mathf.Abs(speed * Time.deltaTime);
                
                // collider的偏移量也纳入考虑
                Vector2 startPos = (Vector2)transform.position + boxCollider.offset;
                var hit = Physics2D.BoxCast(startPos, boxCollider.size, 0, dir, dist, LayerMask.GetMask("Wall"));
                if (hit.collider != null)
                {
                    Debug.Log("hit x");
                    Vector2 endPos = (Vector2) hit.collider.transform.position + hit.collider.offset;
                  // 计算实际上可以移动的最大距离,即两个collider边界之间的距离
                    float maxDist = Mathf.Abs(endPos.x - startPos.x) -
                                    (boxCollider.size.x + hit.collider.bounds.size.x) / 2;
                    transform.Translate(dir * maxDist);
                }
                else
                {
                    transform.Translate(dir * dist);   
                }
            }
        }

这样就可以了……吗?如果你把刚体去掉,然后把这个脚本挂到玩家身上,你会发现确实不抖了,但是玩家会贴在墙上卡住,没法移动了,而且会疯狂打印hit x,仿佛玩家的碰撞体和墙的碰撞体有所重合,所以射线检测在起点处就能检测到它。

给maxDist加一点偏移量可以解决:

maxDist -= 0.01f;

但这是为啥呢?

打开Project Settings - Physics2D,你能看到一个属性叫Default Contact Offset,值正好就等于0.01。根据文档,如果碰撞体的距离小于它们的 contactOffset 值之和,则将生成接触,也就是撞上了。所以我们强行让两个碰撞体之间的距离为0,实际上已经嵌在一起了。如果你仔细看本文开头的动图,可以发现即使是贴墙的时候,两者之间确实也有一点缝隙,这就是那0.01的距离。

这个值可以再调小一些,但调成0似乎会造成一些问题。我们通过射线确定移动距离时,记得把它给加上。

总算不抖了!可以睡个好觉了!

最后一个问题:怎么玩家走起来似乎有点忽快忽慢?

解决方案:只会在编辑器里这样,build出来顺滑得很。

 

标签:hit,unity2d,Vector2,碰撞,collider,移动,inputX,dir
From: https://www.cnblogs.com/chopwoodfish/p/17103937.html

相关文章