From 97888084e8e0d485576e545bab7a26b4c8295bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AB=8F=E8=A8=AA=E5=AD=90?= Date: Tue, 25 Nov 2025 20:04:17 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=B3=E3=83=9F=E3=83=83=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 + Makefile | 24 +++++ Peepo.ttf | Bin 0 -> 24812 bytes README.md | 37 ++++++++ main.cc | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 319 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 Makefile create mode 100644 Peepo.ttf create mode 100644 README.md create mode 100644 main.cc diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0a0d870 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +# 1.0.0 +* 最初リリース diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8926e8c --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +NAME=pong +VERSION=1.0.0 +# 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_ttf +FILES=main.cc + +all: + ${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} + +clean: + rm -f ${NAME} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f ${NAME} ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${NAME} + +.PHONY: all clean install uninstall diff --git a/Peepo.ttf b/Peepo.ttf new file mode 100644 index 0000000000000000000000000000000000000000..45a8c384a03614fdd3ceef6de5098b98e9ec0fa5 GIT binary patch literal 24812 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a5231c --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# pong + +初めてSDLだけでゲームを作りました。\ +2時間以内で完成しました。 + +このpongゲームはレベルアップシステムがあり、レベルが上がると徐々に難しくなります。\ +また、NPCが10ポイントを獲得すると、ゲームオーバーになります。 + +## 勉強点 +* C++、超ひさしぶりですから(´・ω・`) +* SDL2(今まではC言語だけで座ったことがある) +* フォントライブラリ +* スコア +* プレイヤーの移動 +* NPCの移動 +* レベルアップ +* ゲームオーバー +* ゲームループ + +## ソフト +* SDL2 +* SDL2_ttf + +## Linux +```sh +make +``` + +## \*BSD +```sh +gmake +``` + +## Illumos +```sh +gmake PREFIX=/usr +``` diff --git a/main.cc b/main.cc new file mode 100644 index 0000000..027e136 --- /dev/null +++ b/main.cc @@ -0,0 +1,256 @@ +#include +#include +#include + +#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; +}