Unity躲猫猫游戏脚印生成bug:单角色踩颜料全员显示脚印
问题排查与解决:仅踩入颜料的角色留下脚印
问题根源分析
- 全局共享状态冲突:
CheckPaintItem使用静态单例instance,导致所有角色共享同一个isPaint状态。任意角色触发颜料区域时,所有角色的FootPaint都会读取到isPaint = true,进而全部生成脚印。 - 角色逻辑局限性:
FootPaint的FootPaints方法仅针对名为PlayerSeek的角色,AI角色的脚印生成逻辑完全缺失。 - 触发逻辑错误:
CheckPaintItem的OnTriggerEnter中,else分支会在触发非颜料标签(如胶水、加速道具)时直接重置所有颜料状态,导致颜料效果被意外中断。 - 移动速度处理单一:移动速度修改逻辑仅针对
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
相关产品推荐
相关产品推荐

