← Назад к вопросам

Как написать Unity match 3 игру с 1 MonoBehaviour?

2.0 Middle🔥 101 комментариев
#Unity Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разработка Match-3 игры на Unity с одним MonoBehaviour

Создание Match-3 игры с использованием всего одного MonoBehaviour — это интересная архитектурная задача, которая заставляет максимально эффективно использовать объектно-ориентированное программирование и систему компонентов Unity. Такой подход способствует созданию чистого, поддерживаемого кода с четким разделением ответственности.

Ключевые архитектурные принципы

Вместо множества MonoBehaviour-скриптов, разбросанных по объектам сцены, мы создадим единый управляющий класс Match3GameManager, который будет координировать все аспекты игры:

  1. Управление состоянием игры (состояние сетки, проверка matches, подсчет очков)
  2. Обработка пользовательского ввода (перетаскивание, свайпы)
  3. Визуальное представление (спавн, анимация игровых элементов)
  4. Логика игровых правил (генерация, проверка комбинаций)

Базовая структура класса GameManager

using UnityEngine;
using System.Collections.Generic;

public enum GameState { Idle, Swapping, Matching, Falling, GameOver }

public class Match3GameManager : MonoBehaviour
{
    [Header("Game Settings")]
    [SerializeField] private int gridWidth = 8;
    [SerializeField] private int gridHeight = 8;
    [SerializeField] private GameObject[] tilePrefabs;
    
    private GameState currentState = GameState.Idle;
    private Tile[,] grid;
    private Tile selectedTile;
    private Vector2Int selectedGridPos;
    
    // Система событий для коммуникации между системами
    private System.Action onMatchFound;
    private System.Action onGridUpdated;
}

Реализация основных систем в одном классе

1. Инициализация сетки

private void Start()
{
    InitializeGrid();
    SetupInputHandling();
    SetupEventSystem();
}

private void InitializeGrid()
{
    grid = new Tile[gridWidth, gridHeight];
    
    for (int x = 0; x < gridWidth; x++)
    {
        for (int y = 0; y < gridHeight; y++)
        {
            SpawnTileAt(x, y);
        }
    }
    
    // Убедимся, что нет начальных совпадений
    RemoveInitialMatches();
}

private void SpawnTileAt(int x, int y)
{
    int tileType = GetRandomTileTypeExcludingMatches(x, y);
    GameObject tileObj = Instantiate(tilePrefabs[tileType], 
        new Vector3(x, y, 0), Quaternion.identity);
    
    Tile tile = tileObj.AddComponent<Tile>();
    tile.Initialize(x, y, tileType);
    grid[x, y] = tile;
}

2. Обработка ввода и свайпов

private void Update()
{
    if (currentState != GameState.Idle) return;
    
    if (Input.GetMouseButtonDown(0))
    {
        HandleTileSelection();
    }
    else if (Input.GetMouseButtonUp(0) && selectedTile != null)
    {
        HandleTileSwap();
    }
}

private void HandleTileSelection()
{
    Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    Vector2Int gridPos = new Vector2Int(
        Mathf.RoundToInt(worldPos.x),
        Mathf.RoundToInt(worldPos.y)
    );
    
    if (IsWithinGrid(gridPos))
    {
        selectedTile = grid[gridPos.x, gridPos.y];
        selectedGridPos = gridPos;
        selectedTile.Select();
    }
}

3. Логика проверки совпадений

private List<Tile> FindMatches()
{
    HashSet<Tile> matchedTiles = new HashSet<Tile>();
    
    // Проверка по горизонтали
    for (int y = 0; y < gridHeight; y++)
    {
        for (int x = 0; x < gridWidth - 2; x++)
        {
            Tile tile1 = grid[x, y];
            Tile tile2 = grid[x + 1, y];
            Tile tile3 = grid[x + 2, y];
            
            if (tile1.Type == tile2.Type && tile2.Type == tile3.Type)
            {
                matchedTiles.Add(tile1);
                matchedTiles.Add(tile2);
                matchedTiles.Add(tile3);
            }
        }
    }
    
    // Аналогичная проверка по вертикали
    
    return new List<Tile>(matchedTiles);
}

Преимущества подхода с одним MonoBehaviour

  • Централизованное управление — все игровые состояния обрабатываются в одном месте
  • Упрощенная отладка — легче отслеживать состояние игры
  • Минимизация накладных расходов — меньше GameObject'ов со скриптами
  • Четкая архитектура — принуждает к правильному разделению ответственности

Недостатки и ограничения

  • Может стать "божественным объектом" — если не разделять ответственность внутри класса
  • Сложность расширения — добавление новых функций требует модификации основного класса
  • Нарушение SRP — один класс отвечает за слишком много аспектов

Рекомендации по структурированию кода

Для поддержки масштабируемости внутри одного MonoBehaviour рекомендуется использовать вложенные классы или частичные классы:

// Вложенный класс для управления сеткой
[System.Serializable]
public class GridSystem
{
    private Tile[,] grid;
    
    public void Initialize(int width, int height) { /* ... */ }
    public bool IsWithinGrid(Vector2Int pos) { /* ... */ }
    public List<Tile> GetMatches() { /* ... */ }
}

// Использование в основном классе
public class Match3GameManager : MonoBehaviour
{
    private GridSystem gridSystem;
    private InputSystem inputSystem;
    private VisualSystem visualSystem;
    
    private void Start()
    {
        gridSystem = new GridSystem();
        inputSystem = new InputSystem();
        visualSystem = new VisualSystem();
        
        // Настройка взаимодействия между системами
        inputSystem.OnTileSwapped += gridSystem.HandleSwap;
        gridSystem.OnMatchesFound += visualSystem.AnimateMatches;
    }
}

Практические советы

  1. Используйте конечный автомат состояний для четкого управления игровым процессом
  2. Применяйте пулы объектов для оптимизации создания/удаления тайлов
  3. Разделяйте логику и представление даже внутри одного класса
  4. Используйте события для коммуникации между различными аспектами игры
  5. Создайте класс Tile как простой контейнер данных, а всю логику обрабатывайте в GameManager

Такой подход отлично подходит для прототипирования и небольших проектов, но для коммерческих игр рекомендуется более модульная архитектура с несколькими специализированными MonoBehaviour-скриптами.