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

Unity类俄罗斯方块游戏:Physics.RaycastAll方块行检测异常问题

类俄罗斯方块游戏行检测异常:漏检与延迟问题

问题概述

开发类俄罗斯方块游戏时,核心需求是检测落地方块所在行是否集齐10个方块,满行则消除并加分,但当前实现存在两个关键问题:

  • 检测延迟:刚落下的方块能立即触发检测,此前已落下的方块触发检测时存在明显延迟
  • RaycastAll漏检:多数情况下无法正确捕获整行10个方块,仅偶尔随机成功(截图显示实际10个方块仅检测到8个)

相关代码

行检测脚本(CheckBlocksRow)

using System.Collections.Generic;
using UnityEngine;

public class CheckBlocksRow : MonoBehaviour
{
public bool check;
public List<float> blocksHeight = new List<float>();

public int blocksInRow;
public GameObject[] caughtBlocks;
public LayerMask layerMask;

RaycastHit[] hittedBlocks;

void Update()
{
    if (check)
    {
        blocksHeight.Sort();

        for (int i = 0; i < blocksHeight.Count; i++)
        {
            if (SendLaser(blocksHeight[i])) CheckBlocks();
        }
    }
}

bool SendLaser(float heightToCheck)
{
    RaycastHit[] hits = Physics.RaycastAll(new Vector2(-5.78f, heightToCheck), transform.TransformDirection(Vector3.right), Mathf.Infinity, layerMask);
    blocksInRow = hits.Length;

    if (blocksInRow >= 1)
    {
        hittedBlocks = hits;
        return true;
    }
    else
    {
        check = false;
        return false;
    }
}

void CheckBlocks()
{
    caughtBlocks = new GameObject[0];
    caughtBlocks = new GameObject[hittedBlocks.Length];

    for (int i = 0; i < caughtBlocks.Length; i++)
    {
        caughtBlocks[i] = hittedBlocks[i].transform.gameObject;
    }

    if (caughtBlocks.Length == 10) print("Full row: " + caughtBlocks[0].transform.position.y);

    check = false;
}
}

检测触发脚本

void SetToCheck()
{
    CheckBlocksRow _checkBlocksRow = GameObject.FindGameObjectWithTag("Board").transform.GetChild(1).GetComponent<CheckBlocksRow>();
    _checkBlocksRow.blocksHeight.Clear();

    for (int i = 0; i < transform.childCount; i++)
    {
        if(transform.GetChild(i).name != "Center") _checkBlocksRow.blocksHeight.Add(transform.GetChild(i).transform.position.y);
    }

    _checkBlocksRow.check = true;
}

问题根源

  1. RaycastAll漏检原因
    • 浮点精度误差:直接使用方块的position.y作为射线高度,可能因碰撞盒位置偏移、浮点计算误差,导致射线未穿过部分方块的碰撞盒
    • 射线方向错误:transform.TransformDirection(Vector3.right)会受检测脚本所在物体的旋转影响,导致射线偏离水平方向
    • 射线范围不足:起点x=-5.78f可能未覆盖最左侧方块的左侧区域,导致边缘方块无法被检测到
  2. 延迟与未触发早期检测原因
    • 触发逻辑仅收集当前落下方块的子方块高度,仅检测这些行,早期已填满的行不会被触发检测
    • CheckBlocks方法中直接设置check=false,导致Update中的循环只执行一次就终止,同一批传入的多个高度仅检测第一个

修复方案

1. 解决RaycastAll漏检问题

  • 修正射线高度:对方块Y坐标取整(假设行高度为整数间隔),用行的基准高度作为射线高度,避免浮点误差
  • 固定射线方向:使用世界空间的Vector3.right,而非受物体旋转影响的转换方向
  • 调整射线范围:将起点设为足够靠左的位置(如x=-6),射线长度设为覆盖整个棋盘宽度(如12),确保覆盖所有方块
  • 验证LayerMask:在Inspector中确认LayerMask仅勾选方块所在层,无误排除情况

2. 解决检测延迟与未触发问题

  • 扩展触发逻辑:每次方块落地后,收集所有已存在方块的行高度,而非仅当前方块的子方块,确保所有可能满行的行都被检测
  • 修正循环逻辑:将check=false移到Update循环遍历完所有高度后,避免循环提前终止
  • 去重优化:对收集的行高度去重,避免重复检测同一行

修复后代码示例

优化后的行检测脚本

using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class CheckBlocksRow : MonoBehaviour
{
    public bool check;
    public List<float> blocksHeight = new List<float>();
    public LayerMask layerMask;

    void Update()
    {
        if (check)
        {
            // 去重并排序,避免重复检测同一行
            var uniqueHeights = blocksHeight.Distinct().OrderBy(h => h).ToList();
            
            foreach (var height in uniqueHeights)
            {
                CheckRowAtHeight(height);
            }
            
            check = false;
        }
    }

    void CheckRowAtHeight(float heightToCheck)
    {
        // 取整消除浮点误差,用世界空间水平射线覆盖整个棋盘
        float adjustedHeight = Mathf.Round(heightToCheck);
        Ray ray = new Ray(new Vector3(-6f, adjustedHeight, 0f), Vector3.right);
        RaycastHit[] hits = Physics.RaycastAll(ray, 12f, layerMask);

        if (hits.Length == 10)
        {
            Debug.Log($"消除满行:Y={adjustedHeight}");
            // 添加消除方块、加分逻辑
            foreach (var hit in hits)
            {
                Destroy(hit.transform.gameObject);
            }
        }
    }
}

优化后的触发脚本

void SetToCheck()
{
    CheckBlocksRow checkRow = GameObject.FindGameObjectWithTag("Board").transform.GetChild(1).GetComponent<CheckBlocksRow>();
    checkRow.blocksHeight.Clear();

    // 收集所有已存在方块的行高度
    GameObject[] allBlocks = GameObject.FindGameObjectsWithTag("Block");
    foreach (var block in allBlocks)
    {
        float rowHeight = Mathf.Round(block.transform.position.y);
        if (!checkRow.blocksHeight.Contains(rowHeight))
        {
            checkRow.blocksHeight.Add(rowHeight);
        }
    }

    checkRow.check = true;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:45:35