技术求助:如何检查物体运动状态及优化Unity球体缩小代码
1. 如何检查物体的运动状态?
- 通过刚体(Rigidbody)检测:若物体带有Rigidbody组件,可直接读取
rigidbody.velocity属性,结合向量长度判断是否运动(需考虑浮点误差):
注意:运动学刚体(Rigidbody rb = GetComponent<Rigidbody>(); if (rb.velocity.sqrMagnitude < 0.001f) { // 物体静止 } else { // 物体正在运动 }isKinematic=true)的velocity不会自动更新,需用其他方法检测。 - 通过位置变化检测:记录每一帧的物体位置,对比前后帧的位置差判断状态:
private Vector3 lastPosition; void Start() { lastPosition = transform.position; } void Update() { bool isMoving = Vector3.Distance(transform.position, lastPosition) > 0.001f; lastPosition = transform.position; } - 通过动画状态检测:若物体由动画驱动,可读取Animator组件的状态判断是否处于运动动画:
Animator anim = GetComponent<Animator>(); bool isRunning = anim.GetCurrentAnimatorStateInfo(0).IsName("Run");
2. Unity代码优化方案
原代码存在每帧查找子弹效率低、固定位置判断状态不可靠的问题,以下是针对需求的优化实现:
核心优化思路
- 缓存子弹引用,避免重复查找
- 用布尔变量
isBulletActive标记子弹是否处于飞行状态 - 仅当无活跃子弹时,允许触发玩家缩小逻辑
- 通过子弹的刚体状态或生命周期事件更新活跃标记
优化后的代码
using UnityEngine; public class PlayerBallController : MonoBehaviour { [SerializeField] private float shrinkAmount; // 每次点击缩小的量 [SerializeField] private int healthPlayer; [SerializeField] private GameObject bulletPrefab; [SerializeField] private Transform bulletSpawnPoint; [SerializeField] private float bulletSpeed = 20f; public GameOver gameOver; private GameObject currentBullet; private bool isBulletActive = false; void Update() { // 更新子弹活跃状态:子弹存在但静止时标记为非活跃 if (currentBullet != null) { Rigidbody bulletRb = currentBullet.GetComponent<Rigidbody>(); if (bulletRb != null && bulletRb.velocity.sqrMagnitude < 0.001f) { isBulletActive = false; } } // 无活跃子弹时,响应点击触发缩小和子弹生成 if (!isBulletActive && Input.GetMouseButtonDown(0)) { SpawnBullet(); ShrinkPlayer(); healthPlayer--; if (healthPlayer <= 0) { Destroy(gameObject); gameOver.GameOverScreen(); } } } void SpawnBullet() { // 清理旧子弹 if (currentBullet != null) Destroy(currentBullet); // 生成新子弹并赋予初速度 currentBullet = Instantiate(bulletPrefab, bulletSpawnPoint.position, bulletSpawnPoint.rotation); Rigidbody bulletRb = currentBullet.GetComponent<Rigidbody>(); if (bulletRb != null) { bulletRb.AddForce(bulletSpawnPoint.forward * bulletSpeed, ForceMode.Impulse); } isBulletActive = true; // 自动销毁子弹,避免内存泄漏 Destroy(currentBullet, 5f); } void ShrinkPlayer() { // 限制最小缩放,避免异常 Vector3 newScale = transform.localScale - Vector3.one * shrinkAmount; newScale = Vector3.Max(newScale, Vector3.one * 0.1f); transform.localScale = newScale; } // 销毁玩家时清理子弹 void OnDestroy() { if (currentBullet != null) Destroy(currentBullet); } }
额外建议
- 避免在
Update中使用GameObject.FindWithTag,改用直接引用或事件系统传递状态 - 若需要持续缩小(而非点击一次缩小一次),可将缩小逻辑放在
Update中,并用isBulletActive做开关 - 给子弹添加独立脚本,通过
OnCollisionEnter或OnDestroy事件通知玩家控制器,状态判断会更准确
内容的提问来源于stack exchange,提问作者Roman Babenko
相关产品推荐
相关产品推荐

