Объявленные переменные :
№ |
Name |
Type |
Dim. |
Знач. |
Вид. |
Описание |
1 |
Grid |
Int8 |
20 60 |
- |
local |
Переменная хранит размеры игрового поля |
2 |
GridH |
Int32 |
- |
60 |
local |
Высота игрового поля |
3 |
GridW |
Int32 |
- |
20 |
local |
Ширина игрового поля |
4 |
Shapes |
Int32 |
6 |
- |
local |
Массив для хранения 6-ти фигур |
5 |
S1 |
Int32 |
- |
0x202222 |
local |
Геометрия и цвет фигуры 1 |
6 |
S2 |
Int32 |
- |
0x3044c0 |
local |
Геометрия и цвет фигуры 2 |
7 |
S3 |
Int32 |
- |
0x150360 |
local |
Геометрия и цвет фигуры 3 |
8 |
S4 |
Int32 |
- |
0x250c60 |
local |
Геометрия и цвет фигуры 4 |
9 |
S5 |
Int32 |
- |
0x3504e0 |
local |
Геометрия и цвет фигуры 5 |
10 |
S6 |
Int32 |
- |
0x170223 |
local |
Геометрия и цвет фигуры 6 |
12 |
nS |
Int32 |
- |
- |
local |
Геометрия и цвет фигуры после переворота |
13 |
s |
Int32 |
- |
- |
local |
Геометрия и цвет активной фигуры |
14 |
x |
Int32 |
- |
- |
local |
Положение фигуры по оси х |
15 |
y |
Int32 |
- |
- |
local |
Положение фигуры по оси y |
16 |
nx |
Int32 |
- |
- |
local |
Возможное положение фигуры по оси х |
17 |
ny |
Int32 |
- |
- |
local |
Возможное положение фигуры по оси y |
18 |
delay |
double |
- |
- |
local |
Длительность задержки |
19 |
Level |
Int32 |
- |
- |
local |
Уровень |
20 |
Offscreen |
Int32 |
- |
- |
local |
Количество точек фигуры находящ. вне. ИП |
21 |
Score |
Int32 |
- |
- |
local |
Счёт |
22 |
t |
double |
- |
- |
local |
Для орг. Задержки |
23 |
tOld |
double |
- |
- |
local |
Для орг. Задержки |
24 |
ClearedRows |
Int32 |
- |
- |
local |
Количество полностью заполненных строк |
Объявленные процедуры и функции :
№ |
Объявление |
Переменные |
Краткое описание |
||||||||||||||||||||||||||||||||||||
1 |
Num:=Clear_rows() |
|
Функция удаляет полностью заполненных строки - возвращает их количество |
||||||||||||||||||||||||||||||||||||
2 |
c = get(x,y) |
|
Функция возвращает значение Grid[x][y] |
||||||||||||||||||||||||||||||||||||
3 |
r = okay(x,y,s) |
|
Функция проверяет может ли фигура S перемещена в координату x,y |
||||||||||||||||||||||||||||||||||||
4 |
ns = rot(s,right) |
|
Функция производит вращение фигуры S в одном из направлений |
||||||||||||||||||||||||||||||||||||
5 |
e = set(x,y,c) |
|
Устанавливает новое значение Grid[x][y] И Image.Cdata[x,y] |
||||||||||||||||||||||||||||||||||||
6 |
e = show(x,y,s,c) |
|
Обрисовывает фигуру на игровом поле |
Объявленные блоки :
№ |
Имя |
Инструкции |
Описание |
1 |
A |
entry: ml.sf_tetris_gui("paint",level,score); exit: t = ml.cputime(); |
Блок отправляет GUI API параметры текущего уровня и счёт на отрисовку |
2 |
P |
- |
Пустой блок предназначен для установки игры в режим <пауза> и выхода из него |
3 |
Q |
entry: ml.sf_tetris_gui('gameover'); QuitOut; |
Блок завершения игры. GUI API отрисовывает |
Объявленные события :
№ |
Имя |
Тип |
Триггер |
Порт |
Описание |
1 |
Ticks |
Input |
Either |
9 |
Посылает импульсы каждые 2 секунды |
2 |
Left |
Input |
Either |
1 |
Переместить фигуры влево |
3 |
Right |
Input |
Either |
2 |
Переместить фигуры вправо |
4 |
Restart |
Input |
Either |
7 |
Начать заново |
5 |
RotLeft |
Input |
Either |
3 |
Повернуть фигуру против часовой стрелки |
6 |
RotRight |
Input |
Either |
4 |
Повернуть фигуру по часовой стрелки |
7 |
Pause |
Input |
Either |
5 |
Приостановить |
5 |
Drop |
Input |
Either |
8 |
Кинуть фигуру |
6 |
Quit |
Input |
Either edge |
6 |
Выйти |
7 |
QuitOut |
Out |
1 |
Остановка симуляции |
Общие положения.
В качестве основы пользовательского интерфейса игры Tetris была выбран модуль
На котором размещены компоненты : "Image" и 2 компонента "Text"
"Image" используется в качестве игрового поля .
Один из "Text" применяется для вывода информации о состоянии игрового процесса : счёт и уровень ,
другой "Text" для вывода сообщения об окончании игры (Game over).
Игровое поле "Image" имеет свойство "Cdata" - двумерный массив целых чисел , размерностью 60х20 . Он и используется в алгоритмах для отображения , хранения и оперирования фигурами.
Фигура. Всего используемых фигур в игре шесть . Фигура является объектом над которым предусмотрены следующие действия : Перемещение по Х и Y , вращение по часовой и против часовой стрелки. Фигура в программе это 22 битное двоичное число , где в последних 16-ти битах
закодирована геометрия фигуры , а в оставшихся её цвет . Ниже приведены подробная расшифровка фигуры S1 , для остальных 5 фигур всё аналогично
Фигура S1
Hex код фигуры : 0x202222 в двоичной форме [100000][0010][0010][0010][0010]
Цвет : 6 бит |
Фигура 16 бит (4х4) |
||||||||||||||||||||
1 строка |
2 строка |
3 строка |
4 строка |
||||||||||||||||||
Номера битов |
|||||||||||||||||||||
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
Правило построения фигуры :
0 |
1 |
0 |
0 |
Строка № 4 |
0 |
1 |
0 |
0 |
Строка № 3 |
0 |
1 |
0 |
0 |
Строка № 2 |
0 |
1 |
0 |
0 |
Строка № 1 |
Фигура S2 Hex код фигуры : 0x3044C0 Двоичная форма : 110000 0100010011000000 Код цвета : 110000 = 48 |
|
||||||||||||||||
Фигура S3 Hex код фигуры : 0x150360 Двоичная форма : 010101 0000001101100000 Код цвета : 010101 = 21 |
|
||||||||||||||||
Фигура S4 Hex код фигуры : 0x250c60 Двоичная форма : 100101 0000110001100000 Код цвета : 100101= 37 |
|
||||||||||||||||
Фигура S5 Hex код фигуры : 0x3504e0 Двоичная форма : 110101 0000010011100000 Код цвета : 110101= 53 |
|
||||||||||||||||
Фигура S6 Hex код фигуры : 0x170223 Двоичная форма : 010111 0000001000100011 Код цвета : 010111 = 23 |
|
Алгоритм
При инициализации :
{ ml.sf_tetris_gui("init",gridH,gridW);
x = gridW / 2;
y = -2;
delay = .1; // Задержку устанавливаем в 100 миллисекунд
clearedRows = 0;
tOld = 0;
t= 0;
s = shapes[0];
grid = 0; // 60x20 заполняем нулями
level = 0;
score = 0; }
Вызывается функция "init" расположенная в m файле sf_tetris_gui.m , закомментируем основные её действия
switch operation
case 'init',
data = zeros(varargin{1},varargin{2}); // Массив 60x20 заполняется нулями , игровое поле пусто
if ishandle(img) // Если переменная img уже связана и объектом "Image" то ( был Restart )
set(img,'CData',data); //Свойству CDataобъекта Image присваиваем Массив 60x20
else
fig = figure('Name','Tetris', ... 'KeyPressFcn', 'sf_tetris_gui keypress;', ... 'DoubleBuffer', 'on'); oldUnits = get(fig,'Units'); set(fig,'Units','normalized'); ratioXY = double(varargin{2})/double(varargin{1}); height = 0.75; width = ratioXY * height; left = (1 - width) / 2; top = (1 - height)/ 2; set(fig,'Position',[left top width height]); set(fig,'Units',oldUnits); |
|
img = image(data); axis = get(img,'Parent'); titleText = get(axis,'Title'); set(titleText,'fontWeight','bold'); set(axis,'DataAspectRatio',[1 1 1],... 'XTick',[],'YTick',[]); end |
|
if ~isempty(gameoverTxt) && ishandle(gameoverTxt) set(gameoverTxt,'Visible','off'); end |
Далее переменной S присваивается фигура под номером 1 .
После этого переходим в блок A
При входе в блок вызывается функция :
ml.sf_tetris_gui("paint",level,score);
Её задачи : обновить состояние счёта , уровня и для объекта image обновить свойство Cdata.
Её код :
case 'paint', if ishandle(img) switch mod(varargin{1},3) case 0, colorMap = jet; case 1, colorMap = hot; case 2, colorMap = pink; end set(fig,'colormap',colorMap); titleText = get(axis,'Title'); titleString = sprintf('Level %2d Score %10d',double(varargin{1}),double(varargin{2})); set(titleText,'String',titleString); set(img,'Cdata',data); else error('Figure has been closed aborting.'); end
При выходе из блока A в переменную t запишется текущее время . Зная t и tOld был организован таймер , выключение которого происходит при условии t-tOld>=delay или при вызове события Drop
Эта операция делает невидимой фигуру S на время её движения или вращения (очищает занимаемой фигурой клетки игрового поля)
{ offscreen=show(x,y,s,0); Записываем 1 если фигура вне экрана 0 в противном
nx = x; Сохраняем во временных переменных
ny = y;
ns = s; }
Следующий шаг это выбор между событиями
Drop,Left , Right , RotLeft , RotRight , Ticks
Ели игрок вызвал событие Drop То попадаем цикл , который не прекращается до тех пор пока функция Okay возвращает 1 В процессе цикла увеличиваем очки и Координату ny после цикла присваиваем y значение временной переменной ny.
Если было вызвано одно из событий Rotleft , RotRight или Left , Right
Выполняем соответствующие функции После переворота или перемещения вдоль Х проверяем свободно требуемое пространство игрового поля с помощью функции okay , если успех то значения временных переменных nx и ns записываем в текущие x и s
Если игрок не нажимал управляющих кнопок переход произойдёт по событию Ticks
{ny = y + 1} - перемещаем фигуру на клетку ниже {tOld = t} - присваиваем текущей ttOld - для организации задержки. Ели перемещение фигуры в новую координату функцией Okay было разрешено то {y=ny} ,
иначе это значит что фигура достигла препятствие и не может дальше продвигаться.
{ show(x,y,s,s>>16); отрисовываем фигуру на игровом поле
score +=5; прибовляем очки
clearedRows += clear_rows(); - проверяем есть ли полностью заполненные строки .
s = shapes[floor(ml.rand() * 6.0)]; - выбираем фигуру которая будет следующей , случайно
y = -2; - начальные координаты блока фигуры
x = gridW/2; } -//-
Далее если offset больше нуля то это означает что фигура находится вне зоны игрового поля тоесть достигнут предел , переходи в блок Q - остановка симуляции и обрисовка "Game over"
Иначе если число полностью заполненных строк превысило 10 - переходим на новый уровень
{clearedRows = 0; обнуляем достижения
delay *= .75; уменьшаем время задержки на 35 %
level++;} прибавляем уровень
Завершающей операцией итерации является вызов функции show которая отобразит на игровом поле
Фигуре S с координатами X,Y и цветом S>>16 (22-битное число смещённое на 16 разрядов вправо = 6 бит цвета )
Функции.
1. Num:=Clear_rows()
Основные задачи данной функции это :
1. Определить присутствует ли на игровом поле полностью закрашенные строки
2. При наличии закрашенной(ых) строки(к) (за количество отвечает - num) выполнить перерисовку всего игрового поля со смещение на num строк вниз
Алгоритм .
Присваиваем y номер самой нижней строки. Инициализируем num .
До тех пор пока недостигнута строка игрового поля с индексом 1 выполняем y-раз следующие действия. Устанавливаем x в начало строки . До тех пор пока x не превышает длину строки игрового поля проверяем наличие в <строке-кондидате> присутствие не заполненных клеток (нулевых).
Если таковых не найдено во всей строке то увеличиваем num на 1.
Переходим к строке y-1 , и выполняем выше описанную часть. Только на этот раз при условии что num больше нуля переходим не посредственно к перерисовке игрового поля со смещением на num строк в низ.
2. c = get(x,y)
Основная задача данной функции это :
По имеющимся координатам x,y определить число (цвет) хранящийся в ячейке массива Grid.
При выполнении условий : x находится в пределах игрового поля y находится в пределах игрового поля присваиваем "c" содержимое двумерного массива Grid[x][y]
3. r =okay(x,y,s)
Основные задачи данной функции это :
Определить наличие препятствий на пути движения фигуры будь то
А) Движение по вертикали вниз
Б) Движение в обе стороны по горизонтали
В) Вращение по и против часовой стрелки
Как видно из общей схемы данная функция вызывается после каждой операции над фигурой.
Переменные dx и dy это переменные которые передаются в описанную выше функцию get , которая определяет свободная ли ячейка с координатами (x+dx , x+dy) или нет , если место занято (ячейка содержит значение отличное от нуля) то покидаем функцию с нулевым значением.
4. ns =rot(s,right)
Основная задача данной функции это :
Переворот фигуры S по часовой или против часовой стрелки
Операция поворота реализуемая данной функции осуществляется с помощью операций побитного смещения , логических операций AND , все эти манипуляции осуществляются над 16 битным двоичным числом которое описывает геометрию фигуры.
Рассмотрение данных вопросов выходит за рамки данной работы.
5. e =Set(x,y,c)
Основные задачи данной функции это :
1. Определить находится какая-либо часть фигуры вне зоны игрового поля .
2. Установить в ячейку массива Grid с координатами x,y значение из переменной с (установить цвет)
При условиях что и X и Y находятся в пределах игрового поля , выполняем :
1 . Присваиваем Grid[x][y] значение переменной C
2 . Вызываем M-функцию ml.sf_tetris_gui("set",y,x,c)
её код :
case 'set',
data(varargin{1},varargin{2}) = varargin{3};
** data - это указатель на Cdata.
Это мы проводим для того чтобы обновить значение свойства <Cdata> компонента <Image> , который по сути дела является слепком локальной переменной <Grid>
Если же условия вхождения X,Y в зону видимости не выполняются, то в это случае покидаем функцию и переменной <e> присваиваем 1.
5. e =Show(x,y,s,c)
Основные задачи данной функции это :
1. Записать данную фигуры в массив Grid и свойство Cdata объекта image
2. Определить выходит ли как-либо часть фигуры S за пределы игрового поля
Если абстрактно представить себе фигуру , как она интерпретируется в программе то она будет иметь вид :
Координаты Блока фигуры (X,Y) располагается тут
|
Локальные координаты точки фигуры внутри блока фигуры : (dx,dy)
Рассмотрим более подробно эту функцию
В качестве параметра мы получаем x,y - координаты фигуры.
Глобальная координата точки фигуры получается из выражения (x+dx,y+dy)
Цикл продолжается пока верно условие i<16
В этом цикле мы перемещаемся по всем 16 клеткам блока фигуры сверху вниз , слева направо
Путём побитового смещения вправо [(s>>i) & 1] (равен ли младший бит смещённого на i-бит вправо слова s единице (операция логического AND) ).
И там где клетка содержит значение 1 выполняем функцию set(x+dx,y+dy,c)