コミット

This commit is contained in:
2025-11-25 20:11:42 +09:00
commit 755b579d01
18 changed files with 507 additions and 0 deletions

15
Makefile Normal file
View File

@@ -0,0 +1,15 @@
NAME=brickbreaker
# Linux、Haiku、かIllumos = /usr、FreeBSDかOpenBSD = /usr/local、NetBSD = /usr/pkg
PREFIX=/usr/local
CC=clang++
CFLAGS=-I${PREFIX}/include -L${PREFIX}/lib
LDFLAGS=-lSDL2 -lSDL2_mixer -lSDL2_ttf
FILES=main.cc src/ball.cc src/brick.cc src/render.cc
all:
${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
clean:
rm -f ${NAME}
.PHONY: all clean

32
README.md Normal file
View File

@@ -0,0 +1,32 @@
# ブリックブレイカー
7つレベルがあり、レベルを上がるとより難しくなります。\
「YOU WIN」と表示されたら、ハイスコアを教えて下さいね。(´・ω・`)
## 音楽
[Stas Gavrikさんより](https://youtube.owacon.moe/playlist?list=PLyc7oozhSJMytXF1eWMUAU2zl84W0Nkqg)
## 勉強点
* BGM
* オブジェクト指向プログラミング
* オブジェクトを非表示にする事
## ソフト
* SDL2
* SDL2_mixer
* SDL2_ttf
## Linux
```sh
make
```
## \*BSD
```sh
gmake
```
## Illumos
```sh
gmake PREFIX=/usr
```

BIN
ass/1.ogg Normal file

Binary file not shown.

BIN
ass/2.ogg Normal file

Binary file not shown.

BIN
ass/3.ogg Normal file

Binary file not shown.

BIN
ass/4.ogg Normal file

Binary file not shown.

BIN
ass/5.ogg Normal file

Binary file not shown.

BIN
ass/6.ogg Normal file

Binary file not shown.

BIN
ass/7.ogg Normal file

Binary file not shown.

BIN
font.ttf Normal file

Binary file not shown.

76
main.cc Normal file
View File

@@ -0,0 +1,76 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_mixer.h>
#include <iostream>
#include "src/render.hh"
#include "src/ball.hh"
#include "src/player.hh"
#include "src/brick.hh"
int main(void) {
Render r;
Player p(r);
Ball bl(r, p);
Brick br(r);
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
std::cout << "SDL_Init()に失敗" << '\n';
return -1;
}
if (SDL_CreateWindowAndRenderer(r.width, r.height, 0, &r.window, &r.renderer) < 0) {
std::cout << "SDL_CreateWindowAndRenderer()に失敗" << '\n';
SDL_Quit();
return -1;
}
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
std::cout << "Mix_OpenAudio()に失敗" << '\n';
SDL_DestroyRenderer(r.renderer);
SDL_DestroyWindow(r.window);
SDL_Quit();
return -1;
}
SDL_SetWindowTitle(r.window, "ブリックブレイカー");
TTF_Init();
r.font = TTF_OpenFont("font.ttf", r.fontsize);
if (!r.font) {
std::cout << "フォントを読込に失敗" << '\n';
SDL_DestroyRenderer(r.renderer);
SDL_DestroyWindow(r.window);
Mix_CloseAudio();
SDL_Quit();
return -1;
}
static int lastTime = 0;
br.resetBricks(r, p, bl);
while (r.running) {
r.lastFrame = SDL_GetTicks();
if (r.lastFrame >= lastTime + 1000) {
lastTime = r.lastFrame;
r.fps = r.frameCount;
r.frameCount = 0;
}
if (!r.gameover) {
r.update(r, p, bl, br);
}
r.input(p);
r.render(p, bl, br);
}
TTF_CloseFont(r.font);
SDL_DestroyRenderer(r.renderer);
SDL_DestroyWindow(r.window);
SDL_Quit();
Mix_FreeMusic(r.music);
Mix_CloseAudio();
TTF_Quit();
return 0;
}

9
src/ball.cc Normal file
View File

@@ -0,0 +1,9 @@
#include "ball.hh"
void Ball::resetBall(Render &r, Player &p) {
p.player.x = (r.width / 2) - (p.player.w / 2);
Ball::ball.y = p.player.y - (p.player.h * 4);
Ball::yvelocity = Ball::ballspeed / 2;
Ball::xvelocity = 0;
Ball::ball.x = r.width / 2 - (Ball::size / 2);
}

30
src/ball.hh Normal file
View File

@@ -0,0 +1,30 @@
#ifndef BALL_H
#define BALL_H
#include <SDL2/SDL.h>
#include <iostream>
#include "player.hh"
class Ball {
public:
float ballspeed = 8.0f;
int size = 16;
float xvelocity;
float yvelocity;
SDL_Rect ball;
void resetBall(Render &r, Player &p);
Ball(Render& r, Player& p) {
ball.y = p.player.y - (p.player.h / 2);
yvelocity = ballspeed / 2;
xvelocity = ball.x = 0;
ball.w = ball.h = size;
ball.x = r.width / 2 - (size / 2);
}
};
#endif

67
src/brick.cc Normal file
View File

@@ -0,0 +1,67 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include <SDL2/SDL_ttf.h>
#include <iostream>
#include "render.hh"
#include "player.hh"
#include "ball.hh"
#include "brick.hh"
void Brick::resetBricks(Render &r, Player &p, Ball &bl) {
srand(static_cast<unsigned int>(time(nullptr)));
r.c1r = rand() % 256;
r.c1g = rand() % 256;
r.c1b = rand() % 256;
r.c2r = rand() % 256;
r.c2g = rand() % 256;
r.c2b = rand() % 256;
r.c3r = rand() % 256;
r.c3g = rand() % 256;
r.c3b = rand() % 256;
r.c4r = rand() % 256;
r.c4g = rand() % 256;
r.c4b = rand() % 256;
r.c5r = rand() % 256;
r.c5g = rand() % 256;
r.c5b = rand() % 256;
r.c6r = rand() % 256;
r.c6g = rand() % 256;
r.c6b = rand() % 256;
if (r.level > 1) {
Mix_HaltMusic();
}
std::string music = "ass/" + std::to_string(r.level) + ".ogg";
r.music = Mix_LoadMUS(music.c_str());
if (Mix_PlayMusic(r.music, -1) < 0) {
std::cout << "OGGファイルのエラー" << Mix_GetError() << '\n';
r.running = false;
TTF_CloseFont(r.font);
SDL_DestroyRenderer(r.renderer);
SDL_DestroyWindow(r.window);
SDL_Quit();
Mix_CloseAudio();
TTF_Quit();
}
for (int i = 0; i < Brick::col * Brick::row; i++) {
Brick::bricks[i] = 1;
}
bl.size -= 2;
bl.ballspeed += 1.4f;
p.player.w -= 1.4f;
p.player.x = (r.width / 2) - (p.player.w / 2);
bl.ball.y = p.player.y - (p.player.h * 4);
bl.yvelocity = bl.ballspeed / 2;
bl.xvelocity = 0;
bl.ball.x = r.width / 2 - (bl.size / 2);
}
void Brick::setBricks(int i) {
Brick::brick.x = (((i%Brick::col) + 1) * Brick::gaps) + ((i%Brick::col) * Brick::brick.w) - (Brick::gaps/2);
Brick::brick.y = Brick::brick.h * 3 + (((i%Brick::row) + 1) * Brick::gaps) + ((i%Brick::row) * Brick::brick.h) - (Brick::gaps/2);
}

31
src/brick.hh Normal file
View File

@@ -0,0 +1,31 @@
#ifndef BRICK_H
#define BRICK_H
#include <SDL2/SDL.h>
#include <iostream>
#include "render.hh"
#include "player.hh"
#include "ball.hh"
class Brick {
public:
int col = 7;
int row = 5;
int gaps = 16;
bool bricks[7*5];
SDL_Color color = {255, 255, 255};
SDL_Rect brick;
void resetBricks(Render &r, Player &p, Ball &bl);
void setBricks(int i);
Brick(Render& r) {
brick.w = (r.width - (gaps * col)) / col;
brick.h = 22;
}
};
#endif

25
src/player.hh Normal file
View File

@@ -0,0 +1,25 @@
#ifndef PLAYER_H
#define PLAYER_H
#include <SDL2/SDL.h>
#include <iostream>
#include "render.hh"
class Player {
public:
SDL_Rect player, lives;
int score = 0;
int scoreMultiplier = 1;
int livesCount = 3;
Player(Render& r) {
player.h = 12;
player.y = r.height - player.h - 32;
player.w = r.width / 4;
player.x = (r.width / 2) - (player.w / 2);
}
};
#endif

170
src/render.cc Normal file
View File

@@ -0,0 +1,170 @@
#include "render.hh"
#include "ball.hh"
#include "player.hh"
#include "brick.hh"
#define PI 3.14
void Render::gameOver(Player &p, Brick &br, bool winner) {
if (winner) {
Render::winner = true;
} else {
Render::winner = false;
}
Render::gameover = true;
Mix_HaltMusic();
}
void Render::write(Player &p, Brick &br, std::string text, int x, int y) {
SDL_Surface *surface;
SDL_Texture *texture;
const char* t = text.c_str();
surface = TTF_RenderText_Solid(Render::font, t, br.color);
texture = SDL_CreateTextureFromSurface(Render::renderer, surface);
p.lives.w = surface->w;
p.lives.h = surface->h;
p.lives.x = x - p.lives.w;
p.lives.y = y - p.lives.h;
SDL_FreeSurface(surface);
SDL_RenderCopy(Render::renderer, texture, NULL, &p.lives);
SDL_DestroyTexture(texture);
}
void Render::update(Render &r, Player &p, Ball &bl, Brick &br) {
if (p.livesCount <= 0) {
gameOver(p, br, false);
}
if (SDL_HasIntersection(&bl.ball, &p.player)) {
double rel = (p.player.x + ((float)p.player.w/2)) - ((float)bl.ball.x + ((float)bl.size/2));
double norm = rel / ((float)p.player.w/2);
double bounce = norm * (5*PI/12);
bl.yvelocity = -bl.ballspeed * cos(bounce);
bl.xvelocity = bl.ballspeed * -sin(bounce);
p.scoreMultiplier = 1;
}
if (bl.ball.y <= 0) {
bl.yvelocity = -bl.yvelocity;
}
if (bl.ball.y + bl.size >= Render::height) {
bl.yvelocity = -bl.yvelocity;
p.livesCount--;
bl.resetBall(r, p);
}
if (bl.ball.x <= 0 || bl.ball.x + bl.size >= Render::width) {
bl.xvelocity = -bl.xvelocity;
}
bl.ball.x += bl.xvelocity;
bl.ball.y += bl.yvelocity;
if (p.player.x < 0) {
p.player.x = 0;
}
if (p.player.x + p.player.w > Render::width) {
p.player.x = Render::width - p.player.w;
}
bool reset = true;
for (int i = 0; i < br.col*br.row; i++) {
br.setBricks(i);
if (SDL_HasIntersection(&bl.ball, &br.brick) && br.bricks[i]) {
br.bricks[i] = 0;
p.score += 100 * p.scoreMultiplier;
p.scoreMultiplier++;
if (bl.ball.x >= br.brick.x) {
bl.xvelocity = -bl.xvelocity;
bl.ball.x -= 1;
}
if (bl.ball.x <= br.brick.x) {
bl.xvelocity = -bl.xvelocity;
bl.ball.x += 1;
}
if (bl.ball.y <= br.brick.y) {
bl.yvelocity = -bl.yvelocity;
bl.xvelocity = -bl.xvelocity;
bl.ball.y -= 1;
}
if (bl.ball.y >= br.brick.y) {
bl.yvelocity = -bl.yvelocity;
bl.xvelocity = -bl.xvelocity;
bl.ball.y += 1;
}
}
if (br.bricks[i]) {
reset = false;
}
}
if (reset) {
Render::level++;
if (r.level > 7) {
gameOver(p, br, true);
} else {
br.resetBricks(r, p, bl);
}
}
}
void Render::input(Player &p) {
SDL_Event e;
const Uint8 *keystates = SDL_GetKeyboardState(NULL);
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
Render::running = false;
}
}
if (keystates[SDL_SCANCODE_Q]) Render::running = false;
if (!Render::gameover) {
if (keystates[SDL_SCANCODE_LEFT]) p.player.x -= Render::xpos;
if (keystates[SDL_SCANCODE_RIGHT]) p.player.x += Render::xpos;
}
}
void Render::render(Player &p, Ball &bl, Brick &br) {
SDL_SetRenderDrawColor(Render::renderer, 0x00, 0x00, 0x00, 255);
SDL_RenderClear(Render::renderer);
Render::frameCount++;
Render::timerFPS = SDL_GetTicks() - Render::lastFrame;
if (Render::timerFPS < (1000/60)) {
SDL_Delay((1000/60) - Render::timerFPS);
}
SDL_SetRenderDrawColor(Render::renderer, 255, 255, 255, 255);
SDL_RenderFillRect(Render::renderer, &p.player);
SDL_RenderFillRect(Render::renderer, &bl.ball);
write(p, br, "SCORE: " + std::to_string(p.score), Render::width, Render::fontsize*1.5f);
write(p, br, "LIVES: " + std::to_string(p.livesCount), 130, Render::fontsize*1.5f);
write(p, br, "LEVEL: " + std::to_string(Render::level), 130, Render::fontsize*3.5f);
if (Render::gameover) {
if (Render::winner) write(p, br, "YOU WIN", Render::width/2+Render::fontsize, Render::height/2);
else write(p, br, "YOU LOSS", Render::width/2+Render::fontsize, Render::height/2);
}
for (int i = 0; i < br.col*br.row; i++) {
SDL_SetRenderDrawColor(Render::renderer, c1r, c1g, c1b, 255);
if (i%2 == 0) SDL_SetRenderDrawColor(Render::renderer, c2r, c2g, c2b, 255);
if (i%3 == 0) SDL_SetRenderDrawColor(Render::renderer, c3r, c3g, c3b, 255);
if (i%4 == 0) SDL_SetRenderDrawColor(Render::renderer, c4r, c4g, c4b, 255);
if (i%5 == 0) SDL_SetRenderDrawColor(Render::renderer, c5r, c5g, c5b, 255);
if (i%6 == 0) SDL_SetRenderDrawColor(Render::renderer, c6r, c6g, c6b, 255);
if (br.bricks[i]) {
br.setBricks(i);
SDL_RenderFillRect(Render::renderer, &br.brick);
}
}
SDL_RenderPresent(Render::renderer);
}

52
src/render.hh Normal file
View File

@@ -0,0 +1,52 @@
#ifndef RENDER_H
#define RENDER_H
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_mixer.h>
#include <iostream>
class Player;
class Ball;
class Brick;
class Render {
public:
bool running, gameover, winner;
int frameCount, timerFPS, lastFrame, fps = 0;
int width = 620;
int height = 720;
int fontsize = 16;
int xpos = 9;
int level = 1;
// 色
int c1r, c1g, c1b;
int c2r, c2g, c2b;
int c3r, c3g, c3b;
int c4r, c4g, c4b;
int c5r, c5g, c5b;
int c6r, c6g, c6b;
SDL_Window *window;
SDL_Renderer *renderer;
TTF_Font* font;
Mix_Music* music;
void update(Render &r, Player &p, Ball &bl, Brick &br);
void input(Player &p);
void render(Player &p, Ball &bl, Brick &br);
Render() {
winner = false;
gameover = false;
running = true;
}
private:
void gameOver(Player &p, Brick &br, bool winner);
void write(Player &p, Brick &br, std::string text, int x, int y);
};
#endif