You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity技术问询:如何解决多碰撞体动画精灵的计分异常问题

解决多PolygonCollider2D导致的重复计分问题

我完全懂你现在的困扰——给动画精灵的每帧都挂上PolygonCollider2D后,穿过"Point"对象时,多个碰撞体接连触发OnTriggerEnter2D,分数一下就加超了,这确实挺闹心的。下面给你几个实用的解决方案,你可以根据项目场景选最合适的:

方案一:给Point对象加触发冷却标记(最推荐)

核心思路是让被触发的Point暂时"锁死",短时间内不再响应碰撞,避免重复触发。

  1. 先给你的Point对象加个简单脚本,比如PointTriggerHandler
public class PointTriggerHandler : MonoBehaviour
{
    private bool _hasTriggered = false;
    // 冷却时间根据你的动画速度调整,确保精灵完全离开后再重置
    private readonly float _resetDelay = 0.6f;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (!_hasTriggered && collision.CompareTag("Player")) // 假设你的精灵标签是Player
        {
            _hasTriggered = true;
            // 直接通知玩家脚本加分,也可以在这里自己处理计分逻辑
            collision.GetComponent<YourPlayerScript>().AddScore();
            Invoke(nameof(ResetTriggerStatus), _resetDelay);
        }
    }

    private void ResetTriggerStatus()
    {
        _hasTriggered = false;
    }
}
  1. 修改你原来的玩家脚本,把计分逻辑抽成单独方法:
private int _score = 0;
public Text scoreText;

public void AddScore()
{
    _score++;
    scoreText.text = _score.ToString();
}

// 原来的OnTriggerEnter2D可以删掉,或者留着做兼容

这样不管精灵有多少个碰撞体碰到Point,同一冷却周期内只会加一次分。

方案二:仅启用当前动画帧的碰撞体

这种方法是让动画播放时,只有当前显示帧对应的PolygonCollider2D处于启用状态,其他帧的都禁用。

  1. 先把所有碰撞体存到数组里:
private PolygonCollider2D[] _frameColliders;
private Animator _animator;

private void Awake()
{
    _frameColliders = GetComponents<PolygonCollider2D>();
    _animator = GetComponent<Animator>();
    // 初始时禁用所有碰撞体
    foreach (var collider in _frameColliders)
    {
        collider.enabled = false;
    }
}
  1. 每一帧更新时,根据当前动画帧启用对应碰撞体:
private void Update()
{
    AnimatorStateInfo stateInfo = _animator.GetCurrentAnimatorStateInfo(0);
    // 计算当前动画帧索引(假设你的动画是0-7的8帧)
    int currentFrame = Mathf.FloorToInt(stateInfo.normalizedTime * stateInfo.length * stateInfo.speed) % 8;

    // 先禁用所有碰撞体,再启用当前帧的
    foreach (var collider in _frameColliders)
    {
        collider.enabled = false;
    }
    _frameColliders[currentFrame].enabled = true;
}
  1. 保留你原来的OnTriggerEnter2D逻辑就行,现在同一时间只有一个碰撞体工作,不会重复触发。

方案三:在玩家脚本中记录已触发的Point

如果不想改动Point的脚本,也可以在玩家这边用集合记录已经触发过的Point,避免重复计分:

private int _score = 0;
public Text scoreText;
private readonly HashSet<GameObject> _triggeredPoints = new HashSet<GameObject>();

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.CompareTag("Point") && !_triggeredPoints.Contains(collision.gameObject))
    {
        _score++;
        scoreText.text = _score.ToString();
        _triggeredPoints.Add(collision.gameObject);
        
        // 如果Point是一次性收集品,直接销毁就行;如果是循环利用的,记得在重置时从集合移除
        Destroy(collision.gameObject, 1f);
    }
}

这种方法适合Point是一次性的场景,如果Point会循环出现,记得在它回到初始位置时,从_triggeredPoints中移除对应的对象。

以上几种方法都能解决重复计分的问题,你可以根据自己的项目需求选最顺手的~

内容的提问来源于stack exchange,提问作者JayKayKay

火山引擎 最新活动