🚀 シューティングゲーム - 解答と解説
課題4-2の要件
- プレイヤーの移動(左右)
- 弾の発射と移動
- 敵の出現と移動
- 衝突判定
- スコア管理
- ゲームオーバー判定
1. プログラムの実装 (shooting_game.c)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
#define SCREEN_WIDTH 40
#define SCREEN_HEIGHT 20
#define MAX_BULLETS 50
#define MAX_ENEMIES 10
typedef struct {
int x, y;
int active;
} GameObject;
typedef struct {
GameObject player;
GameObject bullets[MAX_BULLETS];
GameObject enemies[MAX_ENEMIES];
int score;
int gameOver;
} GameState;
void initGame(GameState* game);
void handleInput(GameState* game);
void updateGame(GameState* game);
void renderGame(GameState* game);
void spawnEnemy(GameState* game);
void fireBullet(GameState* game);
int checkCollision(GameObject* obj1, GameObject* obj2);
void clearScreen();
int main() {
system("chcp 65001");
srand((unsigned int)time(NULL));
GameState game;
initGame(&game);
while (!game.gameOver) {
if (_kbhit()) {
handleInput(&game);
}
updateGame(&game);
renderGame(&game);
Sleep(50);
}
printf("\nGame Over!\nScore: %d\n", game.score);
return 0;
}
void initGame(GameState* game) {
game->player.x = SCREEN_WIDTH / 2;
game->player.y = SCREEN_HEIGHT - 2;
game->player.active = 1;
for (int i = 0; i < MAX_BULLETS; i++) {
game->bullets[i].active = 0;
}
for (int i = 0; i < MAX_ENEMIES; i++) {
game->enemies[i].active = 0;
}
game->score = 0;
game->gameOver = 0;
}
void handleInput(GameState* game) {
char key = _getch();
switch (key) {
case 'a':
if (game->player.x > 0) game->player.x--;
break;
case 'd':
if (game->player.x < SCREEN_WIDTH - 1) game->player.x++;
break;
case ' ':
fireBullet(game);
break;
case 'q':
game->gameOver = 1;
break;
}
}
void updateGame(GameState* game) {
if (rand() % 20 == 0) {
spawnEnemy(game);
}
for (int i = 0; i < MAX_BULLETS; i++) {
if (game->bullets[i].active) {
game->bullets[i].y--;
if (game->bullets[i].y < 0) {
game->bullets[i].active = 0;
}
}
}
for (int i = 0; i < MAX_ENEMIES; i++) {
if (game->enemies[i].active) {
game->enemies[i].y++;
if (game->enemies[i].y >= SCREEN_HEIGHT) {
game->enemies[i].active = 0;
game->gameOver = 1;
}
}
}
for (int i = 0; i < MAX_BULLETS; i++) {
if (game->bullets[i].active) {
for (int j = 0; j < MAX_ENEMIES; j++) {
if (game->enemies[j].active) {
if (checkCollision(&game->bullets[i], &game->enemies[j])) {
game->bullets[i].active = 0;
game->enemies[j].active = 0;
game->score += 100;
}
}
}
}
}
}
void renderGame(GameState* game) {
clearScreen();
char buffer[SCREEN_HEIGHT][SCREEN_WIDTH + 1];
for (int y = 0; y < SCREEN_HEIGHT; y++) {
for (int x = 0; x < SCREEN_WIDTH; x++) {
buffer[y][x] = ' ';
}
buffer[y][SCREEN_WIDTH] = '\0';
}
buffer[game->player.y][game->player.x] = 'A';
for (int i = 0; i < MAX_BULLETS; i++) {
if (game->bullets[i].active) {
buffer[game->bullets[i].y][game->bullets[i].x] = '|';
}
}
for (int i = 0; i < MAX_ENEMIES; i++) {
if (game->enemies[i].active) {
buffer[game->enemies[i].y][game->enemies[i].x] = 'V';
}
}
printf("Score: %d\n", game->score);
for (int y = 0; y < SCREEN_HEIGHT; y++) {
printf("%s\n", buffer[y]);
}
}
void spawnEnemy(GameState* game) {
for (int i = 0; i < MAX_ENEMIES; i++) {
if (!game->enemies[i].active) {
game->enemies[i].x = rand() % SCREEN_WIDTH;
game->enemies[i].y = 0;
game->enemies[i].active = 1;
break;
}
}
}
void fireBullet(GameState* game) {
for (int i = 0; i < MAX_BULLETS; i++) {
if (!game->bullets[i].active) {
game->bullets[i].x = game->player.x;
game->bullets[i].y = game->player.y - 1;
game->bullets[i].active = 1;
break;
}
}
}
int checkCollision(GameObject* obj1, GameObject* obj2) {
return obj1->x == obj2->x && obj1->y == obj2->y;
}
void clearScreen() {
system("cls");
}
2. プログラムの解説
2.1 データ構造
GameObject
構造体
- x, y: 位置座標
- active: オブジェクトの有効/無効状態
2.2 主要な関数
initGame
: ゲームの初期化
- プレイヤーの初期位置設定
- 弾と敵の配列の初期化
- スコアとゲーム状態の初期化
handleInput
: 入力処理
updateGame
: ゲーム状態の更新
renderGame
: 画面描画
- ダブルバッファリング的な描画手法
- ゲーム要素の描画
- スコア表示
3. 改良のポイント
3.1 ゲームシステムの改良
- プレイヤー機能の拡張
- ライフシステム
- パワーアップアイテム
- 複数の武器タイプ
- 敵の多様化
- ステージシステム
3.2 表示の改良
- グラフィックスの向上
- UI/UXの改善
- スコア表示の改善
- HP/シールドゲージ
- ミニマップ
4. 最適化とパフォーマンス
4.1 処理の最適化
- 描画の効率化
- 部分更新による描画負荷の軽減
- 画面のちらつき防止
- バッファリング処理の改善
- 衝突判定の最適化
4.2 メモリ管理
- オブジェクトプール
- 弾のリサイクル
- 敵の再利用
- メモリ割り当ての最小化
- データ構造の最適化
5. 発展的な機能追加
5.1 ゲーム機能の拡張
- スコアシステムの拡充
- ゲームモード
- エンドレスモード
- タイムアタック
- チャレンジモード
6. デバッグとテスト
- テストケース
- 衝突判定のテスト
- 境界値テスト
- パフォーマンステスト
- デバッグ機能