РГР. Лисова_РГР. Расчётнографическая работа на тему Pacman
Скачать 58.26 Kb.
|
Министерство образования и науки РФ ФГБОУ ВПО «Омский государственный технический университет» Кафедра «Информатика и вычислительная техника» Расчётно-графическая работа на тему: Pacman Выполнил: студент гр. Лисова. А Проверил: ст. преподаватель, Чебаненко Е.В Омск 2020 ОглавлениеВведение 3 Описание кода 4 Реализация кода 14 Файл Unit1.cpp 14 Файл Unit2.cpp 25 Файл Project1.cpp 25 Файл Unit1.h 26 Файл Unit2.h 27 ВведениеСледующий пример кода — ремейк игры Pacman Брайана Постмы вышедшая в далеком 1979 году, и уже очень скоро она завоевала огромную популярность. Цель игры состоит в том, чтобы собрать все очки в лабиринте и избежать призраков. Pacman анимирован в двух направлениях: его положение в лабиринте и его тело. Мы оживляем его тело четырьмя изображениями, в зависимости от направления. Анимация используется для создания иллюзии Pacman открытия и закрытия рта. У Пакмана три жизни. Мы также считаем счет. Игра состоит из нескольких файлов. Реализовать: 1. Обязательные функции 1.1 На игровом поле не менее 50% экрана (можно в коде программы установить фиксированный большой размер) случайным образом расставляются «кусты» и «камни», по одному в ячейке. Пакмен, занимающий одну ячейку, управляется стрелками клавиатуры, может стоять на месте, «поедает» кусты (куст при этом исчезает) и получает за это очки. При столкновении с камнем – теряет жизнь. 1.2 Пакмен анимирован, по крайней мере открывает/закрывает рот, когда движется. Описание кодаПервые переменные являются константами и хранят информацию для работы с картой. Такие как высота и ширина поля, скорость и количества ячеек на поле. А также изменение координат по оси x и y. const byte Height = 13; const byte Width = 13; const int CellSize = 32; const int constMaxWay = CellSize / 3; const byte mpNone = 0; const byte mpSeed = 1; const byte mpEnergizer = 2; const byte mpPacmanStart = 4; const int dx[4] = {1, 0, -1, 0}; const int dy[4] = {0, -1, 0, 1}; Функция BreakWall (), это вспомогательная функция, которая отвечает за разбивание стен на уровне. oid BreakWall(int x, int y, int dx, int dy) //вспомогательная функция. разбивает стену { if (dx == -1) Maze[x][y].left_wall = '0'; if (dx == 1) Maze[x + 1][y].left_wall = '0'; if (dy == -1) Maze[x][y].up_wall = '0'; if (dy == 1) Maze[x][y + 1].up_wall = '0'; } В свою очередь функция PrimGenerateMaze() уже полностью генерирует уровень по алгоритму Прима. Устанавливая границы поля и выбирает локацию, с которой начнётся генерация. void PrimGenerateMaze() { Form1->Label1->Caption = "Идет генерация лабиринта по алгоритму Прима"; const int Inside = 1; const int Border = 2; const int Outside = 0; int x, y; int xc, yc; int xloc, yloc; bool IsEnd; int counter; for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) { Maze[x][y].attr = Outside; Maze[x][y].left_wall = '1'; Maze[x][y].up_wall = '1'; } for (byte i = 0; i < Height; i++) { Maze[i][Height].up_wall = '1'; Maze[Height][i].left_wall = '1'; } x = random(Width); y = random(Height); Maze[x][y].attr = Inside; for (int i = 0; i < 4; i++) { xc = x + dx[i]; yc = y + dy[i]; if ((xc >= 0) && (yc >= 0) && (xc < Width) && (yc < Height)) Maze[xc][yc].attr = Border; } do { IsEnd = true; counter = 0; for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) if (Maze[x][y].attr == Border) counter++; counter = random(counter) + 1; for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) if (Maze[x][y].attr == Border) { counter--; if (counter == 0) { xloc = x; yloc = y; goto ExitFor1; } } ExitFor1: Maze[xloc][yloc].attr = Inside; counter = 0; for (int i = 0; i < 4; i++) { xc = xloc + dx[i]; yc = yloc + dy[i]; if ((xc >= 0) && (yc >= 0) && (xc < Width) && (yc < Height)) { if (Maze[xc][yc].attr == Inside) counter++; if (Maze[xc][yc].attr == Outside) Maze[xc][yc].attr = Border; } } counter = random(counter) + 1; for (int i = 0; i < 4; i++) { xc = xloc + dx[i]; yc = yloc + dy[i]; if ((xc >= 0) && (yc >= 0) && (xc <= Width) && (yc <= Height) && (Maze[xc][yc].attr == Inside)) { counter--; if (counter == 0) BreakWall(xloc, yloc, dx[i], dy[i]); } } for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) if (Maze[x][y].attr == Border) { IsEnd = false; goto ExitFor2; } ExitFor2: ShowMaze(); Sleep(50); Application->ProcessMessages(); } while (!IsEnd); } Функция ShowMaze() рисует лабиринт и отображает его в форме Canvas ImageBack->Canvas->Brush->Color = clNavy; ImageBack->Canvas->Pen->Color = clYellow; ImageBack->Canvas->FillRect(Rect(0,0,Width * CellSize + 1, Height * CellSize + 1)); Дальше в той же функции идет проверка на есть ли на локации стена, если да, то мы рисуем их со всех сторон. if (Maze[i][j].left_wall == '1') { ImageBack->Canvas->MoveTo(i * CellSize, j * CellSize); ImageBack->Canvas->LineTo(i * CellSize, (j + 1) * CellSize); } if (Maze[i][j].up_wall == '1') { ImageBack->Canvas->MoveTo(i * CellSize, j * CellSize); ImageBack->Canvas->LineTo((i+1) * CellSize, j * CellSize); } Здесь рисуем кружки на поле при генерации лабиринта. { ImageBack->Canvas->Brush->Color = clRed; ImageBack->Canvas->Ellipse((i + 0.5) * CellSize - 5, (j + 0.5) * CellSize - 5,(i + 0.5) * CellSize + 5, (j + 0.5) * CellSize + 5); } } Form1->Canvas->Draw(0,0,ImageBack); Следующим рассмотрим метод CreateMap() Генерируем карту и заполняем всё её пространство, в углах ставим бонусы, а по центру pacman. Так же устанавливаем начальные параметры. void CreateMap() { LevelSeed = 0; LevelEnergizer = 0; Score = 0; Maze[0][0].map = mpEnergizer; Maze[0][Width-1].map = mpEnergizer; Maze[Height-1][0].map = mpEnergizer; Maze[Height-1][Width-1].map = mpEnergizer; LevelEnergizer = 4; Maze[Height/2][Width/2].map = mpPacmanStart; for (byte i = 0; i < Height; i++) for (byte j = 0; j < Width; j++) if (Maze[i][j].map == mpNone) { Maze[i][j].map = mpSeed; LevelSeed++; } } Функция ShowMap() рисует в ImageBack, уже сгенерированный лабиринт, карту. И устанавливает pacman начальные координаты. И вызываем метод CheckStartPlace() именно она определяет начальное положение игрока. void ShowMap() { for (byte i = 0; i < Height; i++) for (byte j = 0; j < Width; j++) switch (Maze[i][j].map) { case mpSeed : Form1->Sprites->Draw(ImageBack->Canvas, i * CellSize, j * CellSize, 60); break; case mpEnergizer : Form1->Sprites->Draw(ImageBack->Canvas, i * CellSize, j * CellSize, 61); break; case mpPacmanStart : Pacman.StartX = i * CellSize; Pacman.StartY = j * CellSize; break; } CheckStartPlace(); } И oid CheckStartPlace() { Pacman.X = Pacman.StartX; Pacman.Y = Pacman.StartY; Pacman.Dir = 3; Pacman.NewDir = 3; for (int i=0; i<4; i++) Pacman.DirFlag[i] = false; Pacman.Active = false; Pacman.ActiveTime = 0; for (int a = 0; a < 4; a++) { Ghost[a].X = a * CellSize; Ghost[a].Y = a * CellSize; Ghost[a].Dir = 3; for (int i = 0; i < 4; i++) Ghost[a].DirFlag[i] = false; Ghost[a].Shy = false; Ghost[a].ShyTime = 0; } Bonus.Kind = 0; Bonus.Life = 0; } Метод LevelCreate() делает постобработку сгенерированного лабиринта и разбивает стены чтобы там появились проходы для pacman. Если путь из локации в соседнюю будет больше по убираем стену между локациями. oid LevelCreate() { Form1->TimersRun(false); PrimGenerateMaze(); Sleep(200); for (int x = 1; x < Width; x++) for (int y = 1; y < Height; y++) { WaveTracingSolve(x,y); for (int i = 0; i < 4; i++) if (Maze[x + dx[i]][y + dy[i]].mark > constMaxWay) { BreakWall( x, y, dx[i], dy[i]); ShowMaze(); Sleep(100); } } CreateMap(); ShowMap(); MediaPlayer[sNextLevel]->Play(); Form1->TimersRun(true); } Класс TBonus() содержит в себе параметры для бонусов, которые может подбирать игрок. Бонусы имеет 4 вида с разными эффектами. class TBonus { public: int Kind; int Time; int Life; void Show() { if (Kind == 0) { switch (random(10)) { case 0: case 1: case 2: case 3: Kind = 4; break; case 4: case 7: case 8: Kind = 3; break; case 5: case 6: Kind = 2; break; case 9: Kind = 1; break; } Time = (5 + random(10)) * 100; } else Time--; if ((Kind >= 0) && (Time == 0)) { MediaPlayer[sShowBonus]->Play(); Life = 1000; } if (Life != 0) { Form1->Sprites->Draw(ImageBack->Canvas,Height/2 * CellSize,Width /2 * CellSize,Kind + 61,true); Life--; if (Life == 0) Kind = 0; } } } Bonus; Метод TPacman() содежрит в себе все параметры pacman, его начальные координаты X и Y. Скорость движения и изменения координат на поле. class TPacman { public: int X; int Y; int StartX; int StartY; int Dir; int NewDir; int SpriteNum; bool Active; int ActiveTime; bool DirFlag[3]; void VerifyDir() { if (Maze[X/CellSize][Y/CellSize].left_wall == '1') DirFlag[0] = false; else DirFlag[0] = true; if (Maze[X/CellSize][Y/CellSize].up_wall == '1') DirFlag[1] = false; else DirFlag[1] = true; if (Maze[X/CellSize + 1][Y/CellSize].left_wall == '1') DirFlag[2] = false; else DirFlag[2] = true; if (Maze[X/CellSize][Y/CellSize + 1].up_wall == '1') DirFlag[3] = false; else DirFlag[3] = true; } void Go() { Form1->Sprites->Draw(ImageBuffer->Canvas, X, Y, Dir * 3 + SpriteNum); if ((X % CellSize == 0) && (Y % CellSize == 0)) { Dir = NewDir; if (Maze[X/CellSize][Y/CellSize].mark != 1) WaveTracingSolve(X/CellSize,Y/CellSize); switch (Maze[X/CellSize][Y/CellSize].map) { case mpSeed : MediaPlayer[sEatSeed]->Play(); Score++; Maze[X/CellSize][Y/CellSize].map = mpNone; break; case mpEnergizer : MediaPlayer[sEatEnergizer]->Play(); Score += 5; Active = true; ActiveTime = 20; Maze[X/CellSize][Y/CellSize].map = mpNone; break; case mpPacmanStart : if (Bonus.Life != 0) { MediaPlayer[sEatBonus]->Play(); Active = true; ActiveTime = 20; Bonus.Kind = 0; Bonus.Life = 0; } break; } LevelRenewSeed(); VerifyDir(); } if (DirFlag[Dir] == true) switch (Dir) { case 0: X--; break; case 1: Y--; break; case 2: X++; break; case 3: Y++; break; } } } Pacman; Метод TGhost() содежрит в себе все параметры ghost, его начальные координаты X и Y. Скорость движения и изменения координат на поле и их количество. class TGhost { public: int X; int Y; int StartX; int StartY; int Dir; int SpriteNum; bool Shy; int ShyTime; int CurrN; bool DirFlag[3]; void NextStep() { if ((Maze[X/CellSize - 1][Y/CellSize].mark == CurrN - 1) && (Maze[X/CellSize][Y/CellSize].left_wall == '0')) Dir = 0; if ((Maze[X/CellSize][Y/CellSize-1].mark == CurrN - 1) && (Maze[X/CellSize][Y/CellSize].up_wall == '0')) Dir = 1; if ((Maze[X/CellSize + 1][Y/CellSize].mark == CurrN - 1) && (Maze[X/CellSize+1][Y/CellSize].left_wall == '0')) Dir = 2; if ((Maze[X/CellSize][Y/CellSize+1].mark == CurrN - 1) && (Maze[X/CellSize][Y/CellSize+1].up_wall == '0')) Dir = 3; } void VerifyDir() { if (Maze[X/CellSize][Y/CellSize].left_wall == '1') DirFlag[0] = false; else DirFlag[0] = true; if (Maze[X/CellSize][Y/CellSize].up_wall == '1') DirFlag[1] = false; else DirFlag[1] = true; if (Maze[X/CellSize + 1][Y/CellSize].left_wall == '1') DirFlag[2] = false; else DirFlag[2] = true; if (Maze[X/CellSize][Y/CellSize + 1].up_wall == '1') DirFlag[3] = false; else DirFlag[3] = true; } void Go(int a) { if ((X % CellSize == 0) && (Y % CellSize == 0)) { VerifyDir(); if (random(a*2) == 0) {CurrN = Maze[X/CellSize][Y/CellSize].mark; NextStep();} else CurrN++; } if ((Shy == false) && (DirFlag[Dir] == true)) switch (Dir) { case 0: X--; break; case 1: Y--; break; case 2: X++; break; case 3: Y++; break; } } } Ghost[3]; Пример выполнения программы Рисунок 1. Выполнение программы PAC-MAN Реализация кодаФайл Unit1.cpp#include #include #include #include #include #include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //---------------------КОНСТАНТЫ--------------------------------------------- //для работы с картой const byte Height = 13; const byte Width = 13; const int CellSize = 32; const int constMaxWay = CellSize / 3; const byte mpNone = 0; const byte mpSeed = 1; const byte mpEnergizer = 2; const byte mpPacmanStart = 4; const int dx[4] = {1, 0, -1, 0}; // смещения, чтобы определить const int dy[4] = {0, -1, 0, 1}; // какую стену разбивать в BreakWall //для звуков TMediaPlayer *MediaPlayer[10]; const byte sMove = 0; const byte sEatSeed = 1; const byte sEatEnergizer = 2; const byte sShowBonus = 3; const byte sEatBonus = 4; const byte sEatGhost = 5; const byte sDie = 6; const byte sLoose = 7; const byte sWin = 8; const byte sNextLevel = 9; //---------------------ПЕРЕМЕННЫЕ-------------------------------------------- int N = 0; int LevelSeed = 0; int LevelEnergizer = 0; int Score = 0; //для графики Graphics::TBitmap* ImageBuffer; // буфер (все в него перед выводом на форму) Graphics::TBitmap* ImageSprites; //картинка со спрайтами Graphics::TBitmap* ImageBack; //фон + лабиринт + неизменяемые элементы //--------------------------------------------------------------------------- struct Location { byte left_wall; //флаг наличия у данной локации левой и верхней стен byte up_wall; //правая и нижняя вычисляются через соседние локации int attr; //Outside = 0, Inside = 1, Border = 2. int mark; // хранят N при волновой трассировке int map; // карта } Maze[Height+1][Width+1]; // +1 чтобы у ряда [Height][] была нижняя стена, а у [][Width] правая //--------------------------------------------------------------------------- class TBonus { public: int Kind; // вид бонуса int Time; // время до появления следующего int Life; // время "жизни" бонуса void Show() { if (Kind == 0) { switch (random(10)) { case 0: case 1: case 2: case 3: Kind = 4; break; // 40% case 4: case 7: case 8: Kind = 3; break; // 30% case 5: case 6: Kind = 2; break; // 20% case 9: Kind = 1; break; // 10% } Time = (5 + random(10)) * 100; } else Time--; if ((Kind >= 0) && (Time == 0)) { MediaPlayer[sShowBonus]->Play(); Life = 1000; } if (Life != 0) { Form1->Sprites->Draw(ImageBack->Canvas,Height/2 * CellSize,Width /2 * CellSize,Kind + 61,true); Life--; if (Life == 0) Kind = 0; } } } Bonus; //--------------------------------------------------------------------------- class TPacman { public: int X; int Y; int StartX; int StartY; int Dir; int NewDir; int SpriteNum; bool Active; int ActiveTime; bool DirFlag[3]; void VerifyDir() { if (Maze[X/CellSize][Y/CellSize].left_wall == '1') DirFlag[0] = false; else DirFlag[0] = true; if (Maze[X/CellSize][Y/CellSize].up_wall == '1') DirFlag[1] = false; else DirFlag[1] = true; if (Maze[X/CellSize + 1][Y/CellSize].left_wall == '1') DirFlag[2] = false; else DirFlag[2] = true; if (Maze[X/CellSize][Y/CellSize + 1].up_wall == '1') DirFlag[3] = false; else DirFlag[3] = true; } void Go() { Form1->Sprites->Draw(ImageBuffer->Canvas, X, Y, Dir * 3 + SpriteNum); if ((X % CellSize == 0) && (Y % CellSize == 0)) { Dir = NewDir; if (Maze[X/CellSize][Y/CellSize].mark != 1) WaveTracingSolve(X/CellSize,Y/CellSize); switch (Maze[X/CellSize][Y/CellSize].map) { case mpSeed : MediaPlayer[sEatSeed]->Play(); Score++; Maze[X/CellSize][Y/CellSize].map = mpNone; break; case mpEnergizer : MediaPlayer[sEatEnergizer]->Play(); Score += 5; Active = true; ActiveTime = 20; Maze[X/CellSize][Y/CellSize].map = mpNone; break; case mpPacmanStart : if (Bonus.Life != 0) { MediaPlayer[sEatBonus]->Play(); Active = true; ActiveTime = 20; Bonus.Kind = 0; Bonus.Life = 0; } break; } LevelRenewSeed(); VerifyDir(); } if (DirFlag[Dir] == true) switch (Dir) { case 0: X--; break; case 1: Y--; break; case 2: X++; break; case 3: Y++; break; } } } Pacman; //--------------------------------------------------------------------------- class TGhost { public: int X; int Y; int StartX; int StartY; int Dir; int SpriteNum; bool Shy; int ShyTime; int CurrN; bool DirFlag[3]; void NextStep() { if ((Maze[X/CellSize - 1][Y/CellSize].mark == CurrN - 1) && (Maze[X/CellSize][Y/CellSize].left_wall == '0')) Dir = 0; if ((Maze[X/CellSize][Y/CellSize-1].mark == CurrN - 1) && (Maze[X/CellSize][Y/CellSize].up_wall == '0')) Dir = 1; if ((Maze[X/CellSize + 1][Y/CellSize].mark == CurrN - 1) && (Maze[X/CellSize+1][Y/CellSize].left_wall == '0')) Dir = 2; if ((Maze[X/CellSize][Y/CellSize+1].mark == CurrN - 1) && (Maze[X/CellSize][Y/CellSize+1].up_wall == '0')) Dir = 3; } void VerifyDir() { if (Maze[X/CellSize][Y/CellSize].left_wall == '1') DirFlag[0] = false; else DirFlag[0] = true; if (Maze[X/CellSize][Y/CellSize].up_wall == '1') DirFlag[1] = false; else DirFlag[1] = true; if (Maze[X/CellSize + 1][Y/CellSize].left_wall == '1') DirFlag[2] = false; else DirFlag[2] = true; if (Maze[X/CellSize][Y/CellSize + 1].up_wall == '1') DirFlag[3] = false; else DirFlag[3] = true; } void Go(int a) { if ((X % CellSize == 0) && (Y % CellSize == 0)) { VerifyDir(); if (random(a*2) == 0) {CurrN = Maze[X/CellSize][Y/CellSize].mark; NextStep();} else CurrN++; } if ((Shy == false) && (DirFlag[Dir] == true)) switch (Dir) { case 0: X--; break; case 1: Y--; break; case 2: X++; break; case 3: Y++; break; } } } Ghost[3]; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //---------------------ФУНКЦИИ-ФОРМЫ----------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { Form1->ClientWidth = ::Width * CellSize + 1; Form1->ClientHeight = ::Height * CellSize + Form1->Label1->Height + 1; for (int i = 0; i < 10; i++) { MediaPlayer[i] = new TMediaPlayer(this); MediaPlayer[i]->Parent = this; MediaPlayer[i]->Visible = false; MediaPlayer[i]->FileName ="sound" + IntToStr(i) + ".wav"; MediaPlayer[i]->Open(); } ImageBack = new Graphics::TBitmap(); ImageBack->Width = ::Width * CellSize + 1; ImageBack->Height = ::Height * CellSize + 1; ImageBuffer = new Graphics::TBitmap(); ImageBuffer->Width = ImageBack->Width; ImageBuffer->Height = ImageBack->Height; Randomize; Form1->DoubleBuffered = true; TimersRun(false); } //---------------------МЕНЮ-ФОРМЫ-------------------------------------------- void __fastcall TForm1::MenuNewClick(TObject *Sender) { LevelCreate(); } //--------------------------------------------------------------------------- void __fastcall TForm1::MenuExitClick(TObject *Sender) { Form1->Close(); } //---------------------ОБРАБОТКА-КЛАВИШ---------------------------------- void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if ((Key >= 37) && (Key <= 40)) Pacman.NewDir = Key - 37; } //---------------------ТАЙМЕРЫ----------------------------------------------- void __fastcall TForm1::TimerMoveTimer(TObject *Sender) { ImageBuffer->Canvas->Draw(0,0,ImageBack); // выводим все в буфер Bonus.Show(); Pacman.Go(); for (int a = 0; a < 4; a++) { Ghost[a].Go(a); if (Ghost[a].Shy == false) Form1->Sprites->Draw(ImageBuffer->Canvas, Ghost[a].X, Ghost[a].Y, 12 * (a + 1) + Ghost[a].Dir * 3 + Ghost[a].SpriteNum); else Form1->Sprites->Draw(ImageBuffer->Canvas, Ghost[a].X, Ghost[a].Y, 12 * (random(3) + 1) + Ghost[a].Dir * 3 + Ghost[a].SpriteNum); if (((abs(Pacman.Y - Ghost[a].Y) < 10) && (abs(Pacman.X - Ghost[a].X) < 10)) && (((Pacman.X % CellSize == 0) && (Ghost[a].X % CellSize == 0)) || ((Pacman.Y % CellSize == 0) && (Ghost[a].Y % CellSize == 0)))) { if (Pacman.Active == true) { MediaPlayer[sEatGhost]->Play(); Ghost[a].Shy = true; Ghost[a].ShyTime = 15; } else { MediaPlayer[sDie]->Play(); Form1->TimersRun(false); MessageDlg(" Вас съели !!! ", mtInformation, TMsgDlgButtons() << mbOK, 0); CheckStartPlace(); Form1->TimersRun(true); } } } Label1->Caption = "Набрать очков для победы=" + IntToStr(LevelSeed + LevelEnergizer * 5) + "; Набрано очков=" + IntToStr(Score) + "; Время неуязвимости=" + IntToStr(Pacman.ActiveTime); // если съедены все зерна в уровне, то он пройден - грузим следующий if (Score == LevelSeed + LevelEnergizer * 5) { MediaPlayer[sWin]->Play(); TimersRun(false); MessageDlg(" Вы победили. Следующий уровень !!! ", mtInformation, TMsgDlgButtons() << mbOK, 0); LevelCreate(); } Form1->Canvas->Draw(0,0,ImageBuffer); // выводим все на экран } //--------------------------------------------------------------------------- void __fastcall TForm1::TimerActiveTimer(TObject *Sender) { if (Pacman.ActiveTime > 0) Pacman.ActiveTime--; if (Pacman.ActiveTime == 0) Pacman.Active = false; for (int a = 0; a < 4; a++) { if (Ghost[a].ShyTime > 0) Ghost[a].ShyTime--; if (Ghost[a].ShyTime == 0) Ghost[a].Shy = false; } } //--------------------------------------------------------------------------- void __fastcall TForm1::TimerSpriteTimer(TObject *Sender) { if (Pacman.SpriteNum < 2) Pacman.SpriteNum++; else { Pacman.SpriteNum = 0; MediaPlayer[sMove]->Play(); } for (int a = 0; a < 4; a++) if (Ghost[a].SpriteNum < 2) Ghost[a].SpriteNum++; else Ghost[a].SpriteNum = 0; } //--------------------------------------------------------------------------- void TForm1::TimersRun(bool active) { TimerMove->Enabled = active; TimerSprite->Enabled = active; TimerActive->Enabled = active; } //---------------------ОБЩИЕ-ФУНКЦИИ----------------------------------------- //-------------генерация лабиринта по алгоритму Прима------------------------ void BreakWall(int x, int y, int dx, int dy) //вспомогательная функция. разбивает стену { if (dx == -1) Maze[x][y].left_wall = '0'; if (dx == 1) Maze[x + 1][y].left_wall = '0'; if (dy == -1) Maze[x][y].up_wall = '0'; if (dy == 1) Maze[x][y + 1].up_wall = '0'; } //--------------------------------------------------------------------------- void PrimGenerateMaze() { Form1->Label1->Caption = "Идет генерация лабиринта по алгоритму Прима"; const int Inside = 1; const int Border = 2; const int Outside = 0; int x, y; int xc, yc; int xloc, yloc; bool IsEnd; int counter; //вначале есть все стены и все локации внешние for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) { Maze[x][y].attr = Outside; Maze[x][y].left_wall = '1'; Maze[x][y].up_wall = '1'; } //нижняя и правая границы for (byte i = 0; i < Height; i++) { Maze[i][Height].up_wall = '1'; Maze[Height][i].left_wall = '1'; } //выбираем локацию, с которой начнется генерация x = random(Width); y = random(Height); Maze[x][y].attr = Inside; for (int i = 0; i < 4; i++) { xc = x + dx[i]; yc = y + dy[i]; if ((xc >= 0) && (yc >= 0) && (xc < Width) && (yc < Height)) Maze[xc][yc].attr = Border; } do { IsEnd = true; counter = 0; //выбираем случайную локацию с атрибутом Border for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) if (Maze[x][y].attr == Border) counter++; counter = random(counter) + 1; for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) if (Maze[x][y].attr == Border) { counter--; if (counter == 0) { xloc = x; yloc = y; goto ExitFor1; } } ExitFor1: //присваиваем ей атрибут Inside Maze[xloc][yloc].attr = Inside; counter = 0; for (int i = 0; i < 4; i++) { xc = xloc + dx[i]; yc = yloc + dy[i]; if ((xc >= 0) && (yc >= 0) && (xc < Width) && (yc < Height)) { if (Maze[xc][yc].attr == Inside) counter++; if (Maze[xc][yc].attr == Outside) Maze[xc][yc].attr = Border; } } counter = random(counter) + 1; for (int i = 0; i < 4; i++) { xc = xloc + dx[i]; yc = yloc + dy[i]; if ((xc >= 0) && (yc >= 0) && (xc <= Width) && (yc <= Height) && (Maze[xc][yc].attr == Inside)) { counter--; if (counter == 0) BreakWall(xloc, yloc, dx[i], dy[i]); } } for (x = 0; x < Width; x++) for (y = 0; y < Height; y++) if (Maze[x][y].attr == Border) { IsEnd = false; goto ExitFor2; } ExitFor2: ShowMaze(); Sleep(50); Application->ProcessMessages(); } while (!IsEnd); } //--------------------------------------------------------------------------- void ShowMaze() //рисуем в ImageBack лабиринт и отображаем на канвасе формы { ImageBack->Canvas->Brush->Color = clNavy; ImageBack->Canvas->Pen->Color = clYellow; ImageBack->Canvas->FillRect(Rect(0,0,Width * CellSize + 1, Height * CellSize + 1)); for (byte i = 0; i <= Height; i++) for (byte j = 0; j <= Width; j++) { if (Maze[i][j].left_wall == '1') //если у данной локации есть левая стена, то рисуем ее { ImageBack->Canvas->MoveTo(i * CellSize, j * CellSize); ImageBack->Canvas->LineTo(i * CellSize, (j + 1) * CellSize); } if (Maze[i][j].up_wall == '1') //соответственно если есть верхняя стена, то и ее рисуем { ImageBack->Canvas->MoveTo(i * CellSize, j * CellSize); ImageBack->Canvas->LineTo((i+1) * CellSize, j * CellSize); } if (Maze[i][j].attr == 2) //рисуем кружки в Border-локациях при генерации лабирнта { ImageBack->Canvas->Brush->Color = clRed; ImageBack->Canvas->Ellipse((i + 0.5) * CellSize - 5, (j + 0.5) * CellSize - 5,(i + 0.5) * CellSize + 5, (j + 0.5) * CellSize + 5); } } Form1->Canvas->Draw(0,0,ImageBack); } //--------------------------------------------------------------------------- void CreateMap() // генерируем карту: заполняем все пространство Seed // в углах ставим бонусные Energizer // в центре mpPacmanStart // + считаем количество Seed и Energizer { LevelSeed = 0; LevelEnergizer = 0; Score = 0; Maze[0][0].map = mpEnergizer; Maze[0][Width-1].map = mpEnergizer; Maze[Height-1][0].map = mpEnergizer; Maze[Height-1][Width-1].map = mpEnergizer; LevelEnergizer = 4; Maze[Height/2][Width/2].map = mpPacmanStart; for (byte i = 0; i < Height; i++) for (byte j = 0; j < Width; j++) if (Maze[i][j].map == mpNone) { Maze[i][j].map = mpSeed; LevelSeed++; } } //--------------------------------------------------------------------------- void ShowMap() // рисуем в ImageBack(там уже сгенерирован лабиринт) карту // инициализируем Pacman.StartX и StartY // вызываем CheckStartPlace() { for (byte i = 0; i < Height; i++) for (byte j = 0; j < Width; j++) switch (Maze[i][j].map) { case mpSeed : Form1->Sprites->Draw(ImageBack->Canvas, i * CellSize, j * CellSize, 60); break; case mpEnergizer : Form1->Sprites->Draw(ImageBack->Canvas, i * CellSize, j * CellSize, 61); break; case mpPacmanStart : Pacman.StartX = i * CellSize; Pacman.StartY = j * CellSize; break; } CheckStartPlace(); } //--------------------------------------------------------------------------- void LevelCreate() { Form1->TimersRun(false); PrimGenerateMaze(); //постобработка сгенерированного лабиринта //разбиваем рэндомные стены чтоб были проходы Sleep(200); for (int x = 1; x < Width; x++) for (int y = 1; y < Height; y++) { //если путь из данной локации в соседнюю больше constMaxWay, //то разбиваем стену между этими локациями WaveTracingSolve(x,y); for (int i = 0; i < 4; i++) if (Maze[x + dx[i]][y + dy[i]].mark > constMaxWay) { BreakWall( x, y, dx[i], dy[i]); ShowMaze(); Sleep(100); } } CreateMap(); ShowMap(); MediaPlayer[sNextLevel]->Play(); Form1->TimersRun(true); } //--------------------------------------------------------------------------- void CheckStartPlace() { Pacman.X = Pacman.StartX; Pacman.Y = Pacman.StartY; Pacman.Dir = 3; Pacman.NewDir = 3; for (int i=0; i<4; i++) Pacman.DirFlag[i] = false; Pacman.Active = false; Pacman.ActiveTime = 0; for (int a = 0; a < 4; a++) { Ghost[a].X = a * CellSize; Ghost[a].Y = a * CellSize; Ghost[a].Dir = 3; for (int i = 0; i < 4; i++) Ghost[a].DirFlag[i] = false; Ghost[a].Shy = false; Ghost[a].ShyTime = 0; } Bonus.Kind = 0; Bonus.Life = 0; } //--------------------------------------------------------------------------- void LevelRenewSeed() { ImageBack->Canvas->Brush->Color = clNavy; ImageBack->Canvas->FillRect(Rect(Pacman.X + 1, Pacman.Y + 1, Pacman.X + CellSize, Pacman.Y + CellSize)); } //--------------------------------------------------------------------------- bool CanGo(int x, int y, int dx, int dy) { byte f = '0'; if (dx == -1) f = Maze[x][y].left_wall; if (dx == 1) f = Maze[x + 1][y].left_wall; if (dy == -1) f = Maze[x][y].up_wall; if (dy == 1) f = Maze[x][y + 1].up_wall; if (f == '0') return true; return false; } //--------------------------------------------------------------------------- void Solve() { bool FindNull; //{ начинаем с итерации номер 1 } do { FindNull = false; //положим нулей нет -> волну построили -> конец алгоритма for ( int x = 0; x < Width; x++) for ( int y = 0; y < Height; y++) { if (Maze[x][y].mark == N) //найти локации, помеченные числом N for (int i = 0; i < 4; i++) //просмотр соседних локаций if ((CanGo(x, y, dx[i], dy[i])) && (Maze[x + dx[i]][y + dy[i]].mark == 0)) //локация доступна и помечена нулем -> помечаем ее числом N + 1 Maze[x + dx[i]][y + dy[i]].mark = N + 1; if (Maze[x][y].mark == 0) FindNull = true; //нашли локации помеченные нулем => продолжаем строить волну } N++; } while (FindNull == true); //повторять, если есть непройденные локации } //--------------------------------------------------------------------------- void WaveTracingSolve(int xs, int ys) { int x, y, xc, yc; for (int x = 0; x < Width; x++) for (int y = 0; y < Height; y++) Maze[x][y].mark = 0; Maze[xs][ys].mark = 1; //стартовой локации соответствует единица N = 1; Solve(); Form1->Label1->Caption = "Алгоритм сработал"; } //--------------------------------------------------------------------------- void __fastcall TForm1::N4Click(TObject *Sender) { Form2->ShowModal(); } //--------------------------------------------------------------------------- Файл Unit2.cpp#include #pragma hdrstop #include "Unit2.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm2 *Form2; //--------------------------------------------------------------------------- __fastcall TForm2::TForm2(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm2::Timer1Timer(TObject *Sender) { Label1->Caption=Date(); Label2->Caption=Time(); } Файл Project1.cpp#include #pragma hdrstop //--------------------------------------------------------------------------- USEFORM("Unit1.cpp", Form1); USEFORM("Unit2.cpp", Form2); //--------------------------------------------------------------------------- WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->CreateForm(__classid(TForm2), &Form2); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; } Файл Unit1.h#ifndef Unit1H #define Unit1H #include #include #include #include #include #include #include #include #include #include #include class TForm1 : public TForm { __published: // IDE-managed Components TMainMenu *MainMenu1; TLabel *Label1; TMenuItem *MenuGame; TMenuItem *MenuNew; TMenuItem *N5; TMenuItem *MenuExit; TImageList *Sprites; TTimer *TimerMove; TTimer *TimerSprite; TTimer *TimerActive; TMenuItem *N1; TMenuItem *N4; void __fastcall FormCreate(TObject *Sender); void __fastcall MenuNewClick(TObject *Sender); void __fastcall MenuExitClick(TObject *Sender); void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift); void __fastcall TimerMoveTimer(TObject *Sender); void __fastcall TimerActiveTimer(TObject *Sender); void __fastcall TimerSpriteTimer(TObject *Sender); void __fastcall N4Click(TObject *Sender); private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); void TimersRun(bool active); }; void BreakWall(int x, int y, int dx, int dy); void PrimGenerateMaze(); void ShowMaze(); void CreateMap(); void ShowMap(); void LevelCreate(); void CheckStartPlace(); void LevelRenewSeed(); bool CanGo(int x, int y, int dx, int dy); void Solve(); void WaveTracingSolve(int xs, int ys); extern PACKAGE TForm1 *Form1; #endif Файл Unit2.h#ifndef Unit2H #define Unit2H //--------------------------------------------------------------------------- #include #include #include #include #include #include //--------------------------------------------------------------------------- class TForm2 : public TForm { __published: // IDE-managed Components TImage *Image1; TLabel *Label1; TLabel *Label2; TTimer *Timer1; TLabel *Label3; TLabel *Label4; TLabel *Label5; TLabel *Label6; void __fastcall Timer1Timer(TObject *Sender); private: // User declarations public: // User declarations __fastcall TForm2(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm2 *Form2; //--------------------------------------------------------------------------- #endif |