Unity 2D游戏Sprite高速移动并穿透物体问题求助
Unity 2D 敌人穿透Tilemap地面后快速下坠的解决方法
在带有Tilemap Collider组件的Tilemap方块上方放置虫子模型,启动游戏后虫子初始移动正常,但几秒后轻微陷入地面,随后以极快速度下坠。虫子使用的脚本如上,已设置两个正确的Vector3位置点。
问题原因
- 直接修改transform.position绕过物理引擎:当前用
transform.position = Vector3.MoveTowards(...)直接修改物体位置,这种方式完全跳过Unity的2D物理系统,Tilemap Collider的碰撞检测无法正确拦截物体移动。随着时间推移,物体位置会慢慢穿透碰撞体,当物理引擎发现位置异常时,会强制修正导致物体快速下坠。 - 浮点数精度导致的位置判断失效:用
transform.position == positions[indexPosition]判断是否到达目标点,由于浮点数的精度误差,这个条件可能永远不会触发,导致敌人卡在移动状态,进一步加剧穿透问题。
解决方案
- 用Rigidbody2D控制移动:确保虫子物体挂载
Rigidbody2D组件,通过物理引擎处理移动,让碰撞检测正常工作:- 若敌人不需要重力,将Rigidbody2D设为Kinematic类型,用
MovePosition()方法移动 - 若需要重力影响,设为Dynamic类型,通过设置
velocity控制移动
- 若敌人不需要重力,将Rigidbody2D设为Kinematic类型,用
- 修正位置到达判断逻辑:用
Vector3.Distance()或Vector3.sqrMagnitude(性能更优)判断是否接近目标点,规避浮点数精度问题 - 检查物理组件设置:确保Rigidbody2D的重力缩放(Gravity Scale)合理,若不需要下落可设为0;BoxCollider2D(或其他碰撞体)的大小和位置完全覆盖虫子模型
修改后的代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class Enemy : MonoBehaviour { public float speed = 5f; public Vector3[] positions; private int indexPosition; private Rigidbody2D rb; private SpriteRenderer sr; // 目标点判定阈值,可根据场景调整 public float reachThreshold = 0.1f; void Start() { rb = GetComponent<Rigidbody2D>(); sr = GetComponent<SpriteRenderer>(); // 初始化位置到第一个目标点,避免偏移 if(positions.Length > 0) transform.position = positions[0]; } // 物理相关逻辑放在FixedUpdate中,保证稳定 void FixedUpdate() { // 翻转Sprite逻辑 Vector3 playerPos = GameObject.Find("Player").transform.position; sr.flipX = transform.position.x > playerPos.x; // 计算移动方向 Vector3 targetPos = positions[indexPosition]; Vector3 moveDir = (targetPos - transform.position).normalized; // Kinematic类型Rigidbody2D的移动方式 rb.MovePosition(transform.position + moveDir * speed * Time.fixedDeltaTime); // 判断是否到达目标点 float distanceToTarget = Vector3.Distance(transform.position, targetPos); if (distanceToTarget < reachThreshold) { indexPosition = (indexPosition + 1) % positions.Length; } } private void OnCollisionEnter2D(Collision2D other) { if (other.gameObject.CompareTag("Player")) { SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); } } private void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.CompareTag("Player")) { Destroy(gameObject); } } }
额外说明
- 移动逻辑从
Update移到FixedUpdate,匹配物理引擎的固定更新帧率,避免移动抖动 - 移除了原代码中
speed/1000的缩放,可直接通过调整speed参数控制移动速度 - 提前缓存
Rigidbody2D和SpriteRenderer组件,避免每帧调用GetComponent损耗性能 - 用取余运算简化了目标点循环逻辑
内容的提问来源于stack exchange,提问作者Mendo
相关产品推荐
相关产品推荐

