You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity躲猫猫游戏脚印生成bug:单角色踩颜料全员显示脚印

问题排查与解决:仅踩入颜料的角色留下脚印

问题根源分析

  1. 全局共享状态冲突:CheckPaintItem使用静态单例instance,导致所有角色共享同一个isPaint状态。任意角色触发颜料区域时,所有角色的FootPaint都会读取到isPaint = true,进而全部生成脚印。
  2. 角色逻辑局限性:FootPaint的FootPaints方法仅针对名为PlayerSeek的角色,AI角色的脚印生成逻辑完全缺失。
  3. 触发逻辑错误:CheckPaintItem的OnTriggerEnter中,else分支会在触发非颜料标签(如胶水、加速道具)时直接重置所有颜料状态,导致颜料效果被意外中断。
  4. 移动速度处理单一:移动速度修改逻辑仅针对PlayerController单例,AI角色的速度变化未被处理。

解决步骤

1. 移除单例模式,维护独立状态

删除CheckPaintItem中的静态instance字段,让每个角色的脚本实例独立管理自身的颜料状态。

2. 修改FootPaint的状态判断逻辑

让FootPaint获取当前角色自身的CheckPaintItem组件,判断该角色是否处于颜料生效状态,同时移除仅针对玩家角色的判断,让AI角色也能正常生成脚印。

3. 修复触发区域的状态管理

将颜料状态的重置逻辑从OnTriggerEnter的else分支移至OnTriggerExit,确保只有角色离开颜料区域时才清除对应状态,避免触发其他道具时误清除颜料效果。

4. 调整移动速度的作用范围

让CheckPaintItem修改自身角色的移动速度组件,而非仅针对玩家单例,确保AI角色也能受到胶水、加速道具的影响。

修改后的代码示例

FootPaint.cs

public class FootPaint : MonoBehaviour
{
    #region --- helpers --
    public enum enumFoot
    {
        Left,
        Right,
    }
    #endregion

    public GameObject LeftPrefab;
    public GameObject RightPrefab;
    public float FootprintSpacer = 1.0f;
    private Vector3 LastFootprint;
    private enumFoot WhichFoot;
    public GameObject[] intdexPos;
    private CheckPaintItem _checkPaintItem;

    private void Start()
    {
        LastFootprint = transform.position;
        _checkPaintItem = GetComponent<CheckPaintItem>();
    }

    private void Update()
    {
        FootPaints();
    }

    public void FootPaints()
    {
        // 判断当前角色自身的颜料状态是否生效
        if (_checkPaintItem != null && _checkPaintItem.isPaint)
        {
            float DistanceSinceLastFootprint = Vector3.Distance(transform.position, LastFootprint);
            if (DistanceSinceLastFootprint >= FootprintSpacer)
            {
                LastFootprint = transform.position;
                if (WhichFoot == enumFoot.Left)
                {
                    SpawnFootDecal(LeftPrefab);
                    WhichFoot = enumFoot.Right;
                }
                else
                {
                    SpawnFootDecal(RightPrefab);
                    WhichFoot = enumFoot.Left;
                }
                LastFootprint = new Vector3(transform.position.x, transform.position.y + 1f, transform.position.z);
            }
        }
    }

    public void SpawnFootDecal(GameObject prefab)
    {
        int index = Random.Range(0, intdexPos.Length);
        GameObject decal = Instantiate(prefab);
        decal.transform.position = intdexPos[index].transform.position;
        decal.transform.Rotate(Vector3.forward, intdexPos[index].transform.eulerAngles.y);
    }
}

CheckPaintItem.cs

public class CheckPaintItem : MonoBehaviour
{
    #region boolPaint
    [HideInInspector]
    public bool isPaint = false;
    [HideInInspector]
    public bool isPaintYellow = false;
    [HideInInspector]
    public bool isPaintRed = false;
    [HideInInspector]
    public bool isPaintBlue = false;
    [HideInInspector]
    public bool isPaintGreen = false;
    #endregion

    private float _originalMoveSpeed;
    private PlayerController _playerController;
    private NavMeshAgent _aiAgent; // 假设AI用NavMeshAgent控制移动,可根据实际调整

    private void Start()
    {
        // 获取自身角色的移动组件,兼容玩家和AI
        _playerController = GetComponent<PlayerController>();
        _aiAgent = GetComponent<NavMeshAgent>();
        
        if (_playerController != null)
        {
            _originalMoveSpeed = _playerController._moveSpeed;
        }
        else if (_aiAgent != null)
        {
            _originalMoveSpeed = _aiAgent.speed;
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PaintsYellow")
        {
            Debug.Log("Yellow");
            isPaint = true;
            isPaintYellow = true;
            StartCoroutine(ResetPaintStateAfterDelay());
        }
        else if (other.gameObject.tag == "PaintsRed")
        {
            isPaint = true;
            isPaintRed = true;
            StartCoroutine(ResetPaintStateAfterDelay());
        }
        else if (other.gameObject.tag == "PaintsBlue")
        {
            isPaint = true;
            isPaintBlue = true;
            StartCoroutine(ResetPaintStateAfterDelay());
        }
        else if (other.gameObject.tag == "PaintsGreen")
        {
            isPaint = true;
            isPaintGreen = true;
            StartCoroutine(ResetPaintStateAfterDelay());
        }

        if (other.gameObject.tag == "PaintsGlue")
        {
            StartCoroutine(SlowDownMoveSpeed());
        }

        if (other.gameObject.tag == "Speed")
        {
            StartCoroutine(SpeedUpMoveSpeed());
            Destroy(other.gameObject);
        }
    }

    private void OnTriggerExit(Collider other)
    {
        // 离开颜料区域时清除对应颜色的状态
        if (other.gameObject.tag == "PaintsYellow")
        {
            isPaintYellow = false;
        }
        else if (other.gameObject.tag == "PaintsRed")
        {
            isPaintRed = false;
        }
        else if (other.gameObject.tag == "PaintsBlue")
        {
            isPaintBlue = false;
        }
        else if (other.gameObject.tag == "PaintsGreen")
        {
            isPaintGreen = false;
        }

        // 如果所有颜色状态都为false,重置总isPaint状态
        isPaint = isPaintYellow || isPaintRed || isPaintBlue || isPaintGreen;
    }

    IEnumerator ResetPaintStateAfterDelay()
    {
        yield return new WaitForSeconds(15f);
        isPaint = false;
        isPaintYellow = false;
        isPaintRed = false;
        isPaintBlue = false;
        isPaintGreen = false;
    }

    IEnumerator SlowDownMoveSpeed()
    {
        if (_playerController != null)
        {
            _playerController._moveSpeed = _playerController._moveSpeed / 2 + 0.2f;
        }
        else if (_aiAgent != null)
        {
            _aiAgent.speed = _aiAgent.speed / 2 + 0.2f;
        }
        yield return new WaitForSeconds(5f);
        ResetMoveSpeed();
    }

    IEnumerator SpeedUpMoveSpeed()
    {
        if (_playerController != null)
        {
            _playerController._moveSpeed = _playerController._moveSpeed + 1f;
        }
        else if (_aiAgent != null)
        {
            _aiAgent.speed = _aiAgent.speed + 1f;
        }
        yield return new WaitForSeconds(5f);
        ResetMoveSpeed();
    }

    private void ResetMoveSpeed()
    {
        if (_playerController != null)
        {
            _playerController._moveSpeed = _originalMoveSpeed;
        }
        else if (_aiAgent != null)
        {
            _aiAgent.speed = _originalMoveSpeed;
        }
    }
}

额外说明

  • 若AI角色使用其他移动控制组件(如CharacterController),需在CheckPaintItem中替换对应的速度修改逻辑。
  • 确保每个角色的碰撞体和触发器设置正确,避免触发区域的误判。

内容的提问来源于stack exchange,提问作者N3.nguyễn Ngọc sơn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 04:27:27