commit 97888084e8e0d485576e545bab7a26b4c8295bde Author: 諏訪子 Date: Tue Nov 25 20:04:17 2025 +0900 コミット 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 0000000..45a8c38 Binary files /dev/null and b/Peepo.ttf differ 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; +}