Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- // По умолчанию все классы создаются с наследованием от класса MonoBehaviour
- // Этот класс отвечает за движение объекта, к которому он прикреплен
- public class MoveTetromino : MonoBehaviour
- {
- // Две обычные публичные переменные
- // rotationPoint - точка поворота фигуры
- // fallTime - интервал между падениями в секундах
- // Такие переменные видны из редактора
- // И их можно настраивать из редактора
- // Также к ним можно обратиться из любого места любого другого скрипта
- // При этом значение таких переменных будет приходить
- // в скрипт со значением, сохраненным в редакторе
- public Vector3 rotationPoint;
- public float fallTime = 0.8f;
- // Обычная приватная переменная
- // Ее видно только из конкретного экземпляра скрипта
- // Другие скрипты и объекты ее увидеть не смогут
- private float previousTime;
- // Публичная статическая переменная
- // Такая переменная одна для всех экземпляров
- // Ее не видно из редактора
- // Но если ее поменять в одном экземпляре скрипта,
- // то она поменяется во всех экземплярах всех объектов
- // к которому прикреплен этот скрипт
- public static int height = 20;
- public static int width = 10;
- // Публичный статический массив
- // В нем хранится один экземпляр поля с занятыми клетками
- // Каждый скрипт (экземпляр) может обращаться
- // к информации о занятых клетках
- // Например, когда линяя собралась - удалять ее
- // А когда фигура упирается вниз, то нужно где-то сохранить,
- // какие ячейки заняты
- // при старте скрипта все ячейки не просто 0, они пустые - null
- // стоит посмотреть на тип Transform, это тип объекта
- // это такой тип хранимых объектов, например, int хранит целые числа
- // в Unity Transform фактически хранит ссылку на любой объект в сцене
- // Transform есть у каждого объекта в сцене
- // название Transform, а не (например) GameObject - видимо наследие из ранних версий Unity
- // поэтому этот тип определят корень объекта также как и GameObject, когда мы хотим обратится к любому
- // объекту в сцене, но в отличии от GameObject и к детям (в GameObject придется написать GameObject.Transform)
- // в боле поздних версиях Unity сделали GameObject, но он не может существовать без Transform
- // поэтому полностью функциональность ссылок на положение в сцене не стали убирать и оставили Transform
- // в итоге, если расшифровать строку ниже, то на русском это будет звучать так
- // приватный (видимый только объекту)
- // статический (один для всех объектов MoveTetromino)
- // типа Transform - хранит ссылки на любой физический объект в сцене
- // [,] двухмерный массив
- // с названием grid
- // при первом вызове grid инициализируется (к нему будет приравнен)
- // новый массив пустых ячеек (null - пустые ссылки, которые не на что не ссылаются)
- // размерность массива [width, height], т.е. 10 на 20, где 10 - столбцы, 20 - строки
- private static Transform[,] grid = new Transform[width, height];
- // Start вызывается до первого нарисованного кадра один раз
- void Start()
- {
- }
- // Update вызывается один раз за кадр
- // При 60 кадрах в секунду вызовется 60 раз
- void Update()
- {
- // Если нажата кнопка влево
- if(Input.GetKeyDown(KeyCode.LeftArrow))
- {
- // Двигаем Объект, к которому прикреплен скрипт на значение переменной
- // Vector3 - удобная организация трех пар чисел в одну переменную
- transform.position += new Vector3(-1, 0, 0);
- // Сдвинули и проверили, что куда сдвинули там не занято
- // Идея в том, что т.к. это происходит в одном кадре,
- // то пользователь не успеет увидеть, что объект подвинули
- // проверили, а потом вернули назад, если фигура не влезла
- if(!ValidMove())
- // Если занято - вернули назад
- transform.position -= new Vector3(-1, 0, 0);
- }
- // Если не нажата кнопка влево, но нажата кнопка вправо
- // Тут все аналогично движению влево
- else if (Input.GetKeyDown(KeyCode.RightArrow))
- {
- transform.position += new Vector3(1, 0, 0);
- if (!ValidMove())
- transform.position -= new Vector3(1, 0, 0);
- }
- // Если не влево и не вправо, то если нажата вверх
- else if (Input.GetKeyDown(KeyCode.UpArrow))
- {
- // Поворачиваем вокруг точки, которая получена из публичной переменной rotationPoint
- // Поворот происходит относительно локальной начальной точки объекта + rotationPoint
- transform.RotateAround(transform.TransformPoint(rotationPoint), new Vector3(0,0,1), 90);
- // аналогично, если поворот приводит к пересечению - повернуть назад
- if (!ValidMove())
- transform.RotateAround(transform.TransformPoint(rotationPoint), new Vector3(0, 0, 1), -90);
- }
- // Тут все просто, движение фигуры вниз
- // Допустим, текущее время относительно старта программы
- // 22,3 секунды - это функция Time.time
- // previousTime - предыдущее время, когда мы попали в тело проверки
- // например, 21,8 секунды
- // fallTime можно изменить из редактора, но по умолчанию это 0,8 секунды
- // 22,3 - 21,5 = 0,5 секунды
- // Если кнопка не нажата, то сравнение идет с 0,8 и 0,5 явно меньше 0,8
- // Если кнопка нажата, то сравнение идет с 0,8 / 10 = 0,08 секунды
- // тут использован тернарный оператор, это как вложенный if
- // тернарный оператор <проверочное значение> ? <если проверочное значение истина> : <если проверочное значение ложь>;
- if (Time.time - previousTime > (Input.GetKey(KeyCode.DownArrow) ? fallTime / 10 : fallTime))
- {
- // аналогично, сначала двигаем вниз
- transform.position += new Vector3(0, -1, 0);
- // если фигкра не поместилась
- // тут логика несколько меняется
- // т.к. при движении вниз нужно зафиксировать фигуру
- if (!ValidMove())
- {
- // сначала возвращаем ее назад, что логично, т.к. в новой позиции она не поместилась
- transform.position -= new Vector3(0, -1, 0);
- // теперь ее нужно зафиксировать фигуру в стакане
- AddToGrid();
- // теперь нужно проверить, не появились ли в стакане собранные линии
- // и удалить их, если такие линии появились
- CheckForLines();
- // теперь эта фигура больше не нужна, ее нужно отключить
- // это неправильное решение, но пока сойдет
- // каждый объект нужно удалить, а не отключить (поставить на паузу)
- // т.к. он немного, но занимает место в памяти
- // это ошибка, которую мы исправим позже
- this.enabled = false;
- // теперь находим объект, у которого есть скрипт, который создает новую фигуру
- // и выполняем его функцию создания новой фигуры
- FindObjectOfType<SpawnTetromino>().NewTetromino();
- }
- // Записываем текущее время относительно старта программы
- previousTime = Time.time;
- }
- }
- // Функция проверки собранности линий
- void CheckForLines()
- {
- // в цикле проходимся по линиям сверху вниз
- // от линии 19 до 0
- // стоит обратить внимание на создание переменной
- // любые циклы позволяют создавать временные переменные
- // которые будут действовать только внутри цикла
- // в данном цикле мы создаем переменную nextLineRow
- // типа int
- for (int nextLineRow = height-1; nextLineRow >= 0; nextLineRow--)
- {
- // Проверям, если в строеке собранная линия
- if(HasLine(nextLineRow))
- {
- // Сначала удаляем объекты из собранной линии
- DeleteLine(nextLineRow);
- // Затем все линии выше опускаем на одну строку вниз
- RowDown(nextLineRow);
- }
- }
- }
- // Функция проверки, что строка собралась
- bool HasLine(int lineRow)
- {
- // проходим по каждому столбцу в строке
- // от 0 до 9
- for(int column = 0; column< width; column++)
- {
- // если наткнулись на пустую ячейку,
- // значит строка точно не заполнена
- if (grid[column, lineRow] == null)
- return false; // выходим из функции, говорим, сто строка не готова
- }
- // если дошли сюда, значит в строке нет пустых ячеек
- // возвращаем истину, строка заполнена
- return true;
- }
- // Функция удаления линии
- void DeleteLine(int deleteLineRow)
- {
- // По очередии от 0 до 9 удаляем объекты из массива
- for (int column = 0; column < width; column++)
- {
- // Вызываем функцию удаления блока из массива по адресу
- // j, i, где i строка, а j столбец
- Destroy(grid[column, deleteLineRow].gameObject);
- // И очищаем значение самой ячейки (null - это признак отсутсвия значения,
- // это как бы ничто)
- grid[column, deleteLineRow] = null;
- }
- }
- // функция смещения данных ячеек сверху вниз на одну строку
- void RowDown(int clearedLineRow)
- {
- // вложенные циклы начинаются
- // со значения строки, которая была очищена
- // до самой верней, которая равна 19
- for (int line = clearedLineRow; line < height; line++)
- {
- // теперь столбец
- for (int column = 0; column < width; column++)
- {
- // у нас по очереди меняется солбец
- // если в ячейке текущей обрабатываемой строки
- // значение не пустое
- if (grid[column,line] != null)
- {
- // перемещаем значение на строку вниз
- grid[column, line - 1] = grid[column, line];
- // текущее значение очищаем
- grid[column, line] = null;
- // двигаем объект (блок, это физический объект) вниз на 1
- // Т.е. в двух строчках выше мы поменяли его положение в таблице
- // как в экселе, сдинув из одной ячеки данных на ячейку ниже
- // а строчкой ниже мы двигаем картинку (спрайт) на экране, у которой есть координаты
- grid[column, line - 1].transform.position -= new Vector3(0, 1, 0);
- }
- }
- }
- }
- // Добавляем блоки фигуры, которая упала, в конкретные ячейки таблицы
- // Которая хранит в каждой ячейке либо пустоту (null), либо
- // блок, который вырван из фигуры (префаба)
- void AddToGrid()
- {
- // Находим каждый блок в фигуре
- // children по очереди станет каждым блоком фигуры
- // Нам даже не нужно знать, сколько блоков в фигуре
- // сколько блоков мы добавили в префаб, столько раз
- // будет выполнена каждая строчка в блоке foreach
- // в данном цикле мы создаем временную переменную children
- // тип у переменной children Transform
- foreach (Transform children in transform)
- {
- // Округляем и сохраняем положение блока по X
- int roundedX = Mathf.RoundToInt(children.transform.position.x);
- // Округляем и сохраняем положение блока по Y
- int roundedY = Mathf.RoundToInt(children.transform.position.y);
- // для значения блока по округленным координатам в таблице
- // вместо пустоты записываем ссылку на объект (блок от фигуры)
- // Ссылка это такая штука, к которой можно дописать точку
- // и все что слева от точки - это ссылка на объект
- // а все что справа от точки - это обращение к элементам объекта
- // например grid[roundedX, roundedY].transform.position
- // и children.transform.position
- // после приравнивания будет действовать одинаково
- // и мы сможем поменять положение позиции объекта
- // т.к. ссылку на него мы храним в каждой нужной ячейке таблицы
- grid[roundedX, roundedY] = children;
- }
- }
- // Проверяем, не попал ли объект на стену или другой объект
- bool ValidMove()
- {
- // для каждого блока в фигуре
- // кстати, у children тоже могут быть вложенные объекты
- // и для них тоже мог бы быть вложенный foreach
- foreach (Transform children in transform)
- {
- // получаем значения положения блока в таблице
- int roundedX = Mathf.RoundToInt(children.transform.position.x);
- int roundedY = Mathf.RoundToInt(children.transform.position.y);
- // проверяем положение блока зы выход за границы стакана
- if(roundedX < 0 || roundedX >= width || roundedY < 0 ||roundedY >= height)
- {
- return false; // блок вышел за границы стакана, ход неправильный
- }
- if (grid[roundedX, roundedY] != null)
- return false; // в ячейке таблицы есть другой объект, ход неправильный
- }
- // сюда мы попадем только в том случае, если каждый блок не пересек стакан
- // и не столкнулся с записанным ранее блоком в таблице
- return true;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement