Файл: Курсовая работа по дисциплине Основы программирования систем управления (наименование дисциплины).docx

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 04.12.2023

Просмотров: 36

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
таймер при помощи класса Clock.
// Получаем время, прошедшее с начала отсчета, и конвертируем его

float time = clock.getElapsedTime().asSeconds();

clock.restart();

timer += time;

Затем будет идти обработка событий с окном и клавишами( вверх, влево, вправо): при нажатии на крестик в меню игры, окно закроется, и программа будет завершена; при нажатии стрелочки вверх, переменной rotate будет присвоено значение true;

при нажатии стрелочки влево переменной dx будет присвоено значение минус единицы; при нажатии стрелочки вправо переменной dx будет присвоено значение единицы;

// Обрабатываем очередь событий в цикле

Event event;

while (window.pollEvent(event))

{

if (event.type == Event::Closed) {

// тогда закрываем его

window.close();

return 0;

}

// Была нажата кнопка на клавиатуре?

if (event.type == Event::KeyPressed)

// Эта кнопка – стрелка вверх?…

if (event.key.code == Keyboard::Up) rotate = true;

// …или же стрелка влево?…

else if (event.key.code == Keyboard::Left) dx = -1;

// …ну тогда может это стрелка вправо?

else if (event.key.code == Keyboard::Right) dx = 1;

}
Дополнительно было введено ускорение падения тетрамино, уменьшением значения delay.
//Ускоряем падение тетрамино

if (Keyboard::isKeyPressed(Keyboard::Down)) delay = 0.05;
После обработки нажатий клавиш прописываем горизонтальное перемещение посредством прибавления к координатам фигуры dx.
//// Горизонтальное перемещение ////

for (int i = 0; i < 4; i++) { b[i] = a[i]; a[i].x += dx; }
При выходе за границы поля или наложении одной фигуры на другую возвращаем старые координаты.
// Вышли за пределы поля после перемещения возвращаем старые координаты

if (!check()) for (int i = 0; i < 4; i++) a[i] = b[i];
Далее реализуем вращение фигуры. Для реализации данного функционала нам понадобятся знания линейной алгебры, а именно то, что вращение спрайта вокруг заданной точки с координатами (x_0; y_0) описывается уравнениями следующего вида:

X = x_0 + (x − x_0) * cos⁡(a) − (y − y_0 ) * sin⁡(a);
Y = y_0 + (y − y_0) * cos⁡(a) + (x − x_0 ) * sin⁡(a);


Где — (x_0; y_0) это старые (исходные) координаты точки, (x; y) — это новые координаты (после вращения), а а — это угол поворота. Так как все повороты у нас идут исключительно на 90°, а из школьного курса алгебры мы знаем, что:


sin⁡(90°) = 1
cos⁡(90°) = 0


то, подставляя соответствующие значения синуса и косинуса, исходные уравнения упрощаются до следующего вида:

X = x_0 − (y − y_0);
Y = y_0 + (x − x_0);


Данный алгоритм будет выполняться при значении переменной rotate равной true.

//// Вращение ////

if (rotate)

{

Point p = a[1]; // задаем центр вращения

for (int i = 0; i < 4; i++)

{

int x = a[i].y - p.y; //y-y0

int y = a[i].x - p.x; //x-x0

a[i].x = p.x - x;

a[i].y = p.y + y;

}

// Вышли за пределы поля после поворота? Тогда возвращаем старые координаты

if (!check()) { for (int i = 0; i < 4; i++) a[i] = b[i]; }
}

Для движения тетрамино вниз, уже были созданы необходимые переменные. Теперь по истечении 0.3 секунды с начала отсчета времени, мы будем сдвигать все части тетрамино на 1 позицию вниз. Когда фигура занимает окончательное положение на поле, задаём цвет и тип следующей фигуре.

//// Движение тетрамино вниз////

if (timer > delay)

{
for (int i = 0; i < 4; i++)

{

b[i] = a[i];

a[i].y += 1;

}
if (!check())

{

for (int i = 0; i < 4; i++) field[b[i].y][b[i].x] = colorNum;

colorNum = 1 + rand() % 7;

n = rand() % 7;

for (int i = 0; i < 4; i++)

{

a[i].x = figures[n][i] % 2;

a[i].y = figures[n][i] / 2;

}
}

timer = 0;
}

По правилам тетриса заполненная линия должна удаляться, для этого реализован цикл, который будет проходить по полю снизу вверх и проверять заполнено ли поле.

//----ПРОВЕРКА ЛИНИИ----//

int k = M - 1;

for (int i = M - 1; i > 0; i--)

{

int count = 0;

for (int j = 0; j < N; j++)

{

if (field[i][j]) count++;

field[k][j] = field[i][j];

}

if (count < N) k--;

}

Необходимо было задать первоначальное положение тетрамино корректно, для этого прописан блок if(beginGame).

// Первое появление тетрамино на поле

if (beginGame)

{

beginGame = false;

n = rand() % 7;

for (int i = 0; i < 4; i++)

{

a[i].x = figures[n][i] % 2;

a[i].y = figures[n][i] / 2;

}

}

dx = 0; rotate = 0; delay = 0.3;

Код, расположенный внутри блока if(beginGame), вызывается только один раз при старте приложения. Благодаря ему задается первоначальный тип тетрамино.

После обработки событий произведена отрисовка самого поля и фигур уже присутствующих на нём.
//----ОТРИСОВКА----//
// Задаем цвет фона - белый

window.clear(Color::White);

window.draw(sprite_background);

for (int i = 0; i < M; i++)

for (int j = 0; j < N; j++)

{

if (field[i][j] == 0) continue;

sprite.setTextureRect(IntRect(field[i][j] * 18, 0, 18, 18));

sprite.setPosition(j * 18, i * 18);

sprite.move(28, 31); // смещение



window.draw(sprite);

}

Затем идёт отрисовка движущейся фигуры.

for (int i = 0; i < 4; i++)

{

// Разукрашиваем тетрамино

sprite.setTextureRect(IntRect(colorNum * 18, 0, 18, 18));
// Устанавливаем позицию каждого кусочка тетрамино

sprite.setPosition(a[i].x * 18, a[i].y * 18);
sprite.move(28, 31); // смещение
// Отрисовка спрайта

window.draw(sprite);

}

// Отрисовка фрейма

window.draw(sprite_frame);
// Отрисовка окна

window.display();

}
  1. Меню окончания игры



Прежде всего присваиваем переменной IsRun true и заполняем массив field нулями для подготовки к новой игре, если таковая будет.

IsRun = true;
for (int i = 0; i < M; i++)

{

for (int j = 0; j < N; j++)

{

field[i][j] = 0;

}

}

Затем создаём окно window2, размер меню которого 320 на 480 пикселей и в заголовке задано название игры– Tetris. Действия связанные с этим меню описываются в цикле, условием которого будет window2.isOpen() – открыто ли окно. Алгоритм этого цикла аналогичен циклу связанного с стартовым окном за исключением того, что вырисовываться будет спрайт sprite_game_over.

RenderWindow window2(VideoMode(320, 480), "Tetris");
while (window2.isOpen())

{

Event event;

while (window2.pollEvent(event))

{

// Пользователь нажал на «крестик» и хочет закрыть окно?

if (event.type == Event::Closed)

{

window2.close();

return 0;

}

}

window2.clear(Color::White);

window2.draw(sprite_game_over);

window2.display();

if (Keyboard::isKeyPressed(Keyboard::Space))

{

break;

}

}

}

}

После отработки этого цикла программа либо будет завершена, либо вернётся к основному алгоритму.

ЗАКЛЮЧЕНИЕ



В результате выполнения курсовой работы по дисциплине «Основы программирования систем управления» была разработана игра Тетрис. Программа написана в среде разработки Microsoft Visual Studio Enterprise 2022 на языке С++ с использованием стандартной библиотеки и библиотеки SFML/Graphics.hpp.

СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ И ЛИТЕРАТУРЫ

  1. Википедия [сайт}. - Тетрис. - Режим доступа: общий.- Дата обновления: 06.05.2022.- URL: https://ru.wikipedia.org/wiki/Тетрис (Дата обращения: 12.05.2022).

  2. Довбуш, Галина Visual C++ на примерах / Галина Довбуш , Анатолий Хомоненко. - М.: БХВ-Петербург, 2012. - 528 c.

  3. Пахомов, Борис C/C++ и MS Visual C++ 2012 для начинающих / Борис Пахомов. - Москва: СИНТЕГ, 2015. - 518 c.

  4. Роберт, С. Сикорд Безопасное программирование на C и C++ / Роберт С. Сикорд. - Москва: РГГУ, 2014. - 496 c.

  5. Microsoft [сайт].- Reading Input Buffer Events.- Режим доступа: общий.- Дата обновления: 30.10.2022.- URL: https://docs.microsoft.com/en-us/windows/console/reading-input-buffer-events (Дата обращения: 04.05.2022).

  6. TIME [сайт].- The 50 Best Video Games of All Time.- Режим доступа: общий.- Дата обновления: 23.08.2016.- URL: https://time.com/4458554/best-video-games-all-time/ (Дата обращения: 11.05.2022).

  7. Microsoft [сайт].- Virtual-Key Codes.- Режим доступа: общий.- Дата обновления: 20.01.2022.- URL: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes (Дата обращения: 04.05.2022).


Приложение А

Листинг исходного программного кода

#include

#include

#include
using namespace sf;
const int M = 20;

const int N = 10;
int field[M][N] = { 0 }; // игровое поле

int figures[7][4] = // Массив фигурок-тетрамино

{

1,3,5,7, // I

2,4,5,7, // Z

3,5,4,6, // S

3,5,4,7, // T

2,3,5,7, // L

3,5,7,6, // J

2,3,4,5, // O

};
struct Point

{

int x, y;

}a[4], b[4];
// Проверка на выход за границы игрового поля

bool check()

{

for (int i = 0; i < 4; i++)

if (a[i].x < 0 || a[i].x >= N || a[i].y >= M) return 0;

else if (field[a[i].y][a[i].x]) return 0;
return 1;
};
bool endGame(RenderWindow* window)

{

for (int i = 0; i < N; i++)

{

if (field[2][i])

{

window->close();

return false;

}

}

}
int main()

{

srand(time(0));

// Создание и загрузка текстуры

Texture texture,

texture_background,

texture_frame,

texture_start,

texture_game_over;

texture.loadFromFile("tiles.png");

texture_background.loadFromFile("background.png");

texture_frame.loadFromFile("frame.png");

texture_start.loadFromFile("start.png");

texture_game_over.loadFromFile("game_over.png");
// Создание спрайта

Sprite sprite(texture),

sprite_background(texture_background),

sprite_frame(texture_frame),

sprite_start(texture_start),

sprite_game_over(texture_game_over);


sprite.setTextureRect(IntRect(0, 0, 18, 18));
// Переменные для горизонтального перемещения и вращения

int dx = 0; bool rotate = 0;

int colorNum = 1;

bool beginGame = true;

int n = rand() % 7;
// Переменные для таймера и задержки

float timer = 0, delay = 0.3;

Clock clock;
bool IsRun = true;
RenderWindow window0(VideoMode(320, 480), "Tetris");
while (window0.isOpen())

{

Event event;

while (window0.pollEvent(event))

{

// Пользователь нажал на «крестик»

if (event.type == Event::Closed)

{

window0.close();

return 0;

}

}

window0.clear(Color::White);

window0.draw(sprite_start);

window0.display();

if (Keyboard::isKeyPressed(Keyboard::Space))

{

window0.close();

break;

}

}
while (true) {
RenderWindow window(VideoMode(320, 480), "Tetris");

// Цикл игры. Выполняется, пока открыто окно

while (window.isOpen() && IsRun)

{

IsRun = endGame(&window);

if (!IsRun)

{

break;

}
// Получаем время, прошедшее с начала отсчета, и конвертируем его в

float time = clock.getElapsedTime().asSeconds();

clock.restart();

timer += time;
// Обрабатываем очередь событий в цикле

Event event;

while (window.pollEvent(event))

{

if (event.type == Event::Closed) {

// тогда закрываем его

window.close();

return 0;

}

// Была нажата кнопка на клавиатуре?

if (event.type == Event::KeyPressed)

// Эта кнопка – стрелка вверх?…

if (event.key.code == Keyboard::Up) rotate = true;

// …или же стрелка влево?…

else if (event.key.code == Keyboard::Left) dx = -1;

// …ну тогда может это стрелка вправо?

else if (event.key.code == Keyboard::Right) dx = 1;

}
//Ускоряем падение тетрамино

if (Keyboard::isKeyPressed(Keyboard::Down)) delay = 0.05;
//// Горизонтальное перемещение ////

for (int i = 0; i < 4; i++) { b[i] = a[i]; a[i].x += dx; }
// Вышли за пределы поля после перемещения возвращаем старые координаты

if (!check()) for (int i = 0; i < 4; i++) a[i] = b[i];

//// Вращение ////

if (rotate)

{

Point p = a[1]; // задаем центр вращения

for (int i = 0; i < 4; i++)

{

int x = a[i].y - p.y; //y-y0

int y = a[i].x - p.x; //x-x0

a[i].x = p.x - x;

a[i].y = p.y + y;

}

// Вышли за пределы поля после поворота? Тогда возвращаем старые координаты

if (!check()) { for (int i = 0; i < 4; i++) a[i] = b[i]; }
}
//// Движение тетрамино вниз////

if (timer > delay)

{
for (int i = 0; i < 4; i++)

{

b[i] = a[i];

a[i].y += 1;

}
if (!check())

{

for (int i = 0; i < 4; i++) field[b[i].y][b[i].x] =colorNum;

colorNum = 1 + rand() % 7;

n = rand() % 7;

for (int i = 0; i < 4; i++)

{

a[i].x = figures[n][i] % 2;

a[i].y = figures[n][i] / 2;

}
}

timer = 0;
}


//----ПРОВЕРКА ЛИНИИ----//

int k = M - 1;

for (int i = M - 1; i > 0; i--)

{

int count = 0;

for (int j = 0; j < N; j++)

{

if (field[i][j]) count++;

field[k][j] = field[i][j];

}

if (count < N) k--;

}
// Первое появление тетрамино на поле

if (beginGame)

{

beginGame = false;

n = rand() % 7;

for (int i = 0; i < 4; i++)

{

a[i].x = figures[n][i] % 2;

a[i].y = figures[n][i] / 2;

}

}

dx = 0; rotate = 0; delay = 0.3;
//----ОТРИСОВКА----//
// Задаем цвет фона - белый

window.clear(Color::White);

window.draw(sprite_background);

for (int i = 0; i < M; i++)

for (int j = 0; j < N; j++)

{

if (field[i][j] == 0) continue;

sprite.setTextureRect(IntRect(field[i][j] * 18, 0, 18, 18));

sprite.setPosition(j * 18, i * 18);

sprite.move(28, 31); // смещение

window.draw(sprite);

}
for (int i = 0; i < 4; i++)

{

// Разукрашиваем тетрамино

sprite.setTextureRect(IntRect(colorNum * 18, 0, 18, 18));
// Устанавливаем позицию каждого кусочка тетрамино

sprite.setPosition(a[i].x * 18, a[i].y * 18);
sprite.move(28, 31); // смещение
// Отрисовка спрайта

window.draw(sprite);

}

// Отрисовка фрейма

window.draw(sprite_frame);
// Отрисовка окна

window.display();

}
IsRun = true;
for (int i = 0; i < M; i++)

{

for (int j = 0; j < N; j++)

{

field[i][j] = 0;

}

}
RenderWindow window2(VideoMode(320, 480), "Tetris");
while (window2.isOpen())

{

Event event;

while (window2.pollEvent(event))

{

// Пользователь нажал на «крестик» и хочет закрыть окно?

if (event.type == Event::Closed)

{

window2.close();

return 0;

}

}

window2.clear(Color::White);

window2.draw(sprite_game_over);

window2.display();

if (Keyboard::isKeyPressed(Keyboard::Space))

{

break;

}

}

}

}