Очень хочется рассказать про мою самую любимую разработку – игру Тетрис. Начиналось всё с обычной проверки “смогу ли”. Это была как раз производственная практика в колледже, времени было полно. Практику я проходил с другом, по этому он мне подкинул идею добавить в игру бонусы и прочие мелочи.
Ну а дальше больше. Появилась анимация, огромное кол-во бонусов, которые нужно было покупать, отрицательные бонусы, которые либо вызывались “плохой” игрой, либо неудачным выбором.
На данный момент размер основного кода насчитывает более 8 тыс. строк.
Вот объявление основного игрового класса, связывающего все и вся. Он создает все объекты, подгружает графику, связывает события и конечно же отрисовывает всю игру.
TTetrisGame = class //Игровой "двигатель"
private //Личные
aEmpty:Byte; //Пустая ячейка
AlreadyShownButtons:Boolean; //Уже были показаны кнопки (при запуске)
AnswerShut:Byte; //Переменная для ответа при завершении игры
BonusAmountWidth:Byte; //Кол-во бонусов в ширину
CanBeContinue:Boolean; //Игра может быть продолжена
Confirm:Boolean; //Дан ответ или нет
CreatedBoss:Boolean; //Создан босс
Creating:Boolean; //Идет инициализация
DebugEnabled:Boolean; //Отладка
DoDrawFigure:TDrawType; //Состояние редактирования фигуры
Drawing:Boolean; //Идет отрисовка
FBoom:Byte; //Сила тряски
FButtonsHided:Boolean; //Кнопки скрыты
FButtonsHiding:Boolean; //Кнопки скрываются
FCols:Byte; //Кол-во столбцов
FDrawCanvas:TCanvas; //Холст для передачи
FEditBoxActive:Boolean; //Поле для ввода имени активно (Статистика)
FEditBoxText:string; //Текст поля для ввода (Статистика)
FFPS:integer; //Показаталь FPS
FGameKeyCount:Byte; //Кол-во игровых клавиш спец. обработки
FGameState:TGameState; //Состояние игры
FHelper:Boolean; //Бонус - "Помошник"
FHeight:Byte; //Высота ячеек
FieldWidth:Word; //Ширина поля
FieldHeight:Word; //Высота поля
FInfoMode:TInfoMode; //Режим инф. дисплея
FirstUpGold:Boolean; //Первое пополнение золота
FKeyIDDown:Byte; //ИД клавиши вниз
FKeyIDLeft:Byte; //ИД клавиши влево
FKeyIDRight:Byte; //ИД клавиши вправо
FKeyIDRL:Byte; //ИД клавиши поворот влево
FKeyIDRR:Byte; //ИД клавиши поворот вправо
FKeyIDUP:Byte; //ИД клавиши вверх
FLevel:Byte; //Текущий уровень
FLevels:TLevels; //Уровни
FLines:Integer; //Собранные линии
FPauseTutorial:Boolean; //Пауза для туториала
FRows:Byte; //Кол-во строк
FShownTutScene:set of Byte; //Показанные сцены
FSpeedRate:Smallint; //Повышение/понижение скорости
FTimerActivate:Boolean; //Активность таймеров
FWaitAmount:Word; //Кол-во ожиданий
FWidth:Byte; //Ширина ячеек
GameKeys:TGameKeys; //Игровые клавиши
Ghost:Boolean; //Режим "Призрачная фигура"
IDDrawFigure:Byte; //ИД элемента фигуры
OldIM:TInfoMode; //Перыдущий режим инф. дисплея
SLeft:Integer; //Положение поля слева
STop:Integer; //Положение поля справа
TutorialImg:TPNGObject; //Страница туториала
TutorialPos:Integer; //Позиция страницы по X
WasGhostColl:Boolean; //Произошло столкновение "Призрака о дно"
function GetFLevel(Index:Byte):Boolean; //Пройденные уровни
function GetLevel:Byte; //Текущий уровень
function GetScaleLvl:Byte; //Получить значение кол-ва строк %
function KeyIsDown(VK_KEY:Word):Boolean; //Нажата ли клавиша
procedure SetGameState(Value:TGameState); //Установить состояние игры
procedure SetLevel(Value:Byte); //Установить уровень
procedure SetLines(Value:Integer); //Установить кол-во удаленных линий
procedure SetHelper(Value:Boolean); //Установить значение "Помошника"
procedure SetIM(Value:TInfoMode); //Установить режми
procedure SetTimers(Value:Boolean); //Установить активность таймеров
public //Общие
ArrayOfBonuses:TBonuses; //Список бонусов
ArrayOfBosses:TArrayOfFigure; //Массив боссов
ArrayOfButtons:TArrayOfButton; //Список кнопок
ArrayOfClears:TArrayOfClears; //Список удалюящихся элементов
ArrayOfExing:TArrayOfExing; //Список выполненных бонусов
ArrayOfFigure:TArrayOfFigure; //Массив имеющихся фигур
ArrayOfPanels:TArrayOfPanels; //Список панелей
ArrayOfToDo:TBonuses; //Список бонусов на выполнение
BadGameBonusCount:Word; //Кол-во пропущенных бонусов
Bitmaps:TBitmaps; //Графические объекты
CurFigure:TFigure; //Текущая фигура
DoBoom:Boolean; //"Тряска"
DrawBMP:TBitmap; //Холст
ExecutingBonus:Boolean; //Выполняется одноразовый бонус
FigureEnable:Boolean; //Фигура идет
FontTextAutor:TFont; //Шрифт - автор
FontTextBonuses:TFont; //Шрифт - таймер бонусов
FontTextButtons:TFont; //Шрифт - кнопки
FontTextDisplay:TFont; //Шрифт - инф. текст
FontTextGameOver:TFont; //Шрифт - конец игры (ваш счет)
ForcedCoord:TPoint; //Принудительные координаты
ForcedCoordinates:Boolean; //Использовать принудительное смещение поля
FPS:Integer; //Показатель FPS
Gold:Cardinal; //Золото
GoldPos:TPoint; //Позиция золота
Hint:TTextHint; //Подсказка
IsCreating:Boolean; //Идет создание фигуры
IsTheBoss:Boolean; //Идет босс
IsWait:Boolean; //Ожидание
LeftPos:Byte; //Позиция идущей фигуры слева
LinesAmountForLevelUp:Byte; //Количество линия для повышения уровня
MainTet:TTetArray; //Главный массив
MoveTet:TTetArray; //Двигающийся массив
NeedShot:Boolean; //Сделсть снимок
NextFigure:TFigure; //Следуюшая фигура
NextFigureOld:TFigure; //Старая "следующая фигура"
NextID:Integer; //ID следующей фигуры
MarshMode:Boolean; //Режмим "Болото"
Mouse:TPoint; //Курсор
OldLevel:Byte; //Предыдущий уровень
Patched:TFigureItem; //Залатанный элемент
RectForAutor:TRect; //Область для автора т.е. меня =)
RectForBonuses:TRect; //Поле для бонусов
RectForBonusesMouse:TRect; //Поле активности мыши для бонусов
RectForButtons:TRect; //Поле для кнопок
RectForChangeMode:TRect; //Поле для изменения режима инф. дисплея
RectForExed:TRect; //Поле для исп. бонусов
RectForInfoText:TRect; //Поле для текста
RectForNextFigure:TRect; //Поле для след. фигуры
Redraw:TRedrawing; //Класс перерисовки
Score:Cardinal; //Дисплейный счет
Shade:TShade; //"Тень"
ShowCursos:Boolean; //Отрисовка собственного курсора
Shutdowning:Boolean; //Идет завершение игры
Sound:TSound; //Звуковое сопровождение
StartGold:Cardinal; //Стартовое золото
StartSpeed:Word; //Стартовое значение таймера шага
Statistics:TStatistics; //Статистика
TimerAnalysis:TTimer; //Таймер анализирования игры
TimerAnimateFrames:TTimer; //Таймер аимации кадров персонажа
TimerBG:TTimer; //Таймер смены фона
TimerBonus:TTimer; //Таймер истечения времени бонусов
TimerBonusCtrl:TTimer; //Таймер контроля выполнения бонусов
TimerBoom:TTimer; //Таймер обработки тряски
TimerDraw:TTimer; //Таймер отрисовки
TimerStep: TTimer; //Таймер шага фигуры
TimerUpValues:TTimer; //Таймер счета
TheardDraw:TDrawThread; //Отдельный поток для отрисовки
ToGold:Cardinal; //Золото
TopPos:Byte; //Позиция идущей фигуры сверху
ToScore:Cardinal; //Счет
Versa:Boolean; //Обратить управление
UserFPS:Word; //Предел кадров в сек.
ZeroGravity:Boolean; //Невесомость
function BuyAndExcecute(ABonus:TBonus):Integer; //Купить и выполнить бонус (результат - затрата, 0 - не куплен)
function CalcBrokenBonus:Word; //Анализ игрового поля
function CheckCollision(TetArray:TTetArray; ATop:Byte; IsShade:Boolean):Boolean; overload; //Проверить измен. фигуру на столкновение на определенной высоте
function CheckCollision(TetArray:TTetArray; ATop, ALeft:Byte; IsShade:Boolean):Boolean; overload; //Проверить измен. фигуру на столкновение в опред. коорд.
function CheckCollision(TetArray:TTetArray; IsShade:Boolean):Boolean; overload; //Проверить измен. фигуру на столкновение на высоте фигуры
function CheckLine:Boolean; //Проверить на заполнение верхней (скрытой) части поля
function CheckPause:Boolean; //Проверить на пауза и на завершение игры
function CreateBMP(FName:string):TBitmap; overload; //Создать BMP
function CreateBMP(DLL:Cardinal; ID:string):TBitmap; overload; //Создать BMP из ресурса
function CreateBonus(var Figure:TFigure):Boolean; //Создать бонусы в фигуре
function CreateFigure(Deg:Word; FigureName:string):TFigure; //Создание фигуры (преобразование 10 to 2)
function DeleteFilled(Row:Byte):Byte; //Удалить заполненные линии, начиная с Row
function ElemCount(Figure:TFigure):Byte; //Объем фигуры
function GetNextBoss:TFigure; //Получить следующего босса
function GetPreviewFigure:TFigure; //Получить случайную фигуру
function GetRandomFigure:TFigure; //Получить случайную фигуру
function GetSpeed:Word; //Текущая скорость
function GetTop:Byte; //Узнать высоту заполнения поля
function HeightOfFigure(AFigure:TFigure):Byte; //Высота фигуры
function Load:Boolean; //Выполнить загрузку игры
function LoadGame:Boolean; //Загрузка игры
function Max(Val1, Val2:Integer):Integer; //Максимальное из двух значение
function Min(Val1, Val2:Integer):Integer; //Минимальное из двух значение
function RotateFigure(Figure:TFigure; OnSentry:Boolean):TFigure; //Поворот фигуры (со смещением вверх, влево)
function SaveGame:Boolean; //Сохранить игру
function UpGold(Value:Word):Word; //Добавить золота
function UpScore(Value:Integer):Integer; //Добавить к счету
function WidthOfFigure(AFigure:TFigure):Byte; //Ширина фигуры
procedure ActuateTimers; //Активировать необходимые таймеры
procedure AddButton(AButton:TExtButton); //Добавить кнопку
procedure AddFigure(Figure:TFigure; var Dest:TArrayOfFigure); //Добавить фигуру в массив фигур
procedure AddToActionList(Bonus:TBonus); //Добавить бонус к списку для выполнения в свободное время
procedure AddToExed(Bonus:TBonus); //Добавить бонус в список только что исп.
procedure AnimateDelete(ATop:Integer); overload; //Выполнить удаление элементов в строке ATop
procedure AnimateDelete(ATop:Integer; Elems:TFiguresL2); overload; //Выполнить удаление некоторых элементов на высоте ATop
procedure Boom(Size:Byte; BTime:Word); //Тряска
procedure BoomStop; //Остановить тряску
procedure ContinueGame; //Продолжить сохраненную игру
procedure CreateBonuses; //Заполнить список бонусов
procedure CreateButtons; //Заполнить список кнопок
procedure CreateFigures; //Создать фигуры
procedure CreateFonts; //Создание шрифтов
procedure CreateGameKeys; //Создание игровых клавиш
procedure CreateGraphicsAndSound; //Инициализация графики и звука
procedure CreateHints; //Создание подсказок
procedure CreateNextBoss; //Следующая фигура - босс
procedure CreateNextFigure; //Создать следующую фигуру
procedure CreatePanels; //Заполнить список панелей
procedure CreateRectAreas; //Создание областей реагирования
procedure CreateTimers; //Создание таймеров
procedure ChangeBackground(ALevel:Byte); //Сменить фон
procedure ClearAll; //Очистить все поля
procedure ClrChng(var Chng:TTetArray); //Очистить изм. массив
procedure DeactivateAllBonuses; //Деактивировать все бонусы
procedure DecGold(Cost:Word); //Снять золото
procedure DeleteSaved; //Удалить сохр. игру
procedure Draw; //Отрисовка на холсте
procedure DrawBonuses; //Рисовать бонусы
procedure DrawingFigure; //Включить режим редактирования след. фигуры
procedure DrawingFigureClose(State:TGameState); //Выключить режим ред. с выходом из игры
procedure DrawingFigureEnd(State:TGameState); //Выключить режим ред.
procedure ExecuteBonus(Bonus:TBonus); //Выполнить бонус
procedure ExecuteRandomBonus(TB:TTypeBonus); //Выполнить случайный бонус
procedure ExecutingDelete; //Проверить и удалить заполненные строки
procedure Event(e, param1, param2:Integer); //Событие
procedure HideButtons(ShowAnimate:Boolean); //Скрыть кнопки управления (Показывать анимацию скрытия)
procedure KeyDown; //Действие клавиши вниз
procedure KeyLeft; //Действие клавиши влево
procedure KeyRight; //Действие клавиши вправо
procedure KeyRotateLeft; //Действие клавиши поворот влево
procedure KeyRotateRight; //Действие клавиши поворот вправо
procedure KeyUp; //Действие клавиши вверх
procedure LevelUp; //Увеличить уровень
procedure LinesUp(Value:Integer); //Увеличить кол-во удаленных линий
procedure Merger; //Слияние осн. поля и фигуры
procedure Move(Bottom:Byte); //Удалиние линии
procedure MoveMouse; //Убрать мышь с поля
procedure NeedDraw; //Принудительная отрисовка
procedure NewFigure; //Новая фигура
procedure NewGame; //Новая игра
procedure Normalization(var Figure:TFigure); //Нормализовать фигуру
procedure OnBonusExecuted(Bonus:TBonus); //После выполнения бонуса
procedure OnBonusStartExecute(Bonus:TBonus); //Перед выполнением
procedure OnKeyDown(Key:Word; Shift:TShiftState); //При нажатии клавиши
procedure OnKeyPress(Key:Char); //При нажатии клавиши (Символ)
procedure OnMouseDown(Button:TMouseButton; Shift:TShiftState; X, Y: Integer);//При нажатии кнопки мыши
procedure OnMouseMove(Shift:TShiftState; X, Y: Integer); //При перемещении мыши
procedure OnMouseUp(Button:TMouseButton; Shift:TShiftState; X, Y: Integer); //При отпускинии кнопки мыши
procedure PauseGame; //Пауза
procedure Reset; //Сбросить значения
procedure RotateGameFigure(OnSentry:Boolean); //Повернуть текущую фигуру
procedure ShowAnimate(ID:Byte); //Установить нужную анимацию
procedure ShowButtons; //Показать кнопки
procedure ShowFigureChanging(FStart, FEnd:TFigure); //Анимация изменения фигуры
procedure ShowGoldDec(Size:Word); //Показать сколько золота убыло (-Size)
procedure ShowHideButtons; //Показать/скрыть кнопки
procedure Shutdown; //Завершение работы программы
procedure SpeedDown(Value:Byte); //Понизить скорость
procedure SpeedUp(Value:Byte); //Повысить скорость
procedure StepDown; //Упустить фигуру на одну линии (шаг)
procedure StepDownBonuses; //Шаг таймеров бонусов
procedure StepLeft; //Сместить влево
procedure StepRight; //Сместить вправо
procedure StopGame; //Закончить игру
procedure StepUp; //Сместить вверх
procedure TimerAnalysisTimer(Sender: TObject); //Обработка таймера для анализа игры
procedure TimerAnimateFramesTimer(Sender: TObject); //Обработка анимационных кадров
procedure TimerBGTimer(Sender: TObject); //Обработка смены фона
procedure TimerBonusCtrlTimer(Sender: TObject); //Обработка очереди бонусов
procedure TimerBonusTimer(Sender: TObject); //Обработка таймеров бонусов
procedure TimerBoomTimer(Sender: TObject); //Обработка тряски
procedure TimerDownStart; //Запуск таймера движения
procedure TimerDownStop; //Остановка таймера движения
procedure TimerDrawTimer(Sender: TObject); //Обработка отрисовки
procedure TimerStepTimer(Sender: TObject); //Обработка шага
procedure TimerUpValuesTimer(Sender: TObject); //Обработка значений
procedure Tutorial(Scene:Byte); //Туториал определённой сцены
procedure UpdateSpeed; //Обновить скорость движения фигуры
procedure UseAllExcept(IDOfTheFigure:Byte); //Исп. все фигуры кроме одной
procedure UseAllFigures; //Исп. все фигуры
procedure UseOnly(IDOfTheFigure:Byte); //Исп. только один тип фигуры
procedure Wait(MTime:Word); //Подождать
procedure WaitWithoutStop(MTime:Word); //Подождать без остановки игры
procedure WriteDebug(Text:string); //Запись в отладочный файл
property ButtonsHided:Boolean read FButtonsHided; //Скрыты ли кнопки
property Cols:Byte read FCols default 15; //Кол-во столбцов
property EditBoxActive:Boolean read FEditBoxActive; //Активность поля для ввода имени игрока (Статистика)
property GameState:TGameState read FGameState write SetGameState; //Состояние игры
property Helper:Boolean read FHelper write SetHelper; //Помошник
property InfoMode:TInfoMode read FInfoMode write SetIM; //Режим инф. дисп.
property Level:Byte read GetLevel write SetLevel; //Уровень
property Levels[Index :Byte]:Boolean read GetFLevel; //Список уровней
property Lines:Integer read FLines write SetLines; //Собранные и удаленные линии
property Rows:Byte read FRows default 23; //Кол-во строк
property ScaleLvl:Byte read GetScaleLvl; //Шкала линий
property SHeight:Byte read FHeight default 20; //Высота ячеек
property SWidth:Byte read FWidth default 20; //Ширина ячеек
property Timers:Boolean read FTimerActivate write SetTimers; //Активность таймеров
constructor Create(FCanvas:TCanvas); //Конструктор
end;
А вот некоторые типы и классы:
TBitmaps = class; //Графические объекты (картинки) TBonus = class; //Основа бонуса TExtButton = class; //Графическая кнопка (не control) THelpObject = class; //Подсказка TLineClear = class; //Линия удаления (для отрисовки анимированного стирания) TSimpleBonus = class; //Простой бонус TSound = class; //Звуковое сопровождение (потоки звуков, bass.dll) TTetrisGame = class; //Движок игры TTextHint = class; //Простая подсказка TTextPanel = class; //Текстовая панель TTimeBonus = class; //Временной бонус TRedrawing = class; //Клаасс перерисовки TStatistics = class; //Статистика TAppendStat = class; //Математика статистики TGameKey = class; //Игровая клавиша TState = (bsEmpty, bsElement, bsBonus, bsBonusTiming, bsBonusBroken); //Состояние элемента (Пусто, Элемент фигуры, Бонус, Бонус (Таймер), Бонус (Сломан)) TFigureItem = record //Элемент фигуры ID:Byte; //Идентификатор элемента (фигуры) State:TState; //Состояние TimeLeft:Word; //Осталось времени (для бонуса) end; TClearFigureItem = record //Удаляемый элемент X, Y:Extended; //Текущие координаты Speed:Byte; //Скорость Angle:Smallint; //Угол отклонения end; TExing = record //Элемент массива выполненных бонусов Bonus:TBonus; //Бонус Active:Boolean; //Есть ли элемент в сетке end; TElements = array[1..GlSize, 1..GlSize] of TFigureItem; //Матрица элементов фигуры TFigure = record //Одна фигура Allowed:Boolean; //Доступность Elements:TElements; //Элементы фигуры Name:string; //Название end; TStatRecord = record //Запись статистики Gold:Cardinal; //Кол-во золота Score:Cardinal; //Счет Lines:Cardinal; //Кол-во удаленных линии Figure:Cardinal; //Кол-во сброшенных фигур Level:Byte; //Уровень end; TStatTop = record //Запись топ - игрока Name:string[255]; //Имя Score:Cardinal; //Счет end; TGameKeyInfo = record //Структура информации о клавише Code:Word; //Код клавиши FForceDown:Word; //Время в нажатом состоянии (мс) Name:string; //Название end; TArrayOfButton = array of TExtButton; //Массив кнопок TArrayOfClears = array[1..GlRows] of TLineClear; //Удаляющие классы TArrayOfExing = array[1..GlExes] of TExing; //Список выполннеых бонусов TArrayOfFigure = array of TFigure; //Массив имеющихся фигур TArrayOfPanels = array of TTextPanel; //Массив текстовых панелей TBonuses = array of TBonus; //Список бонусов TBonusType = (btForAll, btForGame, btForUser); //Тип доступа к бонусам TDrawType = (dtNotWork, dtNone, dtElement, dtEmpty); //Режим рисования (Не рисуем, ожидание, рисуем, стираем) TFiguresL1 = array[1..GlCols] of TClearFigureItem; //Список удаляющихся элементов TFiguresL2 = array[1..GlCols] of TFigureItem; //Список удаляемых элементов TGameKeys = array of TGameKey; //Список игровых клавиш TButtonState = (bsNormal, bsOver, bsDown); //Состояние кнопки TButtonType = (btFigure, btPicture); //Тип кнопки (Фигура, рисунок) TGameState = (gsNone, gsPlay, gsPause, gsStop, gsDrawFigure, gsShutdown); //Состояние игры (Нет, Идет игра, На паузе, Конец игры, Рисует фигуру, Завершение игры) TInfoMode = (imInfoText, imBonuses, imTransforming); //Режим инф. дисплея TLevels = array[1..GlLvls] of Boolean; //Массив уровней TPoints = array[0..255] of TPoint; //Массив точек для полигона (для кнопки) TProcedure = procedure of object; //Процедура TTetArray = array[1..GlRows, 1..GlCols] of TFigureItem; //Основной тип - поле TTypeBonus = (tbCare, tbBad, tbGood); //Вид бонуса (Нейтральный, отрицательный, положительный)
Весь код, от начала до конца прокомментирован и объяснён (за исключением повторений и мелочей).
Как и все мои остальные графические приложения, игра Тетрис работает без стороннего графического движка, лишь средствами GUI и некоторых наработок и хитростей. Например, здесь отрисовка выполнена в отдельном потоке “TDrawThread” (при чем без синхронизации). Также управление осуществляется не с помощью событий от ОС, а по средством проверки нажатия клавиш классом “TGameKey” для каждой необходимой клавиши:
procedure TTetrisGame.CreateGameKeys;
function AddKey(KC:Word; UN:string; AAction:TProcedure; FTime, MTime:Word):Byte;
begin
SetLength(GameKeys, Length(GameKeys) + 1);
Result:=Length(GameKeys) - 1;
GameKeys[Result]:=TGameKey.Create(Self);
with GameKeys[Result] do
begin
with GameKeyInfo do
begin
Code:=KC;
Name:=UN;
end;
Action:=AAction;
StartTime:=FTime;
MinTime:=MTime;
end;
end;
begin
//Создаем специальные игровые клавиши управления
FKeyIDDown:= AddKey(VK_DOWN, 'Вниз', KeyDown, 100, 20); //Стрелка вниз/"S"
FKeyIDUP:= AddKey(VK_UP, 'Вверх', KeyUp, 150, 50); //Стрелка вверх/"W"
FKeyIDLeft:= AddKey(VK_LEFT, 'Влево', KeyLeft, 150, 50); //Стрелка влево/"A"
FKeyIDRight:=AddKey(VK_RIGHT, 'Вправо', KeyRight, 150, 50); //Стрелка вправо/"D"
FKeyIDRL:=AddKey(Ord('Q'), 'Поворот фигуры против часовой стрелки', KeyRotateLeft, 200, 50);
FKeyIDRR:=AddKey(Ord('E'), 'Поворот фигуры по часовой стрелке', KeyRotateRight, 200, 50);
//Укажем количество клавиш
FGameKeyCount:=Length(GameKeys);
//Установка противостоящих клавиш
GameKeys[FKeyIDDown].Contradiction:=FKeyIDUP;
GameKeys[FKeyIDUP].Contradiction:=FKeyIDDown;
GameKeys[FKeyIDLeft].Contradiction:=FKeyIDRight;
GameKeys[FKeyIDRight].Contradiction:=FKeyIDLeft;
GameKeys[FKeyIDRL].Contradiction:=FKeyIDRR;
GameKeys[FKeyIDRR].Contradiction:=FKeyIDRL;
end;
Что позволило реализовать ещё и степень “нажатия” на клавишу, подсчетом времени её в нажатом состоянии. Также это повысило отклик нажатий и устранило обработку окном и прочими элементами управления, которые могли бы этому помешать.
Таблица бонусов:
| № п/п | Название | Описание | Оценка полезности | Приватность | $ | Уровни |
| 1 | Заполнение | Заполнить всё поле | 120,00 | Только игрок | 1000 | 1-15 |
| 2 | Очистка | Очистить всё поле | 110,00 | Только игрок | 500 | 1-15 |
| 3 | Удар-пресс | Сместить элементы вниз в пустые места | 100,00 | Общий | 300 | 4-15 |
| 4 | Невесомость | Включить или выключить гравитацию | 100,00 | Только игрок | 250 | 1-13 |
| 5 | Град | Рассыпать бонусы по фигурам | 90,00 | Общий | 1300 | 1-13 |
| 6 | Призрак | Позволяет проходить сквозь элементы | 55,00 | Общий | 200 | 1-13 |
| 7 | Карандаш | Позволяет отредактировать следующую фигуру | 50,00 | Только игрок | 200 | 1-15 |
| 8 | Дубликат | Использовать только следующий тип фигур (20 сек) | 40,00 | Общий | 150 | 1-13 |
| 9 | Пластырь | Залатать все одиночные «дыры» | 30,00 | Общий | 100 | 2, 5-15 |
| 10 | Помощник | Отображает конечный пункт фигуры (2 мин) | 20,00 | Общий | 90 | 1-15 |
| 11 | Фигура | Сменить следующую фигуру | 20,00 | Общий | 80 | 1-15 |
| 12 | Болото | Активирует/деактивирует режим “Болото” | 15,00 | Общий | 70 | 1-15 |
| 13 | Нож | Срезает самую верхнюю часть | 10,00 | Общий | 50 | 1, 3-15 |
| 14 | Лазер | Срезает верхний слой каждого столбца | 5,00 | Общий | 20 | 1-6, 10-15 |
| 15 | Исключение | Исключить один тип фигур (40 сек) | 0,00 | Только игра | – | 1-13 |
| 16 | Босс | Вызвать босса текущего уровня | -5,00 | Только игра | – | 1-15 |
| 17 | Дождь | Разбросать элементы поля | -5,00 | Только игра | – | 1-13 |
| 18 | Дробь | Делает выстрел дробью | -10,00 | Только игра | – | 1-15 |
| 19 | Ой, ой | Меняет местами кнопки управления | -20,00 | Только игра | – | 1-13 |
| 20 | Вылазка | Выдавить фигуру со стороны поля | -30,00 | Только игра | – | 1-15 |
| 21 | Дьявол | Сам дьявол управляет фигурой (22 сек) | -40,00 | Только игра | – | 1-15 |
Скриншоты игры
С каждым новым уровнем вы не только будете переживать падение замысловатой фигуры -босса, но и увидите новые пейзажи.






Совместно с написанием игры вёлся лог, который можно скачать здесь.

А теперь импортни ее на андройд,введи монетизацию и профит получай.Проект ж реально лютый,на андройде такие тайм киллеры в топах висят. А у тебя качественно и красиво реализовано.
Если реализовать игру без графического движка – как здесь – она будет очень сильно глючить даже на самом мощном смартфоне. В противном случае нужно изучать движок, например “Unity” или может какой-нибудь другой. При импорте этой игры из всего кода сохранится лишь идея.
а что тебя сдерживает оптимизировать её под смарты?
Нужно движок какой-нибудь изучать. А времени нет. Да и желания тоже.