This repository has been archived on 2023-05-13. You can view files and clone it, but cannot push or open issues or pull requests.
mipt_cpp/seminar13_polymorphism/arkanoid/arkanoid.cpp

223 lines
6.5 KiB
C++
Raw Normal View History

2023-01-04 13:46:41 +03:00
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <list>
#include <cmath>
2023-01-04 14:56:03 +03:00
2023-01-04 14:37:54 +03:00
#include "arkanoid.hpp"
2023-01-04 14:56:03 +03:00
#include "bonus.hpp"
2023-01-04 13:46:41 +03:00
const double pi = 3.14159265358979323846;
2023-01-04 14:37:54 +03:00
void Arkanoid::addRandomBonus(sf::Vector2f position)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
if (m_bonuses.size() > kMaxNumBonuses)
return;
int max_rand = 10000;
if ((rand() % max_rand) * 1.0f / max_rand < m_bonusProbability)
{
m_bonuses.push_back(new Bonus(position));
}
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// Функция, которая обрабатывает все столкновения шарика
void Arkanoid::handleBallCollisions(Ball& ball)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
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);
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
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}
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
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});
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
sf::FloatRect Arkanoid::getBorder() const
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
return m_border;
}
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
const Paddle& Arkanoid::getPaddle() const
{
return m_paddle;
}
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
const BrickGrid& Arkanoid::getBrickGrid() const
{
return m_brickGrid;
}
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
void Arkanoid::addBall(const Ball& ball)
{
if (m_balls.size() < kMaxNumBalls)
m_balls.push_back(ball);
}
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
// Эта функция вызывается каждый кадр
void Arkanoid::update(const sf::RenderWindow& window, float dt)
{
m_time += dt;
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
// Устанавливаем положение ракетки
sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window));
m_paddle.position.x = mousePosition.x;
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
// Обрабатываем шарики
for (std::list<Ball>::iterator it = m_balls.begin(); it != m_balls.end();)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
(*it).update(dt);
handleBallCollisions(*it);
if ((*it).position.y > m_border.top + m_border.height)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
it = m_balls.erase(it);
}
else
{
it++;
2023-01-04 13:46:41 +03:00
}
}
2023-01-04 14:37:54 +03:00
// Если шариков нет, то переходи в режим начала игры и уменьшаем кол-во жизней
if (m_gameState == GameState::running && m_balls.size() == 0)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
m_gameState = GameState::stuck;
m_numLives--;
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// Если жизни кончились, то переходим в состояние конца игры (проигрыш)
if (m_numLives < 0)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
m_gameState = GameState::endLose;
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// Если блоки кончились, то переходим в состояние конца игры (победа)
if (m_brickGrid.getNumActiveBricks() == 0)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
m_gameState = GameState::endWin;
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// Обрабатываем бонусы
for (auto it = m_bonuses.begin(); it != m_bonuses.end();)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
(*it)->update(dt);
if ((*it)->isColiding(m_paddle))
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
(*it)->activate(*this);
delete *it;
it = m_bonuses.erase(it);
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
else if ((*it)->m_position.y > m_border.top + m_border.height)
{
delete (*it);
it = m_bonuses.erase(it);
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
else
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
it++;
2023-01-04 13:46:41 +03:00
}
}
2023-01-04 14:37:54 +03:00
}
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
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);
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
// Рисуем блоки
m_brickGrid.draw(window);
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
// Рисуем шарики
for (Ball& ball : m_balls)
{
ball.draw(window);
}
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
// Рисуем ракетку
m_paddle.draw(window);
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
// Если мы в режиме начала игры, то рисуем шарик на ракетке
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);
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// Рисуем кол-во жизней вверху слева
for (int i = 0; i < m_numLives; i++)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
m_initialBall.position = {m_initialBall.radius * (3 * i + 2), 2 * m_initialBall.radius};
m_initialBall.draw(window);
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// Рисуем бонусы
for (Bonus* pbonus : m_bonuses)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
pbonus->draw(window);
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// При завершении игры рисуем надпись
if (m_gameState == GameState::endWin)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
m_endText.setString("You Win!");
window.draw(m_endText);
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
// При завершении игры рисуем надпись
if (m_gameState == GameState::endLose)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
m_endText.setString("You Lose!");
window.draw(m_endText);
}
}
2023-01-04 13:46:41 +03:00
2023-01-04 14:37:54 +03:00
void Arkanoid::onMousePressed(sf::Event& event)
{
switch (m_gameState)
{
case GameState::stuck:
if (event.mouseButton.button == sf::Mouse::Left)
2023-01-04 13:46:41 +03:00
{
2023-01-04 14:37:54 +03:00
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});
2023-01-04 13:46:41 +03:00
}
2023-01-04 14:37:54 +03:00
break;
case GameState::running:
break;
case GameState::endLose:
break;
case GameState::endWin:
break;
2023-01-04 13:46:41 +03:00
}
}