/* * 大砲ゲーム Ver.2 * by yanagawa@kushiro-ct.ac.jp * * 開発履歴 * Ver.1 敵機を撃ち落とせるようにした * Ver.2 敵機と砲弾を複数にした. */ #include #include #include #include #include #define GRAVITY 0.01 // 重力加速度 #define N_ENEMY 5 // 敵機の最大数 #define N_BULLET 5 // 砲弾の最大数 #define READYINT 10 // 砲弾の発射間隔 /* 移動物体構造体 */ typedef struct { double px, py; // 位置 (position) double vx, vy; // 速度 (velocity) double sx, sy; // サイズ(中心からの距離) int life; // 生死判定用フラグ (0:dead / 1:alive) } Object; /* 物体の初期化 */ void InitObject(Object *obj, double px, double py, double vx, double vy, double sx, double sy, int life) { obj->px = px; obj->py = py; obj->vx = vx; obj->vy = vy; obj->sx = sx; obj->sy = sy; obj->life = life; } void InitEnemy(Object *enemy, double px, double py, double vx, double vy, int life) { InitObject(enemy, px, py, vx, vy, 3.0, 1.0, life); } void InitEnemies(Object enemy[], int n, double px, double py, double vx, double vy, int life) { int i; for (i = 0; i < n; i++) { InitObject(&enemy[i], px, py, vx, vy, 3.0, 1.0, life); } } void InitBullet(Object *bullet, double px, double py, double vx, double vy, int life) { InitObject(bullet, px, py, vx, vy, 0.0, 0.0, life); } void InitBullets(Object bullet[], int n, double px, double py, double vx, double vy, int life) { int i; for (i = 0; i < n; i++) { InitObject(&bullet[i], px, py, vx, vy, 0.0, 0.0, life); } } /* 物体の運動 */ void MoveObject(Object *obj) { obj->px += obj->vx; obj->py += obj->vy; } /* 弾丸の運動 */ void MoveBullet(Object *obj) { int w, h; getmaxyx(stdscr, h, w); obj->vy += GRAVITY; MoveObject(obj); if (((int)(obj->px) < 0) || ((int)(obj->px) >= w)) obj->life = 0; if ((int)(obj->py) >= h) obj->life = 0; } /* 敵の運動 */ void MoveEnemy(Object *obj) { int w, h; getmaxyx(stdscr, h, w); MoveObject(obj); if (((int)(obj->px) < 0) || ((int)(obj->px) >= w)) obj->life = 0; if (((int)(obj->py) < 0) || ((int)(obj->py) >= h)) obj->life = 0; } /* 弾丸の表示 */ void DrawBullet(Object *bullet) { attrset(COLOR_PAIR(3)|A_BOLD); mvaddch((int)(bullet->py), (int)(bullet->px), '*'); } void DrawBullets(Object bullet[], int n) { int i; for (i = 0; i < n; i++) { if (bullet[i].life != 0) DrawBullet(&bullet[i]); } } /* 角度の表示 */ void DrawAngle(int y, int x, int angle) { attrset(COLOR_PAIR(7)); mvprintw(y, x, "angle : %d deg", angle); } /* ブロック列の表示 */ void DrawBlocks(int y, int x, char *s) { int w, h; getmaxyx(stdscr, h, w); if (y < 0) return; if (y >= h) return; while (*s != '\0') { if (x < 0); else if (x >= w); else if (*s != ' ') mvaddch(y, x, *s); s++; x++; } } /* 敵の表示 */ void DrawEnemy(Object *enemy) { int x, y; attrset(COLOR_PAIR(2)); x = (int)(enemy->px) - 3; y = (int)(enemy->py); DrawBlocks(y-1, x, " *|| "); DrawBlocks(y , x, "<=====P"); DrawBlocks(y+1, x, " *\\\\ "); } void DrawEnemies(Object enemy[], int n) { int i; for (i = 0; i < n; i++) { if (enemy[i].life != 0) DrawEnemy(&enemy[i]); } } /* 0 以上 n 未満の乱数 */ int Rand(int n) { return ((int)(n*(rand()/(RAND_MAX + 1.0)))); } /* 衝突の判定 */ int ChkHit(Object *obj1, Object *obj2) { if (fabs(obj1->px - obj2->px) > (obj1->sx + obj2->sx)) return (0); if (fabs(obj1->py - obj2->py) > (obj1->sy + obj2->sy)) return (0); return (1); } /* ゲームの本体 */ void Game() { Object bullet[N_BULLET]; Object enemy[N_ENEMY]; int h, w; int key, fire, ready, angle; int i, j; srand(time(NULL)); getmaxyx(stdscr, h, w); InitBullets(bullet, N_BULLET, 0.0, 0.0, 0.0, 0.0, 0); InitEnemies(enemy, N_ENEMY, 0.0, 0.0, 0.0, 0.0, 0); angle = 45; ready = 0; timeout(0); while (1) { erase(); getmaxyx(stdscr, h, w); DrawBullets(bullet, N_BULLET); DrawEnemies(enemy, N_ENEMY); DrawAngle(0, 0, angle); refresh(); fire = 0; ; key = getch(); switch (key) { case 'h' : case 'k' : case KEY_LEFT : case KEY_UP : angle += 5; break; // 角度 up case 'l' : case 'j' : case KEY_RIGHT : case KEY_DOWN : angle -= 5; break; // 角度 down case ' ' : fire = 1; break; // 発射 case 'q' : return; // ゲーム中止 } if (ready <= 0) { if (fire == 1) { for (i = 0; i < N_BULLET; i++) { if (bullet[i].life == 0) { InitBullet(&bullet[i], 0.0, (double)(h-1), cos(angle*M_PI/180.0), -sin(angle*M_PI/180.0), 1); ready = READYINT; break; } } } } else { ready--; } for (i = 0; i < N_BULLET; i++) { if (bullet[i].life == 0) continue; for (j = 0; j < N_ENEMY; j++) { if (enemy[j].life != 0) { if (ChkHit(&bullet[i], &enemy[j]) != 0) { bullet[i].life = 0; enemy[j].life = 0; // beep(); flash(); break; } } } } for (i = 0; i < N_BULLET; i++) { if (bullet[i].life != 0) MoveBullet(&bullet[i]); } for (j = 0; j < N_ENEMY; j++) { if (enemy[j].life != 0) { MoveEnemy(&enemy[j]); } else { InitEnemy(&enemy[j], (double)(w-1), (double)Rand(h/2), -(Rand(4)+3)*0.1 , 0.0, 1); } } usleep(20000); } } /* タイトル画面 */ int Title() { int h, w, key; getmaxyx(stdscr, h, w); erase(); attrset(COLOR_PAIR(1)); mvaddstr(h/2-2, (w-10)/2, "大砲ゲーム"); attrset(COLOR_PAIR(2)); mvaddstr(h/2, (w-21)/2, "Push [Space] to start"); mvaddstr(h/2+1, (w-16)/2, "Push [Q] to quit"); attrset(COLOR_PAIR(3)); mvaddstr(h/2+3, (w-17)/2, "[Space] [↑] [↓]"); mvaddstr(h/2+4, (w-17)/2, " fire up down"); move(0, 0); timeout(-1); key = getch(); return (key); } /* 色の初期化 */ void InitColor(int bg) { start_color(); /* 文字表示用の色 */ init_pair(0, COLOR_BLACK, bg); init_pair(1, COLOR_RED, bg); init_pair(2, COLOR_GREEN, bg); init_pair(3, COLOR_YELLOW, bg); init_pair(4, COLOR_BLUE, bg); init_pair(5, COLOR_MAGENTA, bg); init_pair(6, COLOR_CYAN, bg); init_pair(7, COLOR_WHITE, bg); /* ブロックキャラクタ表示用の色 */ init_pair(10, COLOR_BLACK, COLOR_BLACK); init_pair(11, COLOR_RED, COLOR_RED); init_pair(12, COLOR_GREEN, COLOR_GREEN); init_pair(13, COLOR_YELLOW, COLOR_YELLOW); init_pair(14, COLOR_BLUE, COLOR_BLUE); init_pair(15, COLOR_MAGENTA, COLOR_MAGENTA); init_pair(16, COLOR_CYAN, COLOR_CYAN); init_pair(17, COLOR_WHITE, COLOR_WHITE); /* 背景の色 */ bkgd(COLOR_PAIR(7)); } int main() { initscr(); noecho(); cbreak(); curs_set(0); keypad(stdscr, TRUE); InitColor(COLOR_BLACK); while (1) { if (Title() == 'q') break; Game(); } endwin(); return (0); }