/* * 大砲ゲーム Ver.5 * by yanagawa@kushiro-ct.ac.jp * 開発履歴 * Ver.5 敵機の種類を複数化した.+ソースコードの微修正. * Ver.4 爆発表現とタイトル画面を追加した. * Ver.3 デモンストレーションモードの実験. * Ver.2 敵機と砲弾を複数にした. * Ver.1 敵機を撃ち落とせるようにした. */ #include #include #include #include #include #define GRAVITY 0.01 // 重力加速度 #define N_TYPE 3 // 敵機の種類 #define N_ENEMY 10 // 敵機の数 #define N_BULLET 10 // 砲弾の数 #define READYINT 10 // 発射の時間間隔 #define BOOMDUR 10 // 爆発の持続時間 /* 移動物体構造体 */ typedef struct { double px, py; // 位置 double vx, vy; // 速度 double sx, sy; // サイズ(中心からの距離) int life; // 生死判定用フラグ (0:dead / 1:alive) } Object; // 物体形状構造体 typedef struct { double sx, sy; // サイズ void (*draw)(double x, double y); // 描画関数ポインタ } Shape; // 敵機構造体 typedef struct { Object obj; Shape shape; } Enemy; /* 0 以上 n 未満の乱数 */ int Rand(int n) { return ((int)(n*(rand()/(RAND_MAX + 1.0)))); } /* 物体の初期化 */ 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(Enemy *enemy, double px, double py, double vx, double vy, int life, Shape shape) { InitObject(&(enemy->obj), px, py, vx, vy, shape.sx, shape.sy, life); enemy->shape = shape; } void InitEnemies(Enemy enemy[], int n, double px, double py, double vx, double vy, int life, Shape shape[]) { int i; for (i = 0; i < n; i++) { InitEnemy(&enemy[i], px, py, vx, vy, life, shape[Rand(N_TYPE)]); } } /* 砲弾の初期化 */ 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++) { InitBullet(&bullet[i], px, py, vx, vy, 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(Enemy *enemy) { int w, h; getmaxyx(stdscr, h, w); MoveObject(&(enemy->obj)); if ((int)(enemy->obj.px) < -10) enemy->obj.life = 0; if (((int)(enemy->obj.py) < 0)||((int)(enemy->obj.py) >= h)) enemy->obj.life = 0; if (enemy->obj.life < 0) enemy->obj.life++; } /* 弾丸の表示 */ 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 DrawEnemy0(double px, double py) { int x, y; attrset(COLOR_PAIR(2)); x = (int)px - 3; y = (int)py; DrawBlocks(y-1, x, " *|| "); DrawBlocks(y , x, "<=====P"); DrawBlocks(y+1, x, " *\\\\ "); }; void DrawEnemy1(double px, double py) { int x, y; attrset(COLOR_PAIR(4)); x = (int)px - 4; y = (int)py; DrawBlocks(y-1, x, " -//| /"); DrawBlocks(y , x, "-=======/"); DrawBlocks(y+1, x, " -\\\\| "); }; void DrawEnemy2(double px, double py) { int x, y; attrset(COLOR_PAIR(6)); x = (int)px - 6; y = (int)py; DrawBlocks(y-2, x, " /#| "); DrawBlocks(y-1, x, " *|#| /|"); DrawBlocks(y , x, "<###########="); DrawBlocks(y+1, x, " *\\#\\ "); DrawBlocks(y+2, x, " \\#\\ "); }; void DrawBoom(double px, double py, int size) { int x, y; attrset(COLOR_PAIR(1)); switch (size) { case 1: case 2: case 3: x = (int)px - 1; y = (int)py; DrawBlocks(y , x, "( )"); break; case 4: case 5: case 6: x = (int)px - 3; y = (int)py; DrawBlocks(y-1, x, " &&& "); DrawBlocks(y , x, " & & "); DrawBlocks(y+1, x, " &&& "); break; default: x = (int)px - 5; y = (int)py; DrawBlocks(y-2, x, " &&&&& "); DrawBlocks(y-1, x, " &&& &&& "); DrawBlocks(y , x, "&& &&"); DrawBlocks(y+1, x, " &&& &&& "); DrawBlocks(y+2, x, " &&&&& "); break; } } /* 敵機の表示 */ void DrawEnemy(Enemy *enemy) { if (enemy->obj.life == 1) { // 機体 enemy->shape.draw(enemy->obj.px, enemy->obj.py); } else { // 爆発 DrawBoom(enemy->obj.px, enemy->obj.py, -enemy->obj.life); } } void DrawEnemies(Enemy enemy[], int n) { int i; for (i = 0; i < n; i++) { if (enemy[i].obj.life != 0) DrawEnemy(&enemy[i]); } } /* 衝突の判定 */ 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(int mode) { Object bullet[N_BULLET]; Enemy enemy[N_ENEMY]; int h, w; int key, fire, ready, angle; int i, j; char autokey[10] = " - - - k j"; Shape shape[] = { {3.0, 1.0, DrawEnemy0}, {4.0, 1.0, DrawEnemy1}, {5.0, 1.0, DrawEnemy2} }; 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, shape); 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(); if ((key == EOF) && (mode == 'd')) key = autokey[Rand(10)]; 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 (angle < 0) angle = 0; if (angle > 90) angle = 90; if (ready <= 0) { if (fire == 1) { for (i = 0; i < N_BULLET; i++) { if (bullet[i].life) continue; 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].obj.life != 0) { if (ChkHit(&bullet[i], &enemy[j].obj) != 0) { bullet[i].life = 0; enemy[j].obj.life = -BOOMDUR; // 爆発 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].obj.life != 0) { MoveEnemy(&enemy[j]); } else { InitEnemy(&enemy[j], (double)(w+Rand(10)+5), (double)Rand(h/2), -(Rand(7)+3)*0.1 , 0.0, 1, shape[Rand(N_TYPE)]); } } usleep(20000); } } /* 色の初期化 */ void InitColor(int bg) { if (bg < -1) return; start_color(); use_default_colors(); /* 文字表示用の色 */ // init_pair(0, COLOR_BLACK, bg); // 誤り assume_default_colors(COLOR_BLACK, bg); // init_pair(0, ...); 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); /* 背景の色 */ if (bg >= 0) { bkgd(COLOR_PAIR(7-bg)); } else { bkgd(COLOR_PAIR(0)); } } /* タイトル画面 */ 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-25)/2, "Push [Space] to start game"); mvaddstr(h/2+1, (w-21)/2, "Push [D] to start demo"); mvaddstr(h/2+2, (w-16)/2, "Push [Q] to quit"); attrset(COLOR_PAIR(3)); mvaddstr(h/2+4, (w-17)/2, "[Space] [↑] [↓]"); mvaddstr(h/2+5, (w-17)/2, " fire up down"); move(0, 0); timeout(-1); key = getch(); return (key); } int main() { int key; initscr(); noecho(); cbreak(); curs_set(0); keypad(stdscr, TRUE); InitColor(COLOR_BLACK); while (1) { if ((key = Title()) == 'q') break; Game(key); } endwin(); return (0); }