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

Unity中检测字母GameObject位置及组成单词的实现求助

Hey there! Let's tackle that word detection problem you're stuck on for your Tetris-style word game. I’ve broken down a practical, step-by-step solution that fits with your existing code structure.


First, Fix Your Grid & Letter Data Structure

Right now your grid stores Transform references, but we need to track the actual letter each block represents. Let's add a small component to each letter GameObject to hold this data:

using UnityEngine;

public class LetterBlock : MonoBehaviour
{
    public char letter; // Set this in the Inspector for each letter block
    public bool isLocked = false; // Marks if the block has settled and can't move
}

Next, update your grid to store LetterBlock references instead of transforms, and fix the grid size (your current width=160/height=280 are world units—since you move blocks by 20 units, we need grid cells based on that):

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

public class TetrisBlock : MonoBehaviour
{
    private float previousTime;
    public float fallTime = 0.8f;
    // Adjust grid size to match your 20-unit block spacing
    public static int gridWidth = 8; // 160 world units / 20 per cell
    public static int gridHeight = 14; // 280 world units / 20 per cell
    private static LetterBlock[,] grid = new LetterBlock[gridWidth, gridHeight];
    private LetterBlock myLetter;

    void Start()
    {
        myLetter = GetComponent<LetterBlock>();
    }

    // Rest of your Update code stays mostly the same—we'll adjust position checks below

Update your ValidMove and AddToGrid methods to use proper grid coordinates (converting world position to grid cells):

Vector2Int GetGridPosition(Vector3 worldPos)
{
    // Convert 20-unit world steps to 0-based grid indices
    int x = Mathf.RoundToInt(worldPos.x / 20);
    int y = Mathf.RoundToInt(worldPos.y / 20);
    return new Vector2Int(x, y);
}

bool ValidMove()
{
    Vector2Int gridPos = GetGridPosition(transform.position);
    int x = gridPos.x;
    int y = gridPos.y;

    // Check if we're outside grid bounds
    if (x < 0 || x >= gridWidth || y < 0 || y >= gridHeight)
        return false;

    // Check if the grid cell is already occupied
    if (grid[x, y] != null)
        return false;

    return true;
}

void AddToGrid()
{
    Vector2Int gridPos = GetGridPosition(transform.position);
    int x = gridPos.x;
    int y = gridPos.y;

    if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight)
    {
        grid[x, y] = myLetter;
        myLetter.isLocked = true; // Lock the block once it settles
    }
}

Core Word Detection Logic

You have two main options for detecting words: player-initiated selection (e.g., clicking/dragging to select letters) or automatic scanning (after a block settles). Let's cover both.

1. Player-Initiated Word Selection

If you want players to manually select adjacent letters to form words:

  • Track the sequence of letters the player selects
  • Verify the letters are adjacent (adjust rules for diagonal adjacency if needed)
  • Check if the resulting string is in your valid word list

Add these methods to your TetrisBlock class (or a dedicated GameManager):

// Store your valid words here (load from a text file in production!)
public HashSet<string> validWords = new HashSet<string>
{
    "sea", "weed", "seaweed", "eat", "seed"
};
public string targetWord = "seaweed"; // Your win condition word

public bool CheckSelectedWord(List<LetterBlock> selectedLetters)
{
    if (selectedLetters.Count < 2) return false; // Ignore single letters

    // Ensure all selected blocks are locked (settled)
    foreach (var letter in selectedLetters)
    {
        if (!letter.isLocked) return false;
    }

    // Check if each letter is adjacent to the previous one
    Vector2Int prevPos = GetGridPosition(selectedLetters[0].transform.position);
    for (int i = 1; i < selectedLetters.Count; i++)
    {
        Vector2Int currentPos = GetGridPosition(selectedLetters[i].transform.position);
        if (!AreAdjacent(prevPos, currentPos))
            return false;
        prevPos = currentPos;
    }

    // Build the word string (lowercase to avoid case mismatches)
    string word = "";
    foreach (var letter in selectedLetters)
    {
        word += letter.letter.ToString().ToLower();
    }

    // Check if the word is valid
    if (validWords.Contains(word))
    {
        ScoreAndRemoveWord(word, selectedLetters);
        return true;
    }
    return false;
}

// Define adjacency (adjust to include diagonals if you want)
bool AreAdjacent(Vector2Int pos1, Vector2Int pos2)
{
    return (Mathf.Abs(pos1.x - pos2.x) == 1 && pos1.y == pos2.y) || 
           (Mathf.Abs(pos1.y - pos2.y) == 1 && pos1.x == pos2.x);
}

void ScoreAndRemoveWord(string word, List<LetterBlock> selectedLetters)
{
    // Calculate score (customize this to your rules)
    int score = word.Length * 10;
    if (word == targetWord.ToLower())
    {
        score += 1000;
        Debug.Log("YOU WIN!");
        // Trigger win condition logic here
    }

    // Update your UI score here (e.g., UIManager.Instance.AddScore(score))

    // Remove the selected blocks from the grid and destroy them
    foreach (var letter in selectedLetters)
    {
        Vector2Int pos = GetGridPosition(letter.transform.position);
        grid[pos.x, pos.y] = null;
        Destroy(letter.gameObject);
    }

    // Make blocks above fall into empty spaces (like Tetris)
    HandleFallingBlocks();
}

void HandleFallingBlocks()
{
    // Iterate each column from bottom to top
    for (int x = 0; x < gridWidth; x++)
    {
        int emptyY = -1;
        for (int y = 0; y < gridHeight; y++)
        {
            if (grid[x, y] == null)
            {
                emptyY = y;
            }
            else if (emptyY != -1)
            {
                // Move the block down to the empty space
                grid[x, emptyY] = grid[x, y];
                grid[x, y] = null;
                grid[x, emptyY].transform.position = new Vector3(emptyY * 20, x * 20, 0);
                emptyY++;
            }
        }
    }
}

2. Automatic Word Scanning

If you want to automatically detect words after a block settles, use a backtracking algorithm to scan all possible adjacent letter sequences:

void AutoDetectWords()
{
    bool[,] visited = new bool[gridWidth, gridHeight];
    foreach (int x in Enumerable.Range(0, gridWidth))
    {
        foreach (int y in Enumerable.Range(0, gridHeight))
        {
            if (grid[x, y] != null && grid[x, y].isLocked && !visited[x, y])
            {
                BacktrackScan(x, y, visited, "");
            }
        }
    }
}

void BacktrackScan(int x, int y, bool[,] visited, string currentWord)
{
    if (x < 0 || x >= gridWidth || y < 0 || y >= gridHeight || visited[x, y] || grid[x, y] == null)
        return;

    visited[x, y] = true;
    currentWord += grid[x, y].letter.ToString().ToLower();

    // Check if current string is a valid word
    if (validWords.Contains(currentWord))
    {
        // For automatic detection, you'll need to track which blocks form the word to remove them
        // (This requires modifying the backtrack to track positions instead of just the string)
        Debug.Log($"Found word: {currentWord}");
        ScoreAndRemoveWord(currentWord, GetBlocksForWord(currentWord)); // Implement GetBlocksForWord based on your needs
    }

    // Scan all 4 adjacent directions (add diagonals if needed)
    int[] dx = { -1, 1, 0, 0 };
    int[] dy = { 0, 0, -1, 1 };
    for (int i = 0; i < 4; i++)
    {
        BacktrackScan(x + dx[i], y + dy[i], visited, currentWord);
    }

    // Unmark for backtracking
    visited[x, y] = false;
}

Call AutoDetectWords() right after AddToGrid() in your Update loop:

if (!ValidMove())
{
    transform.position -= new Vector3(0, -20, 0);
    AddToGrid();
    this.enabled = false;
    FindObjectOfType<SpawnerTetromino>().NewTetromino();
    AutoDetectWords(); // Scan for words after a block settles
}

Key Notes

  • Word List: In production, load your valid words from a text file (e.g., a newline-separated list) instead of hardcoding them.
  • Adjacency Rules: Adjust the AreAdjacent method if you want to allow diagonal letter connections.
  • Performance: For large grids, optimize the automatic scan by limiting word length (e.g., only scan up to 10 letters) to avoid slowdowns.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 19:00:53