added double jump and sitting state
This commit is contained in:
		
							parent
							
								
									2e5c5a8dde
								
							
						
					
					
						commit
						90d07dde3f
					
				
					 148 changed files with 13050 additions and 0 deletions
				
			
		
							
								
								
									
										3
									
								
								term1/seminar13_polymorphism/arkanoid/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								term1/seminar13_polymorphism/arkanoid/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| path = ../../../../3rdparty/SFML-2.5.1 | ||||
| arkanoid: | ||||
| 	g++ -Wall -Wextra arkanoid.cpp bonus.cpp main.cpp ball.cpp brick_grid.cpp paddle.cpp -std=c++17 -o arkanoid -lsfml-graphics -lsfml-window -lsfml-system  | ||||
							
								
								
									
										
											BIN
										
									
								
								term1/seminar13_polymorphism/arkanoid/arkanoid
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								term1/seminar13_polymorphism/arkanoid/arkanoid
									
										
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										258
									
								
								term1/seminar13_polymorphism/arkanoid/arkanoid.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								term1/seminar13_polymorphism/arkanoid/arkanoid.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,258 @@ | |||
| #include <SFML/Window.hpp> | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include <list> | ||||
| #include <cmath> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "arkanoid.hpp" | ||||
| #include "bonus.hpp" | ||||
| 
 | ||||
| const double pi = 3.14159265358979323846; | ||||
| 
 | ||||
| void Arkanoid::addRandomBonus(sf::Vector2f position) | ||||
| { | ||||
|     if (m_bonuses.size() > kMaxNumBonuses) | ||||
|         return; | ||||
|     int max_rand = 10000; | ||||
|     if ((rand() % max_rand) * 1.0f / max_rand < m_bonusProbability)  | ||||
|     { | ||||
|         int max = 4; int min = 1; | ||||
|         int range = max - min + 1; | ||||
|         int num = rand() % range + min; | ||||
|         switch (num) { | ||||
|             case 1: | ||||
|                   m_bonuses.push_back(new TripleBallBonus(position)); | ||||
|                   break;         | ||||
|             case 2: | ||||
|                   m_bonuses.push_back(new EnlargePaddleBonus(position)); | ||||
|                   break; | ||||
|             case 3: | ||||
|                   m_bonuses.push_back(new ShrinkPaddleBonus(position)); | ||||
|                   break; | ||||
|             case 4: | ||||
|                   m_bonuses.push_back(new SlowingBonus(position)); | ||||
|                   break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Функция, которая обрабатывает все столкновения шарика 
 | ||||
| void Arkanoid::handleBallCollisions(Ball& ball)  | ||||
| { | ||||
|     ball.handleWallsCollision(m_border); | ||||
|     ball.handlePaddleCollision(m_paddle); | ||||
| 
 | ||||
|     auto indexes = ball.handleBrickGridCollision(m_brickGrid); | ||||
|     if (indexes.first == -1) | ||||
|         return; | ||||
|     m_brickGrid.deactivateBrick(indexes); | ||||
|     addRandomBonus(ball.position); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Arkanoid::Arkanoid(sf::FloatRect border, sf::Font& font) : | ||||
|     m_time{0.0}, | ||||
|     m_border{border}, | ||||
|     m_paddle{{m_border.left + m_border.width / 2, m_border.top + m_border.height - 100}, {120, 20}}, | ||||
|     m_gameState{GameState::stuck}, | ||||
|     m_numLives{7} | ||||
| { | ||||
|     float gap = border.width / 10; | ||||
|     m_brickGrid = BrickGrid({border.left + gap, border.top + gap, border.width - 2 * gap, border.height / 2}, 50, 30); | ||||
|     m_bonusProbability = 0.1; | ||||
| 
 | ||||
|     m_endText.setFont(font); | ||||
|     m_endText.setString("You Win!"); | ||||
|     m_endText.setCharacterSize(100); | ||||
|     m_endText.setFillColor(sf::Color::White); | ||||
|     sf::FloatRect textRect = m_endText.getLocalBounds(); | ||||
|     m_endText.setOrigin(textRect.left + textRect.width / 2.0f, textRect.top  + textRect.height / 2.0f); | ||||
|     m_endText.setPosition({border.left + border.width / 2, border.top + border.height / 2}); | ||||
| } | ||||
| 
 | ||||
| sf::FloatRect Arkanoid::getBorder() const | ||||
| { | ||||
|     return m_border; | ||||
| } | ||||
| 
 | ||||
| const Paddle& Arkanoid::getPaddle() const | ||||
| { | ||||
|     return m_paddle; | ||||
| } | ||||
| 
 | ||||
| const BrickGrid& Arkanoid::getBrickGrid() const | ||||
| { | ||||
|     return m_brickGrid; | ||||
| } | ||||
| 
 | ||||
| void Arkanoid::addBall(const Ball& ball) | ||||
| { | ||||
|     if (m_balls.size() < kMaxNumBalls) | ||||
|         m_balls.push_back(ball); | ||||
| } | ||||
| 
 | ||||
| bool Arkanoid::isMaxBalls() | ||||
| { | ||||
|     return m_balls.size() == kMaxNumBalls - 1; | ||||
| } | ||||
| 
 | ||||
| // Эта функция вызывается каждый кадр
 | ||||
| void Arkanoid::update(const sf::RenderWindow& window, float dt) | ||||
| { | ||||
|     m_time += dt; | ||||
| 
 | ||||
|     // Устанавливаем положение ракетки
 | ||||
|     sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window)); | ||||
|     m_paddle.position.x = mousePosition.x; | ||||
| 
 | ||||
|     // Обрабатываем шарики
 | ||||
|     for (std::list<Ball>::iterator it = m_balls.begin(); it != m_balls.end();) | ||||
|     { | ||||
|         (*it).update(dt); | ||||
|         handleBallCollisions(*it); | ||||
|         if ((*it).position.y > m_border.top + m_border.height) | ||||
|         { | ||||
|             it = m_balls.erase(it); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             it++; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Если шариков нет, то переходи в режим начала игры и уменьшаем кол-во жизней
 | ||||
|     if (m_gameState == GameState::running && m_balls.size() == 0) | ||||
|     { | ||||
|         m_effects.clear(); | ||||
|         m_gameState = GameState::stuck; | ||||
|         m_numLives--; | ||||
|     } | ||||
| 
 | ||||
|     // Если жизни кончились, то переходим в состояние конца игры (проигрыш)
 | ||||
|     if (m_numLives < 0) | ||||
|     { | ||||
|         m_gameState = GameState::endLose; | ||||
|     } | ||||
| 
 | ||||
|     // Если блоки кончились, то переходим в состояние конца игры (победа)
 | ||||
|     if (m_brickGrid.getNumActiveBricks() == 0) | ||||
|     { | ||||
|         m_gameState = GameState::endWin; | ||||
|     } | ||||
|      | ||||
|     // Обрабатываем бонусы
 | ||||
|     for (auto it = m_bonuses.begin(); it != m_bonuses.end();) | ||||
|     { | ||||
|         (*it)->update(dt); | ||||
|         if ((*it)->isColiding(m_paddle)) | ||||
|         { | ||||
|             (*it)->activate(*this); | ||||
|             delete *it; | ||||
|             it = m_bonuses.erase(it); | ||||
|         } | ||||
|         else if ((*it)->m_position.y > m_border.top + m_border.height) | ||||
|         {  | ||||
|             delete (*it); | ||||
|             it = m_bonuses.erase(it); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             it++; | ||||
|         } | ||||
|          | ||||
|     } | ||||
|     /* Обработка эффектов */ | ||||
|     for (auto it = m_effects.begin(); it != m_effects.end();) | ||||
|     { | ||||
|         if ((*it)->isExpired(m_time)) | ||||
|         { | ||||
|             (*it)->deactivate(*this); | ||||
|             delete *it; | ||||
|             it = m_effects.erase(it); | ||||
|         } | ||||
|         else { | ||||
|             it++; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Arkanoid::draw(sf::RenderWindow& window) | ||||
| { | ||||
|     // Рисуем задний прямоугольник
 | ||||
|     static sf::RectangleShape background {{m_border.width, m_border.height}}; | ||||
|     background.setPosition({m_border.left, m_border.top}); | ||||
|     background.setFillColor(kBackgroundColor); | ||||
|     window.draw(background); | ||||
| 
 | ||||
|     // Рисуем блоки
 | ||||
|     m_brickGrid.draw(window); | ||||
| 
 | ||||
|     // Рисуем шарики
 | ||||
|     for (Ball& ball : m_balls) | ||||
|     { | ||||
|         ball.draw(window); | ||||
|     } | ||||
| 
 | ||||
|     // Рисуем ракетку
 | ||||
|     m_paddle.draw(window); | ||||
| 
 | ||||
|     // Если мы в режиме начала игры, то рисуем шарик на ракетке
 | ||||
|     if (m_gameState == GameState::stuck) | ||||
|     { | ||||
|         m_initialBall.position = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2 - m_initialBall.radius}; | ||||
|         m_initialBall.position = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2 - m_initialBall.radius}; | ||||
|         m_initialBall.draw(window); | ||||
|     } | ||||
| 
 | ||||
|     // Рисуем кол-во жизней вверху слева
 | ||||
|     for (int i = 0; i < m_numLives; i++) | ||||
|     { | ||||
|         m_initialBall.position = {m_initialBall.radius * (3 * i + 2), 2 * m_initialBall.radius}; | ||||
|         m_initialBall.draw(window); | ||||
|     } | ||||
| 
 | ||||
|     // Рисуем бонусы
 | ||||
|     for (Bonus* pbonus : m_bonuses) | ||||
|     { | ||||
|         pbonus->draw(window); | ||||
|     } | ||||
| 
 | ||||
|     // При завершении игры рисуем надпись
 | ||||
|     if (m_gameState == GameState::endWin) | ||||
|     { | ||||
|         m_endText.setString("You Win!"); | ||||
|         window.draw(m_endText); | ||||
|     } | ||||
| 
 | ||||
|     // При завершении игры рисуем надпись
 | ||||
|     if (m_gameState == GameState::endLose) | ||||
|     { | ||||
|         m_endText.setString("You Lose!"); | ||||
|         window.draw(m_endText); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Arkanoid::onMousePressed(sf::Event& event) | ||||
| { | ||||
|     switch (m_gameState) | ||||
|     { | ||||
|         case GameState::stuck: | ||||
|             if (event.mouseButton.button == sf::Mouse::Left) | ||||
|             { | ||||
|                 m_gameState = GameState::running; | ||||
|                 float velocityAngle = (rand() % 100 + 40) * pi / 180; | ||||
|                 float velocityNorm = Ball::initialVelocity; | ||||
|                 sf::Vector2f newPosition = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2.0f - m_initialBall.radius}; | ||||
|                 sf::Vector2f newVelocity = {-velocityNorm * cosf(velocityAngle), -velocityNorm * sinf(velocityAngle)}; | ||||
|                 addBall({m_initialBall.radius, newPosition, newVelocity}); | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|         case GameState::running: | ||||
|             break; | ||||
|         case GameState::endLose: | ||||
|             break; | ||||
|         case GameState::endWin: | ||||
|             break; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										85
									
								
								term1/seminar13_polymorphism/arkanoid/arkanoid.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								term1/seminar13_polymorphism/arkanoid/arkanoid.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| #pragma once | ||||
| #include <list> | ||||
| 
 | ||||
| #include "ball.hpp" | ||||
| #include "brick_grid.hpp" | ||||
| #include "paddle.hpp" | ||||
| class Bonus; | ||||
| class Effect; | ||||
| 
 | ||||
| class Arkanoid | ||||
| { | ||||
| private: | ||||
|     // Константы:
 | ||||
|     // Цвет задника
 | ||||
|     const sf::Color kBackgroundColor {12, 31, 47}; | ||||
|     // Максимально возможное количество шариков в один момент времени
 | ||||
|     const unsigned kMaxNumBalls {250}; | ||||
|     // Максимально возможное количество бонусов в один момент времени
 | ||||
|     const unsigned kMaxNumBonuses {10}; | ||||
| 
 | ||||
|     // Время, которое прошло с начала игры в секундах
 | ||||
|     double m_time; | ||||
|     // Границы игрового поля
 | ||||
|     sf::FloatRect m_border; | ||||
|     // Связный список всех шариков
 | ||||
|     std::list<Ball> m_balls; | ||||
|     // Объект, задающий состояние сетки блоков
 | ||||
|     BrickGrid m_brickGrid; | ||||
|     // Ракетка
 | ||||
|     Paddle m_paddle; | ||||
|     // Состояние игры
 | ||||
|     enum class GameState {stuck, running, endLose, endWin}; | ||||
|     GameState m_gameState; | ||||
| 
 | ||||
|     // Текущее число жизней
 | ||||
|     int m_numLives; | ||||
| 
 | ||||
|     // Связный список указателей на бонусы
 | ||||
|     // Почему указатели - для реализации полиформизма
 | ||||
|     // Так как в будущем мы хотим сделать несколько вариантов бонусов
 | ||||
|     std::list<Bonus*> m_bonuses; | ||||
|     std::list<Effect*> m_effects; | ||||
| 
 | ||||
|     // Вероятность того, что при разрушении блока выпадет бонус
 | ||||
|     float m_bonusProbability; | ||||
| 
 | ||||
|     // Макет шарика, используемый для рисова
 | ||||
|     Ball m_initialBall {6, {0, 0}, {0, 0}}; | ||||
| 
 | ||||
|     // Текст, который рисуется в конце игры
 | ||||
|     sf::Text m_endText; | ||||
| 
 | ||||
| 
 | ||||
|     void addRandomBonus(sf::Vector2f position); | ||||
| 
 | ||||
|     // Функция, которая обрабатывает все столкновения шарика 
 | ||||
|     void handleBallCollisions(Ball& ball); | ||||
| 
 | ||||
| public: | ||||
|     Arkanoid(sf::FloatRect border, sf::Font& font); | ||||
| 
 | ||||
|     sf::FloatRect getBorder() const; | ||||
| 
 | ||||
|     const Paddle& getPaddle() const; | ||||
| 
 | ||||
|     const BrickGrid& getBrickGrid() const; | ||||
| 
 | ||||
|     void addBall(const Ball& ball); | ||||
| 
 | ||||
|     // Эта функция вызывается каждый кадр
 | ||||
|     void update(const sf::RenderWindow& window, float dt); | ||||
| 
 | ||||
|     void draw(sf::RenderWindow& window); | ||||
|     void onMousePressed(sf::Event& event); | ||||
|     bool isMaxBalls(); | ||||
|      | ||||
|     // Класс бонус должен быть дружественным, так как он может менять внутреннее состояние игры
 | ||||
|     friend class Bonus; | ||||
|     friend class TripleBallBonus; | ||||
|     friend class EnlargePaddleBonus; | ||||
|     friend class ShrinkPaddleBonus; | ||||
|     friend class SlowingBonus; | ||||
| 
 | ||||
|     friend class SlowingEffect; | ||||
| }; | ||||
							
								
								
									
										183
									
								
								term1/seminar13_polymorphism/arkanoid/ball.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								term1/seminar13_polymorphism/arkanoid/ball.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,183 @@ | |||
| #include <cmath> | ||||
| #include "ball.hpp" | ||||
| #include "brick_grid.hpp" | ||||
| #include "paddle.hpp" | ||||
| 
 | ||||
| // Вспомагательные функции для работы с векторами типа sf::Vector2f
 | ||||
| float operator*(const sf::Vector2f& first, const sf::Vector2f& second) | ||||
| { | ||||
|     return first.x * second.x + first.y * second.y; | ||||
| } | ||||
| 
 | ||||
| float norm(sf::Vector2f a) | ||||
| { | ||||
|     return std::sqrt(a.x * a.x + a.y * a.y); | ||||
| } | ||||
| 
 | ||||
| float sqnorm(sf::Vector2f a) | ||||
| { | ||||
|     return a.x * a.x + a.y * a.y; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Ball::Ball(float radius, sf::Vector2f position, sf::Vector2f velocity) :  | ||||
|     radius(radius), position(position), velocity(velocity) {} | ||||
| 
 | ||||
| void Ball::update(float dt)  | ||||
| { | ||||
|     position += velocity * dt; | ||||
| } | ||||
| 
 | ||||
| void Ball::draw(sf::RenderWindow& window)  | ||||
| { | ||||
|     static sf::CircleShape shape {}; | ||||
|     shape.setRadius(radius); | ||||
|     shape.setOrigin(radius, radius); | ||||
|     shape.setFillColor(color); | ||||
|     shape.setPosition(position); | ||||
|     window.draw(shape); | ||||
| } | ||||
| 
 | ||||
| std::pair<sf::Vector2f, bool> Ball::findClosestPoint(const sf::FloatRect& rect) const  | ||||
| { | ||||
|     float left   = rect.left; | ||||
|     float right  = rect.left + rect.width; | ||||
|     float bottom = rect.top + rect.height; | ||||
|     float top    = rect.top; | ||||
| 
 | ||||
|     sf::Vector2f d; | ||||
|     if (position.x < left) | ||||
|         d.x = left; | ||||
|     else if (position.x > right) | ||||
|         d.x = right; | ||||
|     else | ||||
|         d.x = position.x; | ||||
| 
 | ||||
|     if (position.y > bottom) | ||||
|         d.y = bottom; | ||||
|     else if (position.y < top) | ||||
|         d.y = top; | ||||
|     else | ||||
|         d.y = position.y; | ||||
| 
 | ||||
|     d -= position; | ||||
|     bool isColiding = sqnorm(d) < radius * radius; | ||||
|     return {d, isColiding}; | ||||
| } | ||||
| 
 | ||||
| bool Ball::handleRectCollision(const sf::FloatRect& rect) | ||||
| { | ||||
|     auto [d, isColiding] = findClosestPoint(rect); | ||||
|     if (!isColiding) | ||||
|         return false; | ||||
| 
 | ||||
|     float closestPointNorm = norm(d); | ||||
|     // Если расстояние == 0, то это значит, что шарик за 1 фрейм зашёл центром внутрь блока
 | ||||
|     // Отражаем шарик от блока
 | ||||
|     if (closestPointNorm < 1e-4) | ||||
|     { | ||||
|         if (std::fabs(velocity.x) > std::fabs(velocity.y)) | ||||
|             velocity.x *= -1; | ||||
|         else | ||||
|             velocity.y *= -1; | ||||
|     } | ||||
|     // Если расстояние != 0, но шарик касается блока, то мы можем просчитать отражение более точно
 | ||||
|     // Отражение от углов и по касательной.
 | ||||
|     else if (closestPointNorm < radius) | ||||
|     { | ||||
|         position -= d * ((radius - closestPointNorm) / closestPointNorm); | ||||
|         velocity -= 2.0f * d * (d * velocity) / (closestPointNorm * closestPointNorm);        | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void Ball::handleWallsCollision(sf::FloatRect boundary) | ||||
| { | ||||
|     if (position.x < boundary.left + radius) | ||||
|     { | ||||
|         position.x = boundary.left + radius; | ||||
|         velocity.x *= -1; | ||||
|     } | ||||
|     if (position.x > boundary.left + boundary.width - radius) | ||||
|     { | ||||
|         position.x = boundary.left + boundary.width - radius; | ||||
|         velocity.x *= -1; | ||||
|     } | ||||
|     if (position.y < boundary.top + radius) | ||||
|     { | ||||
|         position.y = boundary.top + radius; | ||||
|         velocity.y *= -1; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::pair<int, int> Ball::handleBrickGridCollision(const BrickGrid& brickGrid) | ||||
| { | ||||
|     auto [gridColumns, gridRows] = brickGrid.getGridSizes(); | ||||
|     auto [brickWidth, brickHeight] = brickGrid.getBrickSizes(); | ||||
|     auto [left, top, width, height] = brickGrid.getBorder(); | ||||
|      | ||||
|     // Определяем координаты блоков с которыми шарик может пересечься
 | ||||
|     int brickColumnStart  = std::clamp(static_cast<int>((position.x - left - radius) / brickWidth),      0, gridColumns); | ||||
|     int brickColumnFinish = std::clamp(static_cast<int>((position.x - left + radius) / brickWidth) + 1,  0, gridColumns); | ||||
|     int brickRowStart     = std::clamp(static_cast<int>((position.y - top  - radius) / brickHeight),     0, gridRows); | ||||
|     int brickRowFinish    = std::clamp(static_cast<int>((position.y - top  + radius) / brickHeight) + 1, 0, gridRows); | ||||
| 
 | ||||
|     // Если шарик находится вне сетки блоков, то выходим
 | ||||
|     if (brickColumnStart == brickColumnFinish || brickRowStart == brickRowFinish) | ||||
|         return {-1, -1}; | ||||
| 
 | ||||
|     // Находим ближайший к центру шарика активный пересекаемый шариком блок
 | ||||
|     float closestSqNorm = width * width + height * height; | ||||
|     std::pair<int, int> closestBrickIndexes = {-1, -1}; | ||||
|     for (int i = brickColumnStart; i < brickColumnFinish; ++i) | ||||
|     { | ||||
|         for (int j = brickRowStart; j < brickRowFinish; ++j) | ||||
|         { | ||||
|             if (!brickGrid.isBrickActive({i, j})) | ||||
|                 continue; | ||||
| 
 | ||||
|             sf::FloatRect rect {left + i * brickWidth, top + j * brickHeight, brickWidth, brickHeight}; | ||||
|             auto [d, isColiding] = findClosestPoint(rect); | ||||
|             if (!isColiding) | ||||
|                 continue; | ||||
| 
 | ||||
|             if (sqnorm(d) < closestSqNorm) | ||||
|             { | ||||
|                 closestSqNorm = sqnorm(d); | ||||
|                 closestBrickIndexes = {i, j}; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Если такого не нашли, то возвращаем {-1, -1}
 | ||||
|     if (closestBrickIndexes.first == -1) | ||||
|         return closestBrickIndexes; | ||||
| 
 | ||||
|     // Упруго сталкиваем шарик с найденым блоком
 | ||||
|     sf::FloatRect rect {left + closestBrickIndexes.first * brickWidth, top + closestBrickIndexes.second * brickHeight, brickWidth, brickHeight}; | ||||
|     handleRectCollision(rect); | ||||
| 
 | ||||
|     // Возвращаем координаты блока в сетки блоков
 | ||||
|     return closestBrickIndexes; | ||||
| } | ||||
| 
 | ||||
| // Обрабатываем столкновения шарика с ракеткой
 | ||||
| void Ball::handlePaddleCollision(const Paddle& paddle) | ||||
| { | ||||
|     auto [d, isColiding] = findClosestPoint(paddle.getBorder()); | ||||
|     if (!isColiding) | ||||
|         return; | ||||
| 
 | ||||
|     // Столкновение не упругое
 | ||||
|     // Угол отражения зависит от места на ракетке, куда стукнулся шарик
 | ||||
|     // Если шарик стукнулся в левую часть ракетки, то он должен полететь влево.
 | ||||
|     // Если в правую часть ракетки, то вправо.
 | ||||
|     const float pi = 3.14159265; | ||||
|     float velocityAngle = (position.x - paddle.position.x) / (paddle.size.x + 2 * radius) * (0.8 * pi) + pi / 2; | ||||
|     float velocityNorm = norm(velocity); | ||||
|     velocity.x = - velocityNorm * std::cos(velocityAngle); | ||||
|     velocity.y = - velocityNorm * std::sin(velocityAngle); | ||||
| } | ||||
							
								
								
									
										31
									
								
								term1/seminar13_polymorphism/arkanoid/ball.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								term1/seminar13_polymorphism/arkanoid/ball.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| #pragma once | ||||
| #include <SFML/Window.hpp> | ||||
| #include <SFML/Graphics.hpp> | ||||
| 
 | ||||
| class BrickGrid; | ||||
| class Paddle; | ||||
| 
 | ||||
| struct Ball | ||||
| { | ||||
|     inline static const float initialVelocity = 700; | ||||
|     inline static const sf::Color color {246, 213, 92}; | ||||
|     float radius; | ||||
|     sf::Vector2f position; | ||||
|     sf::Vector2f velocity; | ||||
| 
 | ||||
|     Ball(float radius, sf::Vector2f position, sf::Vector2f velocity); | ||||
| 
 | ||||
|     void update(float dt); | ||||
| 
 | ||||
|     void draw(sf::RenderWindow& window); | ||||
| 
 | ||||
|     std::pair<sf::Vector2f, bool> findClosestPoint(const sf::FloatRect& rect) const; | ||||
| 
 | ||||
|     bool handleRectCollision(const sf::FloatRect& rect); | ||||
| 
 | ||||
|     void handleWallsCollision(sf::FloatRect boundary); | ||||
| 
 | ||||
|     std::pair<int, int> handleBrickGridCollision(const BrickGrid& brickGrid); | ||||
| 
 | ||||
|     void handlePaddleCollision(const Paddle& paddle); | ||||
| }; | ||||
							
								
								
									
										221
									
								
								term1/seminar13_polymorphism/arkanoid/bonus.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								term1/seminar13_polymorphism/arkanoid/bonus.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,221 @@ | |||
| #include <cmath> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "bonus.hpp" | ||||
| #include "arkanoid.hpp" | ||||
| #include "ball.hpp" | ||||
| #include "paddle.hpp" | ||||
| 
 | ||||
| /* =============
 | ||||
|  * ===Bonuses=== | ||||
|  * ============= | ||||
|  * */ | ||||
| 
 | ||||
| Bonus::Bonus(sf::Vector2f position): m_position(position) | ||||
| { | ||||
|     m_time = 0; | ||||
| } | ||||
| 
 | ||||
| void Bonus::update(float dt) | ||||
| { | ||||
|     m_time += dt; | ||||
|     m_position.y += speed * dt; | ||||
| } | ||||
| 
 | ||||
| bool Bonus::isColiding(const Paddle& paddle) const | ||||
| { | ||||
|     bool result = paddle.getBorder().intersects({m_position.x - radius, m_position.y - radius, 2 * radius, 2 * radius}); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * TripleBallBonus  | ||||
|  * */ | ||||
| void TripleBallBonus::draw(sf::RenderWindow& window) const | ||||
| { | ||||
|     static sf::CircleShape shape(radius); | ||||
|     shape.setOrigin(radius, radius); | ||||
|     shape.setFillColor(sf::Color{100, 200, 100}); | ||||
|     shape.setPosition(m_position); | ||||
|     window.draw(shape); | ||||
| 
 | ||||
|     float angle = 0; | ||||
| 
 | ||||
|     static Ball ball {5, {0, 0}, {0, 0}}; | ||||
|     float ballRotationRadius = 7; | ||||
|     ball.position = m_position + ballRotationRadius * sf::Vector2f(std::cos(angle), std::sin(angle)); | ||||
|     ball.draw(window); | ||||
|     angle += 2.0 * M_PI / 3.0; | ||||
|     ball.position = m_position + ballRotationRadius * sf::Vector2f(std::cos(angle), std::sin(angle)); | ||||
|     ball.draw(window); | ||||
|     angle += 2.0 * M_PI / 3.0; | ||||
|     ball.position = m_position + ballRotationRadius * sf::Vector2f(std::cos(angle), std::sin(angle)); | ||||
|     ball.draw(window); | ||||
| } | ||||
| 
 | ||||
| void TripleBallBonus::activate(Arkanoid& game) | ||||
| { | ||||
|     int numBalls = game.m_balls.size(); | ||||
|     std::list<Ball>::iterator it = game.m_balls.begin(); | ||||
| 
 | ||||
|     bool isSlowed = false; | ||||
|     bool toApply = true; | ||||
| 
 | ||||
|     Effect* slowing_effect = nullptr; | ||||
|     for (auto it = game.m_effects.begin(); it != game.m_effects.end();) { | ||||
|         if ((*it)->effectId == _EFFECT_SLOWING_ID) { | ||||
|             isSlowed = true; | ||||
|             slowing_effect = *it; | ||||
|             break; | ||||
|         } | ||||
|         it++; | ||||
|     } | ||||
|     for (int i = 0; i < numBalls; i++) | ||||
|     { | ||||
|         float angle, vx, vy; | ||||
|         if (game.isMaxBalls()) { | ||||
|             toApply = false; | ||||
|         } | ||||
| 
 | ||||
|         if (toApply) { | ||||
|             angle = rand() % 1000 * (2 * M_PI / 1000); | ||||
|             vx = Ball::initialVelocity * sin(angle); | ||||
|             vy = Ball::initialVelocity * cos(angle); | ||||
|             game.addBall({game.m_initialBall.radius, (*it).position, {vx, vy}}); | ||||
|              | ||||
|             if (isSlowed) { | ||||
|                 slowing_effect->activate(game.m_balls.back());  | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (game.isMaxBalls()) { | ||||
|             toApply = false; | ||||
|         } | ||||
| 
 | ||||
|         if (toApply) { | ||||
|             angle = rand() % 1000 * (2 * M_PI / 1000); | ||||
|             vx = Ball::initialVelocity * sin(angle); | ||||
|             vy = Ball::initialVelocity * cos(angle); | ||||
|             game.addBall({game.m_initialBall.radius, (*it).position, {vx, vy}}); | ||||
|             if (isSlowed) { | ||||
|                 slowing_effect->activate(game.m_balls.back());  | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         it++; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  * EnlargePaddleBonus  | ||||
|  * */ | ||||
| void EnlargePaddleBonus::draw(sf::RenderWindow& window) const | ||||
| { | ||||
|     static sf::CircleShape shape(radius); | ||||
|     shape.setOrigin(radius, radius); | ||||
|     shape.setFillColor(sf::Color{100, 200, 100}); | ||||
|     shape.setPosition(m_position); | ||||
|     window.draw(shape); | ||||
| 
 | ||||
|     static sf::RectangleShape rect(sf::Vector2f{radius, radius / 2});  | ||||
|     rect.setFillColor(sf::Color::Green); | ||||
|     rect.setPosition(m_position - sf::Vector2f{radius /2, radius / 4}); | ||||
|     window.draw(rect); | ||||
| } | ||||
| 
 | ||||
| void EnlargePaddleBonus::activate(Arkanoid& game) | ||||
| { | ||||
|    if (game.m_paddle.size.x < 300) { | ||||
|        game.m_paddle.size.x *= 1.5;  | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * ShrinkPaddleBonus | ||||
|  * */ | ||||
| void ShrinkPaddleBonus::draw(sf::RenderWindow& window) const | ||||
| { | ||||
|     static sf::CircleShape shape(radius); | ||||
|     shape.setOrigin(radius, radius); | ||||
|     shape.setFillColor(sf::Color{100, 200, 100}); | ||||
|     shape.setPosition(m_position); | ||||
|     window.draw(shape); | ||||
| 
 | ||||
|     static sf::RectangleShape rect(sf::Vector2f{radius, radius / 2}); | ||||
|     rect.setFillColor(sf::Color::Red); | ||||
|     rect.setPosition(m_position - sf::Vector2f{radius /2, radius / 4}); | ||||
|     window.draw(rect); | ||||
| } | ||||
| void ShrinkPaddleBonus::activate(Arkanoid& game) | ||||
| { | ||||
|    if (game.m_paddle.size.x > 40) { | ||||
|         game.m_paddle.size.x *= 0.8; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * SlowingBonus | ||||
|  * */ | ||||
| 
 | ||||
| void SlowingBonus::draw(sf::RenderWindow& window) const | ||||
| { | ||||
|     static sf::CircleShape shape(radius); | ||||
|     shape.setOrigin(radius, radius); | ||||
|     shape.setFillColor(sf::Color{100, 200, 100}); | ||||
|     shape.setPosition(m_position); | ||||
|     window.draw(shape); | ||||
| 
 | ||||
|     static sf::CircleShape clock(radius / 2); | ||||
|     clock.setOutlineColor(sf::Color::Red); | ||||
|     clock.setOutlineThickness(3); | ||||
|     clock.setPosition(m_position - sf::Vector2f{radius /2, radius / 2}); | ||||
|     window.draw(clock); | ||||
| } | ||||
| 
 | ||||
| void SlowingBonus::activate(Arkanoid& game) | ||||
| { | ||||
|     bool isAlreadySlowed = false; | ||||
|     for (auto it = game.m_effects.begin(); it != game.m_effects.end();) { | ||||
|         if ((*it)->effectId == _EFFECT_SLOWING_ID) { | ||||
|             (*it)->mDuration += mDuration; | ||||
|             isAlreadySlowed = true; | ||||
|             break; | ||||
|         } | ||||
|         it++; | ||||
|     } | ||||
|     if (!isAlreadySlowed) { | ||||
|         game.m_effects.push_back(new SlowingEffect(game.m_time, mDuration)); | ||||
|         game.m_effects.back()->activate(game); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* =============
 | ||||
|  * ===Effects=== | ||||
|  * ============= | ||||
|  * */ | ||||
| 
 | ||||
| Effect::Effect(char id, double start_time, double duration) : effectId(id), mStartTime(start_time), mDuration(duration) {}; | ||||
| 
 | ||||
| bool Effect::isExpired(double time) { | ||||
|     if (mStartTime + mDuration > time) | ||||
|             return false; | ||||
|     return true; | ||||
| }  | ||||
| 
 | ||||
| SlowingEffect::SlowingEffect(double start_time, double duration) : Effect(_EFFECT_SLOWING_ID, start_time, duration) {}; | ||||
| 
 | ||||
| void SlowingEffect::activate(Arkanoid& game) { | ||||
|     for (Ball& ball : game.m_balls) { | ||||
|             ball.velocity = sf::Vector2f{ball.velocity.x * mSlowingFactor, ball.velocity.y * mSlowingFactor}; | ||||
|     } | ||||
| } | ||||
| void SlowingEffect::activate(Ball& ball) { | ||||
|     ball.velocity = sf::Vector2f{ball.velocity.x * mSlowingFactor, ball.velocity.y * mSlowingFactor}; | ||||
| } | ||||
| 
 | ||||
| void SlowingEffect::deactivate(Arkanoid& game) { | ||||
|     for (Ball& ball : game.m_balls) { | ||||
|         ball.velocity = sf::Vector2f{ball.velocity.x / mSlowingFactor, ball.velocity.y / mSlowingFactor}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										92
									
								
								term1/seminar13_polymorphism/arkanoid/bonus.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								term1/seminar13_polymorphism/arkanoid/bonus.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| #pragma once | ||||
| #include <SFML/Window.hpp> | ||||
| #include <SFML/Graphics.hpp> | ||||
| class Paddle; | ||||
| class Ball; | ||||
| class Arkanoid; | ||||
| 
 | ||||
| class Bonus | ||||
| { | ||||
| protected: | ||||
|     inline static const float speed = 120; | ||||
|     inline static const float radius = 15; | ||||
| 
 | ||||
|     sf::Vector2f m_position; | ||||
|     float m_time; | ||||
| 
 | ||||
| public: | ||||
|     Bonus(sf::Vector2f position); | ||||
|     void update(float dt); | ||||
|     virtual void draw(sf::RenderWindow& window) const = 0; | ||||
|     virtual void activate(Arkanoid& game) = 0; | ||||
|     virtual ~Bonus() = default; | ||||
| 
 | ||||
|     bool isColiding(const Paddle& paddle) const; | ||||
|     // Класс Arkanoid должен быть дружественным, так как он может менять внутреннее объекта-бонуса
 | ||||
|     friend class Arkanoid; | ||||
| }; | ||||
| 
 | ||||
| class TripleBallBonus : public Bonus { | ||||
| public: | ||||
|     TripleBallBonus(sf::Vector2f position): Bonus(position) {};  | ||||
|     void draw(sf::RenderWindow& window) const; | ||||
|     void activate(Arkanoid& game);  | ||||
| }; | ||||
| 
 | ||||
| class EnlargePaddleBonus : public Bonus { | ||||
| public: | ||||
|     EnlargePaddleBonus(sf::Vector2f position): Bonus(position) {};  | ||||
|     void draw(sf::RenderWindow& window) const; | ||||
|     void activate(Arkanoid& game);  | ||||
| }; | ||||
| 
 | ||||
| class ShrinkPaddleBonus : public Bonus { | ||||
| public: | ||||
|     ShrinkPaddleBonus(sf::Vector2f position): Bonus(position) {};  | ||||
|     void draw(sf::RenderWindow& window) const; | ||||
|     void activate(Arkanoid& game);  | ||||
| }; | ||||
| 
 | ||||
| class SlowingBonus : public Bonus { | ||||
| private: | ||||
|     double mDuration = 10; | ||||
| public: | ||||
|     SlowingBonus(sf::Vector2f position): Bonus(position) {};  | ||||
|     void draw(sf::RenderWindow& window) const; | ||||
|     void activate(Arkanoid& game);  | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Effects | ||||
|  * */ | ||||
| #define _EFFECT_SLOWING_ID 0 | ||||
| class Effect { | ||||
| protected: | ||||
|     char effectId; | ||||
|     double mStartTime; | ||||
|     double mDuration; | ||||
| public: | ||||
|     Effect(char id, double start_time, double duration);  | ||||
|     virtual ~Effect() = default; | ||||
| 
 | ||||
|     virtual void activate(Arkanoid& game) = 0; | ||||
|     virtual void activate(Ball& ball) = 0; | ||||
|     virtual void deactivate(Arkanoid& game) = 0; | ||||
|     bool isExpired(double time); | ||||
| 
 | ||||
| 
 | ||||
|     friend class SlowingBonus; | ||||
|     friend class TripleBallBonus; | ||||
|     friend class Arkanoid; | ||||
| }; | ||||
| 
 | ||||
| class SlowingEffect : public Effect { | ||||
| private: | ||||
|     float mSlowingFactor = 0.1; | ||||
|     char id = _EFFECT_SLOWING_ID; | ||||
| public: | ||||
|     SlowingEffect(double start_time, double duration); | ||||
|     void activate(Arkanoid& game); | ||||
|     void activate(Ball& ball); | ||||
|     void deactivate(Arkanoid& game);  | ||||
| }; | ||||
							
								
								
									
										5
									
								
								term1/seminar13_polymorphism/arkanoid/brick.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								term1/seminar13_polymorphism/arkanoid/brick.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| #pragma once | ||||
| struct Brick | ||||
| { | ||||
|     bool isActive; | ||||
| }; | ||||
							
								
								
									
										63
									
								
								term1/seminar13_polymorphism/arkanoid/brick_grid.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								term1/seminar13_polymorphism/arkanoid/brick_grid.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| #include <vector> | ||||
| #include "brick_grid.hpp" | ||||
| 
 | ||||
| BrickGrid::BrickGrid() {} | ||||
| BrickGrid::BrickGrid(sf::FloatRect borders, int numBrickColumns, int numBrickRows) :  | ||||
|     m_border(borders),  | ||||
|     m_numBrickColumns(numBrickColumns),  | ||||
|     m_numBrickRows(numBrickRows), | ||||
|     m_numActiveBricks(numBrickColumns * numBrickRows) | ||||
| { | ||||
|     m_bricks.resize(m_numBrickColumns * m_numBrickRows, Brick{true}); | ||||
|     m_brickShape.setSize(getBrickSizes()); | ||||
|     m_brickShape.setOutlineColor(sf::Color::Black); | ||||
|     m_brickShape.setOutlineThickness(0.5); | ||||
|     m_brickShape.setFillColor(color); | ||||
| } | ||||
| 
 | ||||
| sf::FloatRect BrickGrid::getBorder() const  | ||||
| { | ||||
|     return m_border; | ||||
| } | ||||
| 
 | ||||
| sf::Vector2i BrickGrid::getGridSizes() const  | ||||
| { | ||||
|     return {m_numBrickColumns, m_numBrickRows}; | ||||
| } | ||||
| 
 | ||||
| sf::Vector2f BrickGrid::getBrickSizes() const  | ||||
| { | ||||
|     return {m_border.width / m_numBrickColumns, m_border.height / m_numBrickRows}; | ||||
| } | ||||
| 
 | ||||
| bool BrickGrid::isBrickActive(std::pair<int, int> indexes) const | ||||
| { | ||||
|     return m_bricks[indexes.first + indexes.second * m_numBrickColumns].isActive; | ||||
| } | ||||
| 
 | ||||
| void BrickGrid::deactivateBrick(std::pair<int, int> indexes) | ||||
| { | ||||
|     m_bricks[indexes.first + indexes.second * m_numBrickColumns].isActive = false; | ||||
|     m_numActiveBricks--; | ||||
| } | ||||
| 
 | ||||
| int BrickGrid::getNumActiveBricks() const | ||||
| { | ||||
|     return m_numActiveBricks; | ||||
| } | ||||
| 
 | ||||
| void BrickGrid::draw(sf::RenderWindow& window) | ||||
| { | ||||
|     auto [brickWidth, brickHeight] = getBrickSizes(); | ||||
| 
 | ||||
|     for (int j = 0; j < m_numBrickRows; ++j)  | ||||
|     { | ||||
|         for (int i = 0; i < m_numBrickColumns; ++i)  | ||||
|         { | ||||
|             if (!isBrickActive({i, j})) | ||||
|                 continue; | ||||
|             m_brickShape.setPosition({m_border.left + i * brickWidth, m_border.top + j * brickHeight}); | ||||
|             window.draw(m_brickShape); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										37
									
								
								term1/seminar13_polymorphism/arkanoid/brick_grid.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								term1/seminar13_polymorphism/arkanoid/brick_grid.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| #pragma once | ||||
| #include <SFML/Window.hpp> | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include "brick.hpp" | ||||
| 
 | ||||
| class BrickGrid | ||||
| { | ||||
| private: | ||||
|     inline static const sf::Color color {100, 200, 250}; | ||||
| 
 | ||||
|     sf::FloatRect m_border; | ||||
|     int m_numBrickColumns; | ||||
|     int m_numBrickRows; | ||||
| 
 | ||||
|     std::vector<Brick> m_bricks; | ||||
|     sf::RectangleShape m_brickShape; | ||||
| 
 | ||||
|     int m_numActiveBricks; | ||||
| 
 | ||||
| public: | ||||
|     BrickGrid(); | ||||
|     BrickGrid(sf::FloatRect borders, int numBrickColumns, int numBrickRows); | ||||
| 
 | ||||
|     sf::FloatRect getBorder() const; | ||||
| 
 | ||||
|     sf::Vector2i getGridSizes() const; | ||||
| 
 | ||||
|     sf::Vector2f getBrickSizes() const; | ||||
| 
 | ||||
|     bool isBrickActive(std::pair<int, int> indexes) const; | ||||
| 
 | ||||
|     void deactivateBrick(std::pair<int, int> indexes); | ||||
| 
 | ||||
|     int getNumActiveBricks() const; | ||||
| 
 | ||||
|     void draw(sf::RenderWindow& window); | ||||
| }; | ||||
							
								
								
									
										
											BIN
										
									
								
								term1/seminar13_polymorphism/arkanoid/consola.ttf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								term1/seminar13_polymorphism/arkanoid/consola.ttf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										73
									
								
								term1/seminar13_polymorphism/arkanoid/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								term1/seminar13_polymorphism/arkanoid/main.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| #include <SFML/Window.hpp> | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include <iostream> | ||||
| #include <vector> | ||||
| #include <utility> | ||||
| #include <list> | ||||
| #include <cmath> | ||||
| #include <cstdlib> | ||||
| #include <ctime> | ||||
| 
 | ||||
| // Описываем все классы, которые мы будем использовать в программе
 | ||||
| // Это нужно сделать так как даже определение одного класса может зависеть от другого
 | ||||
| // Например, класс Bonus зависит от класса Arkanoid и наоборот
 | ||||
| struct Ball; | ||||
| struct Brick; | ||||
| struct Paddle; | ||||
| class Bonus; | ||||
| class BrickGrid; | ||||
| class Arkanoid; | ||||
| 
 | ||||
| #include "paddle.hpp" | ||||
| #include "brick_grid.hpp" | ||||
| #include "ball.hpp" | ||||
| #include "bonus.hpp" | ||||
| #include "arkanoid.hpp" | ||||
| 
 | ||||
| int main ()  | ||||
| { | ||||
|     srand(time(0)); | ||||
|     sf::ContextSettings settings; | ||||
|     settings.antialiasingLevel = 8; | ||||
|     sf::RenderWindow window(sf::VideoMode(1000, 800, 32), "Arkanoid", sf::Style::Default, settings); | ||||
|     window.setFramerateLimit(120); | ||||
| 
 | ||||
|     sf::Clock clock; | ||||
|          | ||||
|     sf::Font font; | ||||
|     if (!font.loadFromFile("consola.ttf")) | ||||
|     { | ||||
|         std::cout << "Can't load font consola.ttf" << std::endl; | ||||
|         std::exit(1); | ||||
|     } | ||||
|     Arkanoid game({0, 0, 1000, 800}, font); | ||||
|          | ||||
| 
 | ||||
|     while (window.isOpen())  | ||||
|     { | ||||
|         float dt = clock.restart().asSeconds(); | ||||
|         //std::cout << "FPS=" << static_cast<int>(1.0 / dt) << "\n"; 
 | ||||
| 
 | ||||
|         // Обработка событий
 | ||||
|         sf::Event event; | ||||
|         while(window.pollEvent(event))  | ||||
|         { | ||||
|             if(event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape))  | ||||
|             { | ||||
|                 window.close(); | ||||
|             } | ||||
|             if (event.type == sf::Event::MouseButtonPressed) | ||||
|             { | ||||
|                 game.onMousePressed(event); | ||||
|             } | ||||
|         } | ||||
|         window.clear(sf::Color(0, 0, 0)); | ||||
|         // Расчитываем новые координаты и новую скорость шарика
 | ||||
|         game.update(window, dt); | ||||
|         game.draw(window); | ||||
| 
 | ||||
|         // Отображам всё нарисованное на временном "холсте" на экран
 | ||||
|         window.display(); | ||||
|     } | ||||
|     return EXIT_SUCCESS; | ||||
| } | ||||
							
								
								
									
										18
									
								
								term1/seminar13_polymorphism/arkanoid/paddle.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								term1/seminar13_polymorphism/arkanoid/paddle.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| #include "paddle.hpp" | ||||
| 
 | ||||
| Paddle::Paddle() {} | ||||
| Paddle::Paddle(sf::Vector2f position, sf::Vector2f size) : position(position), size(size) {} | ||||
| 
 | ||||
| sf::FloatRect Paddle::getBorder() const  | ||||
| { | ||||
|     return {position.x - size.x / 2.0f, position.y - size.y / 2.0f, size.x, size.y}; | ||||
| } | ||||
| 
 | ||||
| void Paddle::draw(sf::RenderWindow& window) | ||||
| { | ||||
|     static sf::RectangleShape shape{}; | ||||
|     shape.setPosition(position - size / 2.0f); | ||||
|     shape.setSize(size); | ||||
|     shape.setFillColor(color); | ||||
|     window.draw(shape); | ||||
| } | ||||
							
								
								
									
										19
									
								
								term1/seminar13_polymorphism/arkanoid/paddle.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								term1/seminar13_polymorphism/arkanoid/paddle.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| #pragma once | ||||
| #include <SFML/Window.hpp> | ||||
| #include <SFML/Graphics.hpp> | ||||
| 
 | ||||
| struct Paddle | ||||
| {    | ||||
|     inline static const sf::Color color {sf::Color::White}; | ||||
|     sf::Vector2f position; | ||||
|     sf::Vector2f size; | ||||
| 
 | ||||
|     Paddle();  | ||||
|     Paddle(sf::Vector2f position, sf::Vector2f size); | ||||
| 
 | ||||
|     sf::FloatRect getBorder() const; | ||||
| 
 | ||||
|     void draw(sf::RenderWindow& window); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										11
									
								
								term1/seminar13_polymorphism/arkanoid/tags
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								term1/seminar13_polymorphism/arkanoid/tags
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| !_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/ | ||||
| !_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/ | ||||
| !_TAG_OUTPUT_EXCMD	mixed	/number, pattern, mixed, or combineV2/ | ||||
| !_TAG_OUTPUT_FILESEP	slash	/slash or backslash/ | ||||
| !_TAG_OUTPUT_MODE	u-ctags	/u-ctags or e-ctags/ | ||||
| !_TAG_PATTERN_LENGTH_LIMIT	96	/0 for no limit/ | ||||
| !_TAG_PROC_CWD	/home/nihonium/projects/mipt_cpp/seminar13_polymorphism/arkanoid/	// | ||||
| !_TAG_PROGRAM_AUTHOR	Universal Ctags Team	// | ||||
| !_TAG_PROGRAM_NAME	Universal Ctags	/Derived from Exuberant Ctags/ | ||||
| !_TAG_PROGRAM_URL	https://ctags.io/	/official site/ | ||||
| !_TAG_PROGRAM_VERSION	5.9.0	/p5.9.20220828.0/ | ||||
		Reference in a new issue