Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lab1/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.15)
project(arkanoid)

set(CMAKE_CXX_STANDARD 17)

include(FetchContent)
FetchContent_Declare(
SDL3
GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git"
GIT_TAG "main"
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(SDL3)

add_executable(arkanoid
src/main.cpp
src/Game.cpp
src/Block.cpp
src/bonuses/Bonus.cpp
src/bonuses/AccelerateBallBonus.cpp
src/bonuses/ExpandPaddleBonus.cpp
src/bonuses/RandomBallDirectionBonus.cpp
src/bonuses/SafetyNetBonus.cpp
src/bonuses/ShrinkPaddleBonus.cpp
src/bonuses/StickyPaddleBonus.cpp
)

target_include_directories(arkanoid PRIVATE include)
target_link_libraries(arkanoid PRIVATE SDL3::SDL3)
30 changes: 29 additions & 1 deletion lab1/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,30 @@
CPP laboratories
sheesh
sheesh

## Чтобы собрать игру:

```bash
mkdir build
cd build
cmake ..
cmake --build .
```
Важно: SDL3.dll должен быть в одной папке с бинарником, SDL очень топит за dynamic link
Если что, CMake fetch content сам скачает и соберет всё что нужно за нас, но .dll куда надо не положит

У неразрушаемых блоков есть белая рамка
У блоков, ускоряющих мяч есть зеленая рамка

## Управление:
1. Стрелки влево/вправо — движение каретки
2. Также каретка следует за мышью
3. R — рестарт игры
4. SPACE или нажатие любой кнопки мыши - отпустить прилипший к каретке мяч (он изначально прилипший)
5. ESCAPE - выйти из игры

## Эффекты бонусов:
- Увеличение/уменьшение каретки и скорости мяча (мгновенное) (увеличение - голубой цвет бонуса, уменьшение - красный)
- Ускорение мяча (мгновенное) (жёлтый цвет)
- Липкая каретка (действует 10 секунд) (зелёный цвет. Также меняет цвет каретки на зелёный)
- Защитная сетка (действует 10 секунд) ('одноразовое дно для шарика' из условия) (Оранжевый цвет бонуса)
- Случайное направление (мгновенное изменение траектории) (Фиолетовый цвет бонуса)
112 changes: 112 additions & 0 deletions lab1/include/Ball.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <iostream>

#include "ICollidable.hpp"
#include "IRenderable.hpp"
#include "Paddle.hpp"
#include "SDL3/SDL_pixels.h"
#include "SDL3/SDL_render.h"

struct GameState;

class Ball : public IRenderable, public ICollidable {
SDL_FRect rect;
constexpr static SDL_Color COLOR{255, 255, 255, 255};

public:
Ball(float x, float y, float w, float h) : rect{x, y, w, h} {}

void render(SDL_Renderer* renderer) override {
SDL_SetRenderDrawColor(renderer, COLOR.r, COLOR.g, COLOR.b, COLOR.a);
SDL_RenderFillRect(renderer, &rect);
}

SDL_FRect& getCollisionRect() override {
return rect;
}

bool checkCollisionWith(ICollidable& other, GameState& state) override {
bool r = ICollidable::checkCollisionWith(other, state);
if (!r) return false;
if (dynamic_cast<Paddle*>(&other)) {
if (!state.stickyPaddle) {
float hitPosition = (rect.x + rect.w / 2 - other.getCollisionRect().x) / other.getCollisionRect().w -
0.5f;
float prevVelocity = sqrtf(
state.ballVelocity.x * state.ballVelocity.x + state.ballVelocity.y * state.ballVelocity.y);
state.ballVelocity.x = hitPosition * prevVelocity;
state.ballVelocity.y = -
sqrtf(prevVelocity * prevVelocity - state.ballVelocity.x * state.ballVelocity.x);
}
else {
state.ballSticked = true;
state.stickyPaddle = false;
}
}
return true;
}

void hit(GameState& gameState, SDL_FRect& collisionResult) override {
float overlapLeft = rect.x + rect.w - collisionResult.x;
float overlapRight = collisionResult.x + collisionResult.w - rect.x;
float overlapTop = rect.y + rect.h - collisionResult.y;
float overlapBottom = collisionResult.y + collisionResult.h - rect.y;

bool horizontalCollision = std::min(overlapLeft, overlapRight) <
std::min(overlapTop, overlapBottom);

if (horizontalCollision) {
gameState.ballVelocity.x = (overlapLeft < overlapRight)
? -std::fabs(gameState.ballVelocity.x)
: fabs(gameState.ballVelocity.x);
rect.x += (overlapLeft < overlapRight) ? -collisionResult.w : collisionResult.w;
}
else {
gameState.ballVelocity.y = (overlapTop < overlapBottom)
? -fabs(gameState.ballVelocity.y)
: fabs(gameState.ballVelocity.y);
rect.y += (overlapTop < overlapBottom) ? -collisionResult.h : collisionResult.h;
}
}

bool update(float deltaTime, GameState& state, Paddle& paddle) {
if (state.ballSticked) {
rect.x = paddle.getCollisionRect().x + paddle.getCollisionRect().w / 2 - rect.w / 2;
rect.y = paddle.getCollisionRect().y - rect.h;
return false;
}

rect.x += state.ballVelocity.x * deltaTime * state.ballSpeedMultiplier;
rect.y += state.ballVelocity.y * deltaTime * state.ballSpeedMultiplier;

// Проверка поражения
if (rect.y + rect.h > SCREEN_HEIGHT) {
if (state.safetyNetActive) {
// Отскок от защитной сетки
state.ballVelocity.y = -fabs(state.ballVelocity.y);
rect.y = SCREEN_HEIGHT - 2 * BASE_BALL_HEIGHT;
state.safetyNetActive = false;
}
else {
state.lives--;
if (state.lives > 0) {
// Респавн мяча
rect = {
SCREEN_WIDTH / 2 - BASE_BALL_WIDTH / 2, SCREEN_HEIGHT / 2 - BASE_BALL_HEIGHT / 2,
BASE_BALL_WIDTH, BASE_BALL_HEIGHT
};
state.ballVelocity = {0, -BASE_BALL_SPEED};
state.ballSticked = true;
}
else {
return true;
}
}
}
return false;
}

~Ball() override = default;
};
36 changes: 36 additions & 0 deletions lab1/include/Block.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once
#include <SDL3/SDL.h>
#include "GameState.hpp"
#include "bonuses/Bonus.hpp"

constexpr float ACCELERATION_BLOCK_MULTIPLIER = 1.05f;

struct Block : ICollidable, IRenderable {
SDL_FRect rect;
int health;
bool indestructible;
bool accelerateBall;
SDL_Color color;
Bonus* innerBonus;

Block(float x, float y, float w, float h, int hp, bool indestruct, bool accelerateBall, Bonus* bonus);
bool IsDestroyed() const;

void render(SDL_Renderer* renderer) override {
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderFillRect(renderer, &rect);
if (indestructible) {
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderRect(renderer, &rect);
}
if (accelerateBall) {
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
SDL_RenderRect(renderer, &rect);
}
}

SDL_FRect& getCollisionRect() override { return rect; };
void hit(GameState& gameState, SDL_FRect& collisionResult) override;

~Block() override = default;
};
46 changes: 46 additions & 0 deletions lab1/include/Game.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once
#include <SDL3/SDL.h>
#include <vector>
#include <memory>

#include "Ball.hpp"
#include "Block.hpp"
#include "Paddle.hpp"
#include "bonuses/Bonus.hpp"

class Game {
public:
Game();
~Game();

bool Initialize();
void Run();
void ResetGame();

private:
void ProcessInput();
void Update(float deltaTime);
void UpdateBonuses(float deltaTime);
void UpdatePaddle(float deltaTime);
void Render();
void CheckCollisions();
void HandleBallBlockCollision(Block& block);
void SpawnBonus(float x, float y);
void HandleBlockHit(Block& block);

SDL_Window* window;
SDL_Renderer* renderer;
bool isRunning;
bool needsReset;

// Game objects
Paddle paddle;
Ball ball;

std::vector<std::unique_ptr<Block>> blocks;
std::vector<std::unique_ptr<Bonus>> activeBonuses;


GameState state{};
static constexpr GameState DEFAULT_STATE{};
};
39 changes: 39 additions & 0 deletions lab1/include/GameState.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

constexpr int LIVES = 1;
constexpr int SCREEN_WIDTH = 800;
constexpr int SCREEN_HEIGHT = 600;
constexpr float BASE_BALL_SPEED = 300.0f;
constexpr int BASE_PADDLE_WIDTH = 100;
constexpr int BASE_PADDLE_HEIGHT = 20;
constexpr float BASE_BALL_HEIGHT = 20;
constexpr float BASE_BALL_WIDTH = 20;

struct GameState {
GameState() = default;

//copy assignment
GameState& operator=(GameState const& other) {
score = other.score;
lives = other.lives;
stickyPaddle = other.stickyPaddle;
safetyNetActive = other.safetyNetActive;
ballSticked = other.ballSticked;
paddleSpeedMultiplier = other.paddleSpeedMultiplier;
ballSpeedMultiplier = other.ballSpeedMultiplier;
paddleWidthMultiplier = other.paddleWidthMultiplier;
ballVelocity = other.ballVelocity;
return *this;
}

int score = 0;
int lives = LIVES;
bool stickyPaddle = false;
bool safetyNetActive = true;
bool ballSticked = true;
float paddleSpeedMultiplier = 1.0f;
float ballSpeedMultiplier = 1.0f;
float paddleWidthMultiplier = 1.0f;
SDL_FPoint ballVelocity{0, -BASE_BALL_SPEED};
const float maxBallSpeed = 500.0f; // Максимальная скорость мяча
};
26 changes: 26 additions & 0 deletions lab1/include/ICollidable.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include "SDL3/SDL_rect.h"

struct GameState;

class ICollidable {
protected:
virtual void hit(GameState& gameState, SDL_FRect& collisionResult) = 0;

public:
virtual SDL_FRect& getCollisionRect() = 0;

virtual bool checkCollisionWith(ICollidable& other, GameState& state) {
SDL_FRect intersection;
if (SDL_GetRectIntersectionFloat(&other.getCollisionRect(), &getCollisionRect(), &intersection)) {
//collision
hit(state, intersection);
other.hit(state, intersection);
return true;
}
return false;
}

virtual ~ICollidable() = default;
};
19 changes: 19 additions & 0 deletions lab1/include/IExpirable.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once
class IExpirable {
public:
virtual ~IExpirable() = default;

bool activated = false;

IExpirable(float duration) : duration(duration) {}

virtual void update(const float dt, GameState& state) {
duration -= dt;
if (duration < 0) expire(state);
}

protected:
float duration; // in seconds

virtual void expire(GameState& state) = 0;
};
7 changes: 7 additions & 0 deletions lab1/include/IRenderable.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

class IRenderable {
public:
virtual void render(SDL_Renderer* renderer) = 0;
virtual ~IRenderable() = default;
};
Loading