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

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;
    }
}

解决方案

推荐:基于玩家跟踪的触发器方案

给每个水泡维护玩家状态集合,单独控制每个玩家的进出权限:

  1. 水泡对象添加变量:
// 记录已离开该水泡的玩家集合
private HashSet<GameObject> _playersWhoLeft = new HashSet<GameObject>();
// 记录当前在水泡上的玩家数
private int _currentPlayerCount = 0;
  1. 修改触发器回调:
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--;
}
  1. 水泡生成初始化:
    水泡生成时,检测目标位置内的所有玩家,直接将他们加入当前计数(不加入已离开集合),确保他们可以正常停留:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:39:55