今回の授業内容は,前回の続きです. 開発中のエデュテイメントアプリの完成度を高めよう. (もし,前回の作業が完了していない場合, まずは大至急,追いつくこと. pdcl は ver.2,pendraw は ver.4.)
作業用のディレクトリとファイルを用意し, 開発中のアプリの現在の状態を確認しておこう:
$ mkdir c-1106 # 今回のディレクトリを作成 $ cp c-1103/*.c c-1106/ # 前回のディレクトリc-1103 からソースファイルをコピー $ cd c-1106 # 今回のディレクトリで作業 $ ls # ソースファイルを確認 pdcl.c pendraw.c $ cc pdcl.c -o pdcl # コンパイル $ cc pendraw.c -lncurses -o pendraw $ vim script.txt # 動作テスト用PDCLスクリプトの作成... up 10 repeat 10 off up 3 left 5 on down 5 right 3 end $ ./pdcl script.txt # バックエンドプログラムの動作確認 kkk...lllq # キーシーケンス $ ./pendraw # フロントエンドプログラムの動作確認 ... # [P]キーでスクリプトを実行
バックエンドで生成されたキーシーケンスによって フロントエンドのペンが自動操作される.
まずは,フロントエンドプログラム pendraw について, ペンが off状態の場合にも画面上にカーソルを表示し, ペンの位置を把握できるようにしてみよう.
現状(前回のバージョン)の pendraw.c では, ペンの軌跡を描画する間は erase() を封印し, 画面に表示したものがすべて残るようにしていた. しかし,erase() しないままでは, off状態のカーソルの軌跡も残ってしまうことになる.
そこで,画像を画面に記録するのではなく, 2次元配列に記録しておき, その画像とカーソルとを画面に再表示し続けるように, プログラムの構成を変更しよう.
ソース pendraw.c:(ver.5)
...
#include <stdlib.h> // malloc()
/*
画像全体に文字を記入する.
img:画像
w, h:画面サイズ
c:文字
*/
void FillImg(char *img, int w, int h, char c)
{
int x, y;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
img[w*y + x] = c;
}
}
}
/*
画像を端末画面に表示する.
img:画像
w, h:画面サイズ
*/
void ShowImg(char *img, int w, int h)
{
int x, y;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
mvaddch(y, x, img[w*y + x]);
}
}
}
/*
ペンを端末画面に表示する.
pen:ペンの状態
x, y:ペンの位置
*/
void ShowPen(int pen, int x, int y)
{
char c;
switch (pen) {
case 0: c = 'O'; break; // off状態のカーソル
case 1: c = '#'; break; // on状態のカーソル
}
mvaddch(y, x, c);
}
/*
ペンの軌跡を画像に書き込む.
img:画像
w:画面サイズ
pen:ペンの状態
x, y:ペンの位置
*/
void Draw(char *img, int w, int pen, int x, int y)
{
if (pen) img[w*y + x] = '#';
}
...
/*
ゲーム本体
fin:操作入力用ファイルポインタ
fout:操作記録用ファイルポインタ
return:終了状態(0:正常,1:異常)
*/
int Game(FILE *fin, FILE *fout) //! 型
{
...
int key;
char *img; // 軌跡画像(動的配列)へのポインタ
// 初期設定
erase();
getmaxyx(stdscr, h, w);
pen = 0; x = w/2; y = h/2;
img = (char *)malloc(sizeof(char)*w*h); // 軌跡画像のメモリを確保
if (img == NULL) goto ERROR; // エラー処理
FillImg(img, w, h, ' '); // 画像を初期化
LOOP: // come from 終了取消
while (1) {
// 描画
Draw(img, w, pen, x, y); // 軌跡を画像に記入
erase(); // 画面をクリア
ShowImg(img, w, h); // 軌跡画像を表示
ShowPen(pen, x, y); // ペン(カーソル)を表示
refresh(); // 画面を表示
...
if (key == 'q') break;
}
if (Info("Done? (y/n)", w, h) != 'y') goto LOOP;
// 終了確認・取消
free(img); // 軌跡画像のメモリを解放
return (0); // 正常終了
ERROR: // come from malloc()
Info("Error... Hit any key!", w, h); // エラー確認
return (1); // 異常終了
}
...
では,pendraw でPDCLスクリプトを再度実行してみよう. ペンが off状態でも,アニメーション表示される.
また,手動操作モードでは, 終了の取り消し(描画の続行)も可能となった.
ファイルやスクリプトによる自動操作モードの途中でも [Q]キーの手動入力によってアニメーションを中止できるようにする.
pendraw では,[!]キーで軌跡をクリアしペンを画面中央に戻す.
pdcl では,resetコマンドで '!' を出力する.
反復回数がゼロ以下(repeat 0 や repeat -1)の場合, そのループ内(その repeat と対になる end まで) のコマンドを無視する.
書式:exec スクリプトファイル名
書式:randdir 反復回数
'h','j','k','l' の内どれか1つのキー文字を連続出力する.
例:randdir 5 ⇨ kkkkk
pendraw では,数字キー [0]〜[8] の入力で色番号を指定. 軌跡画像には文字 '#' ではなく,色番号数字を記録し, その色で文字 '#' を表示する.
pdcl では,数字キーを出力する colorコマンドを追加.
書式:color 色番号
提出: