このリポジトリは2023-11-17にアーカイブされています。 ファイルの閲覧とクローンは可能ですが、プッシュ、イシューの作成、プルリクエストはできません。
pong/main.cc

257 行
6.6 KiB
C++

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <iostream>
#define WIDTH 720
#define HEIGHT 720
#define FONTSIZE 32
#define BALLSIZE 16
#define BALLSPEED 16
#define SPEED 9
#define PI 3.1415926535897323846
SDL_Renderer* renderer;
SDL_Window* window;
TTF_Font* font;
SDL_Color color;
bool running;
int frameCount, timerFPS, lastFrame, fps;
SDL_Rect player, npc, ball, scoreboard, leveldisplay;
float xvelocity, yvelocity, ballvelocity, ballsize;
std::string score, slevel;
int lscore, rscore, level, games;
bool turn, isgameover;
std::string version = "1.0.0";
void serve() {
player.y = npc.y = (HEIGHT/2) - (player.h/2);
if (turn) {
ball.x = player.x + (player.w * 4);
xvelocity = (BALLSPEED*ballvelocity)/2;
turn = false;
} else {
ball.x = npc.x - (player.w * 4);
xvelocity = -(BALLSPEED*ballvelocity)/2;
turn = true;
}
yvelocity = 0;
ball.y = HEIGHT / 2 - ((BALLSIZE-ballsize)/2);
}
void write(std::string text, int x, int y) {
SDL_Surface *surface;
SDL_Texture *texture;
const char* t = text.c_str();
surface = TTF_RenderText_Solid(font, t, color);
texture = SDL_CreateTextureFromSurface(renderer, surface);
// スコアボード
scoreboard.w = surface->w;
scoreboard.h = surface->h;
scoreboard.x = x - scoreboard.w;
scoreboard.y = y - scoreboard.h;
// レベル表示
leveldisplay.w = surface->w;
leveldisplay.h = surface->h;
leveldisplay.x = x - scoreboard.w + 8;
leveldisplay.y = y - scoreboard.h + 8;
// 掃除
SDL_FreeSurface(surface);
SDL_RenderCopy(renderer, texture, NULL, &scoreboard);
SDL_DestroyTexture(texture);
}
void gameover() {
xvelocity = yvelocity = ballvelocity = 0;
}
void update() {
// ボールとコンピュータの衝突検出
if (SDL_HasIntersection(&ball, &npc)) {
double rel = (npc.y + (npc.h / 2)) - (ball.y + ((BALLSIZE-ballsize) / 2));
double norm = rel / (npc.h / 2);
double bounce = norm * (5 * PI / 12);
xvelocity = -(BALLSPEED*ballvelocity) * cos(bounce);
yvelocity = (BALLSPEED*ballvelocity) * -sin(bounce);
}
// ボールとプレイヤーの衝突検出
if (SDL_HasIntersection(&ball, &player)) {
double rel = (player.y + (player.h / 2)) - (ball.y + ((BALLSIZE-ballsize) / 2));
double norm = rel / (player.h / 2);
double bounce = norm * (5 * PI / 12);
xvelocity = (BALLSPEED*ballvelocity) * cos(bounce);
yvelocity = (BALLSPEED*ballvelocity) * -sin(bounce);
}
// コンピュータがボールの方向を付いてくる
if (ball.y > npc.y + (npc.h/2)) npc.y += ((SPEED+level*0.5f)*ballvelocity);
if (ball.y < npc.y + (npc.h/2)) npc.y -= ((SPEED+level*0.5f)*ballvelocity);
// ボールが左右に逃げたら、スコアアップにして、最初フレームから再開
if (ball.x <= 0) {
rscore++;
serve();
} else if (ball.x + (BALLSIZE-ballsize) >= WIDTH) {
lscore++;
serve();
games++;
}
// コンピュータさんを十分倒したら、レベルアップにする
if (games == ((level + 1) * 2)) {
level++;
ballvelocity += 0.25f;
ballsize -= 0.2f;
games = 0;
}
// コンピュータのスコアが10になったら、ゲームオーバーだ
if (rscore > 9) {
isgameover = true;
}
// ボールが上下に逃げる事に対して守る
if (ball.y <= 0 || ball.y + (BALLSIZE-ballsize) >= HEIGHT) yvelocity = -yvelocity;
// ボールの移動
ball.x += xvelocity;
ball.y += yvelocity;
// スコアやレベルの文字
score = std::to_string(lscore) + " " + std::to_string(rscore);
slevel = "Lv" + std::to_string(level);
// プレイヤーを上下に逃げられない為
if (player.y < 0) player.y = 0;
if (player.y + player.h > HEIGHT) player.y = HEIGHT-player.h;
// コンピューターを上下に逃げられない為
if (npc.y < 0) npc.y = 0;
if (npc.y + npc.h > HEIGHT) npc.y = HEIGHT-npc.h;
}
void input() {
SDL_Event e;
const Uint8 *keystates = SDL_GetKeyboardState(NULL);
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
running = false;
}
}
if (keystates[SDL_SCANCODE_Q]) running = false; // Qでゲームを終了
if (!isgameover) {
if (keystates[SDL_SCANCODE_UP]) player.y -= (SPEED*ballvelocity); // ↑でプレイヤーを上がる
if (keystates[SDL_SCANCODE_DOWN]) player.y += (SPEED*ballvelocity); // ↓でプレイヤーを下げる
}
}
void render() {
// 黒色背景
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 255);
SDL_RenderClear(renderer);
frameCount++;
timerFPS = SDL_GetTicks() - lastFrame;
if (timerFPS < (1000/60)) {
SDL_Delay((1000/60) - timerFPS);
}
// 背景以外もレンダーして
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 255);
// プレイヤー、コンピュータ、とボールを創作
if (!isgameover) {
SDL_RenderFillRect(renderer, &player);
SDL_RenderFillRect(renderer, &npc);
SDL_RenderFillRect(renderer, &ball);
}
write(score, WIDTH/2+FONTSIZE, FONTSIZE*2);
write(slevel, WIDTH/2+FONTSIZE, FONTSIZE*3);
if (isgameover) {
write("GAME OVER", WIDTH/2+84, HEIGHT/2);
}
write("076.moe SUWAKO NO PONG v" + version, WIDTH-150, HEIGHT-20);
SDL_RenderPresent(renderer);
}
int main(void) {
// SDLを初期化
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
std::cout << "SDL_Init()に失敗" << '\n';
return -1;
}
// ウィンドウとレンダーを創作
if (SDL_CreateWindowAndRenderer(WIDTH, HEIGHT, 0, &window, &renderer) < 0) {
std::cout << "SDL_CreateWindowAndRenderer()に失敗" << '\n';
SDL_Quit();
return -1;
}
// フォントを読込
TTF_Init();
font = TTF_OpenFont("Peepo.ttf", FONTSIZE);
if (!font) {
std::cout << "フォントを読込に失敗" << '\n';
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return -1;
}
// 初期設定
running = true;
static int lastTime = 0;
color.r = color.g = color.b = 255;
lscore = rscore = games = 0;
ballvelocity = ballsize = 1.0f;
level = 1;
player.x = 32;
player.h = (HEIGHT/4)/(ballsize*1.15f);
player.y = (HEIGHT/2) - (player.h/2);
player.w = 12;
npc = player;
npc.x = WIDTH - npc.w - 32;
ball.w = ball.h = (BALLSIZE-ballsize);
serve();
// ゲームループ
while (running) {
lastFrame = SDL_GetTicks();
if (lastFrame >= (lastTime + 1000)) {
lastTime = lastFrame;
fps = frameCount;
frameCount = 0;
}
if (!isgameover) {
update();
input();
render();
} else {
gameover();
input();
render();
}
}
// 掃除
TTF_CloseFont(font);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}