/* * 迷路系ゲームの超基本サンプル Ver.1 * キャラクタを1文字ではなく AA(ASCIIアート)で表示. */ #include #include #include #include #include "aa.h" #include "mobj.h" #include "term.h" // マップの基準表示位置 #define MAP_X 5 #define MAP_Y 5 // キャラタの AAのサイズ #define CHAR_W 3 #define CHAR_H 2 // 縮尺関連の定数 #define SCALE 100 // 縮尺 // セルの位置×SCALE = キャラの位置 #define V_HERO 25 // 自キャラの速度 #define V_ENEMY 20 // 敵キャラの速度(未使用) // キャラが距離SCALEだけ移動したら,マップ上で1移動 #define D_HIT 50 // 衝突判定の距離の閾値(しきい値;threshold) /* * 各キャラを画面上で1文字ではなく,サイズCHAR_W×CHAR_H文字の ASCIIアートとして表示する. * さらに,各キャラのスムーズな移動を表現するために,縮尺SCALE を導入している. * マップ上のサイズ1×1=画面上のサイズCHAR_W×CHAR_H=仮想世界上のサイズSCALE×SCALE */ // AA生成処理の簡単化のためのマクロ #define AAVAR Aa *aa; int e = 0; int i = 0; // 変数を定義 #define AAERR if (e) { FreeAa(aa); return (NULL); } // エラー処理 #define AANEW(w, h) AAVAR; if ((aa = NewAa(w, h)) == NULL) return (NULL); // AAを生成 #define AAROW(s) if (SetAaRow(aa, i, s) != 0) e++; i++; // 行文字列を設定 #define AAEND AAERR; return (aa); // 終了 // 敵キャラのAAを生成 Aa *NewEnemyAa(void) { AANEW(CHAR_W, CHAR_H); AAROW("`o'"); AAROW("~~~"); AAEND; } // 自キャラのAAを生成 Aa *NewHeroAa(void) { AANEW(CHAR_W, CHAR_H); AAROW("@o@"); AAROW("-^-"); AAEND; } // 壁のAAを生成 Aa *NewWallAa(void) { AANEW(CHAR_W, CHAR_H); AAROW("###"); AAROW("###"); AAEND; } // マップを生成 Aa *NewMap(void) { AANEW(11, 9); AAROW("###########"); AAROW("# E #"); // E は敵キャラ AAROW("# ### ### #"); AAROW("# # # #"); AAROW("## # # # ##"); AAROW("# # #"); AAROW("# # ### # #"); AAROW("# M #"); // M は自キャラ AAROW("###########"); AAEND; } /* * マップデータについては,この例のようにソースファイル内に * ハードコーディング(埋め込み)するよりも, * 次のような内容のデータファイルから入力する方がいろいろと捗るだろう. * * 11 9 * ########### * # # * ... * * 関数 Aa *LoadAa(char *file) とかを aa.c に追加しては? */ // キャラクタの初期位置をマップから取得・設定 int FindChar(Aa *map, Mobj *o, char sym) { char *p; int x, y; p = QueryAaCell(map, sym); // マップからキャラを検索 if (p == NULL) return (1); // 失敗 *p = ' '; // キャラのいたセルをクリア y = (p - map->cell)/(map->sx) * SCALE; x = (p - map->cell)%(map->sx) * SCALE; InitMobj(o, x, y, 0, 0, 1); return (0); // 成功 } // 位置(x0, y0) を基準としてキャラクタを表示 void MvDrawChar(int x0, int y0, Mobj *o, char shape) { mvaddch(y0 + o->py, x0 + o->px, shape); } // マップを AA表示 void DrawMapAa(Aa *map, Aa *a_wall) { int x, y; attron(COLOR_PAIR(4)); for (y = 0; y < map->sy; y++) { for (x = 0; x < map->sx; x++) { if (GetAaCell(map, x, y) != '#') continue; MvDrawAa(MAP_X + x*CHAR_W, MAP_Y + y*CHAR_H, a_wall); } } } // マップ上にキャラクタを AA表示 void MvDrawCharAa(int x0, int y0, Mobj *o, Aa *a) { MvDrawAa(MAP_X + o->px*CHAR_W/SCALE, MAP_Y + o->py*CHAR_H/SCALE, a); } // 自キャラを AA表示 void DrawHeroAa(Mobj *o, Aa *a) { attron(COLOR_PAIR(3)); MvDrawCharAa(MAP_X, MAP_Y, o, a); } // 敵キャラを AA表示 void DrawEnemyAa(Mobj *o, Aa *a) { attron(COLOR_PAIR(1)); MvDrawCharAa(MAP_X, MAP_Y, o, a); } // タイトル画面 int Title(void) { Aa *a_hero; Aa *a_enemy; int h, w; if ((a_hero = NewHeroAa()) == NULL) return (ERR); if ((a_enemy = NewEnemyAa()) == NULL) return (ERR); erase(); getmaxyx(stdscr, h, w); attron(COLOR_PAIR(6)); mvaddstr(h/2-4, (w-28)/2, "迷路系ゲームのサンプル Ver.1"); attron(COLOR_PAIR(2)); mvaddstr(h/2-2, (w-18)/2, "カーソルキーで移動"); mvaddstr(h/2-1, (w-13)/2, "[Q]キーで終了"); attron(COLOR_PAIR(1)); mvaddstr(h/2+1, (w-13)/2, "Hit Any Key!!"); attron(COLOR_PAIR(7)); mvaddstr(h/2+3, (w-27)/2, "ASCIIアート表示を始めました"); attron(COLOR_PAIR(3)); MvDrawAa(w/2-4-CHAR_W, h/2+4, a_hero); attron(COLOR_PAIR(1)); MvDrawAa(w/2+4, h/2+4, a_enemy); refresh(); FreeAa(a_hero); FreeAa(a_enemy); timeout(-1); return (getch()); } // ゲームオーバ画面 void GameOver(void) { int w, h; // erase(); // 新規の画面に表示 or 現在の画面にオーバーレイ getmaxyx(stdscr, h, w); attron(COLOR_PAIR(1)); CenterStr(h, w, -2, "#===============#"); CenterStr(h, w, -1, "# GAME OVER #"); CenterStr(h, w, 0, "# #"); CenterStr(h, w, +1, "# Press [Space] #"); CenterStr(h, w, +2, "#===============#"); refresh(); while (WaitKey(-1) != ' '); // [Space]キーで終了 } // キャラクタの移動予定先のセルの座標 // px, vx:キャラの座標・速度 int NextCellPos(int px, int vx) { int x; // セルの座標 if (vx < 0) x = (px + vx)/SCALE; else if (vx > 0) x = px/SCALE + 1; else x = px/SCALE; return (x); } // マップ上でキャラクタを実際に移動 void MoveChar(Aa *map, Mobj *o) { int x, y; // セルの座標 // 移動予定先のセルが... x = NextCellPos(o->px, o->vx); y = NextCellPos(o->py, o->vy); if (GetAaCell(map, x, y) != '#') { MoveMobj(o); // 通路なら実際に移動 } else { SetMobjVel(o, 0, 0); // 壁なら停止 } } // マップ上でキャラクタの移動方向の変更を要求 void ReqTurnChar(Aa *map, Mobj *o, int vx, int vy) { int x, y; // セルの座標 // 壁にメリ込まないように... if ((vx != 0) && ((o->py)%SCALE != 0)) return; // x方向へ曲がるには,y座標が丁度よく SCALEの倍数でないとダメ if ((vy != 0) && ((o->px)%SCALE != 0)) return; // y方向へ曲がるには,x座標が丁度よく SCALEの倍数でないとダメ // 移動予定先のセルが... x = NextCellPos(o->px, vx); y = NextCellPos(o->py, vy); if (GetAaCell(map, x, y) != '#') { // 通路なら... SetMobjVel(o, vx, vy); // 移動方向を変更(まだ移動しない) } } // 自キャラを操作 void CtrlHero(Aa *map, Mobj *o, int key) { switch (key) { case 'l': case KEY_RIGHT: ReqTurnChar(map, o, +V_HERO, 0); break; case 'j': case KEY_DOWN: ReqTurnChar(map, o, 0, +V_HERO); break; case 'h': case KEY_LEFT: ReqTurnChar(map, o, -V_HERO, 0); break; case 'k': case KEY_UP: ReqTurnChar(map, o, 0, -V_HERO); break; // default: SetMobjVel(o, 0, 0); break; // 無入力なら停止 default: break; // 無入力なら慣性運動 } } // キャラクタ間の衝突判定 int ChkHit(Mobj *o1, Mobj *o2) { if (fabs(o1->px - o2->px) > D_HIT) return (0); // 衝突なし if (fabs(o1->py - o2->py) > D_HIT) return (0); // 衝突なし return (1); // 衝突あり } // ゲームの本体 int Game(void) { Aa *map; // マップ Mobj *hero; // 自キャラ Mobj *enemy; // 敵キャラ Aa *a_wall; // 壁のAA Aa *a_hero; // 自キャラのAA Aa *a_enemy; // 敵キャラのAA int key; // 入力キー // 初期設定 if ((map = NewMap()) == NULL) return (1); if ((a_wall = NewWallAa()) == NULL) return (1); if ((hero = NewMobj()) == NULL) return (1); if (FindChar(map, hero, 'M')) return (1); if ((a_hero = NewHeroAa()) == NULL) return (1); if ((enemy = NewMobj()) == NULL) return (1); if (FindChar(map, enemy, 'E')) return (1); if ((a_enemy = NewEnemyAa()) == NULL) return (1); while (1) { // 画面表示 erase(); DrawMapAa(map, a_wall); DrawHeroAa(hero, a_hero); DrawEnemyAa(enemy, a_enemy); move(0, 0); #ifdef DEBUG attron(COLOR_PAIR(7)); printw("hero: %d %d : %d %d : %d\n", hero->px, hero->py, hero->vx, hero->vy, hero->stts); printw("enemy: %d %d\n", enemy->px, enemy->py); #endif refresh(); // 終了判定 if (hero->stts == 0) { GameOver(); break; } // キー入力/自キャラの移動 key = WaitKey(100); // 25ms待機&キー入力 if (key == 'q') break; CtrlHero(map, hero, key); MoveChar(map, hero); // 敵キャラの移動 ... to-do // CtrlEnemy(map, enemy, hero); // 移動方向を決める(自キャラを追う等) // MoveChar(map, enemy); // キャラクタ間の相互作用 if (ChkHit(hero, enemy)) hero->stts = 0; // とりあえず,捕まったら終了へ } FreeAa(a_enemy); FreeAa(a_hero); FreeAa(a_wall); FreeMobj(enemy); FreeMobj(hero); FreeAa(map); return (0); } int main(void) { // 端末の設定 setlocale(LC_ALL, ""); initscr(); curs_set(0); noecho(); cbreak(); keypad(stdscr, TRUE); // カラーの設定 start_color(); init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_YELLOW, COLOR_BLACK); init_pair(4, COLOR_BLUE, COLOR_BLACK); init_pair(5, COLOR_MAGENTA, COLOR_BLACK); init_pair(6, COLOR_CYAN, COLOR_BLACK); init_pair(7, COLOR_WHITE, COLOR_BLACK); bkgd(COLOR_PAIR(7)); attron(A_BOLD); // 太字を使う // メイン while (1) { if (Title() == 'q') break; // タイトル画面 if (Game() != 0) break; // ゲーム画面 } endwin(); return (0); }