2D仿《泡泡堂》项目水泡区域玩家重入限制问题求助
2D《泡泡堂(Crazy Arcade)》复刻:水泡区域进出权限问题
需求
玩家离开水泡区域后,永久无法再次进入该水泡。
现有实现的问题
触发器方案的多玩家bug
最初通过切换触发器状态实现逻辑,但多玩家场景下失效:
两名玩家同时在水泡上时,只要其中一名离开,另一名会被弹开,无法继续停留。
理想逻辑:
- 水泡生成前已在目标位置的多名玩家,可同时留在水泡上;
- 任意玩家离开后,该玩家无法再进入,但其他仍在水泡上的玩家不受影响,直到他们也离开后同样无法进入。
触发器方案代码:
void OnTriggerStay2D(Collider2D collision) { if (collision.gameObject.tag == "Player") { collider.isTrigger = true; } } void OnTriggerExit2D(Collider2D collision) { if (collision.gameObject.tag == "Player") { collider.isTrigger = false; } }
射线检测方案的失效
尝试用射线检测阻止玩家进入已离开的水泡,但无法实现停止移动的效果。
射线方案代码:
void Ray() { if(moveVec.x == 0 && moveVec.y == 1) { PlusVec = new Vector2(0, 0.2f); dirVec = Vector2.up; } else if(moveVec.x == 0 && moveVec.y == -1) { PlusVec = new Vector2(0, -0.4f); dirVec = Vector2.down; } else if(moveVec.x == -1 && moveVec.y == 0) { PlusVec = new Vector2(-0.3f, 0); dirVec = Vector2.left; } else if (moveVec.x == 1 && moveVec.y == 0) { PlusVec = new Vector2(0.3f, 0); dirVec = Vector2.right; } // Ray Debug.DrawRay(rigid.position + PlusVec, dirVec * 0.4f, new Color(0, 1, 0)); RaycastHit2D rayHit = Physics2D.Raycast(rigid.position + PlusVec, dirVec, 0.4f, LayerMask.GetMask("Balloon")); if (rayHit.collider != null) { scanObject = rayHit.collider.gameObject; rigid.velocity = Vector3.zero; Debug.Log(scanObject.name); } else { scanObject = null; } }
解决方案
推荐:基于玩家跟踪的触发器方案
给每个水泡维护玩家状态集合,单独控制每个玩家的进出权限:
- 水泡对象添加变量:
// 记录已离开该水泡的玩家集合 private HashSet<GameObject> _playersWhoLeft = new HashSet<GameObject>(); // 记录当前在水泡上的玩家数 private int _currentPlayerCount = 0;
- 修改触发器回调:
void OnTriggerEnter2D(Collider2D collision) { if (!collision.CompareTag("Player")) return; var player = collision.gameObject; // 玩家已离开过,禁止进入 if (_playersWhoLeft.Contains(player)) { // 阻止移动并可选推开玩家 var rb = player.GetComponent<Rigidbody2D>(); if (rb != null) rb.velocity = Vector2.zero; // 可选:计算推开方向,比如远离水泡中心 // rb.AddForce((player.transform.position - transform.position).normalized * 5f, ForceMode2D.Impulse); return; } // 允许进入,增加计数 _currentPlayerCount++; } void OnTriggerExit2D(Collider2D collision) { if (!collision.CompareTag("Player")) return; var player = collision.gameObject; // 标记该玩家已离开 _playersWhoLeft.Add(player); // 减少当前玩家计数 _currentPlayerCount--; }
- 水泡生成初始化:
水泡生成时,检测目标位置内的所有玩家,直接将他们加入当前计数(不加入已离开集合),确保他们可以正常停留:
void Start() { // 生成时检测当前区域内的玩家 Collider2D[] playersInArea = Physics2D.OverlapBoxAll(transform.position, transform.localScale, 0f, LayerMask.GetMask("Player")); foreach (var collider in playersInArea) { _currentPlayerCount++; } }
射线检测方案修复
如果坚持用射线检测,问题在于仅设置velocity = 0可能无法阻止直接修改Transform的移动逻辑。需在玩家移动逻辑中提前判断:
在玩家的移动函数中添加前置检查:
bool IsBlockedByRestrictedBalloon() { // 复用你的射线检测逻辑 if(moveVec.x == 0 && moveVec.y == 1) { PlusVec = new Vector2(0, 0.2f); dirVec = Vector2.up; } // ... 其他方向判断省略 RaycastHit2D rayHit = Physics2D.Raycast(rigid.position + PlusVec, dirVec, 0.4f, LayerMask.GetMask("Balloon")); if (rayHit.collider == null) return false; // 获取水泡组件,检查当前玩家是否在已离开集合中 var balloon = rayHit.collider.GetComponent<Balloon>(); // 替换为你的水泡脚本名 return balloon != null && balloon.HasPlayerLeft(gameObject); } // 玩家移动逻辑 void Move() { if (IsBlockedByRestrictedBalloon()) { rigid.velocity = Vector2.zero; return; // 直接跳过移动 } // 正常移动逻辑 }
同时在水泡脚本中添加对外查询方法:
public bool HasPlayerLeft(GameObject player) { return _playersWhoLeft.Contains(player); }
内容的提问来源于stack exchange,提问作者Ori
相关产品推荐
相关产品推荐

