Nodachisoft Nodachi Sword Icon
  
@あまじ✎ 2021年3月12日に更新

第3章3 文字の出力位置を変更

イチからゲーム作りで覚えるC言語
第3章2 コンソールサイズの変更 : PREV
NEXT : 第3章4 文字の色、背景の色を変更 :

概要

Windows API を使い、コンソール画面を制御する機能を確認していきます。 今回はコンソール上で文字を出力する位置を変更する機能の使い方を確認していきます。

通常はコンソールに文字を出力すると、標準では右方向に「追記」されていくと思います。 この出力される位置は Windows API を使うことで変更できます。

画面サイズ変更

Windows 上のコンソールサイズを制御するために、 画面サイズを変更する関数、changeConsoleSize 関数を作っていきます。

 
console_pos_ex.c
#include <stdio.h>
#include <windows.h>
int main() {
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD position = { 0, 0 };
    SetConsoleCursorPosition( hStdout, position );
    printf("AAAAAAAAA\nBBBBBBBBB\nCCCCCCCCC\nDDDDDDDDD\n\n");
}

実行すると、下のようなイメージになります。

コンソールへ出力位置を調整して表示する例

コンソール画面の先頭位置(0, 0)に文字を書き出す位置が変更され、そこから 「AAAAAAAAA\nBBBBBBBBB\nCCCCCCCCC\nDDDDDDDDD\n\n」 が描画されました。

コード解説

4 行目から確認してみます。

座標を操作する対象のコンソールウィンドウへの HANDLE (ポインタ)を取得する必要があるので、 Windows API の GetStdHandle 関数を使い、現在の標準出力先のコンソールウィンドウへのハンドルを取得します。

 
console_pos_ex.c
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
COORD position = { 0, 0 };

続いて 5行目で、コンソールウィンドウの座標を扱うための構造体である、 COORD 型の宣言と初期化を行います。

今回は X 座標は 0、 Y 座標も 0 で値を設定しています。

SetConsoleCursorPosition 関数

Windows API の SetConsoleCursorPosition 関数を使用することで、 指定したコンソールへの文字の出力場所を変更することができます。

SetConsoleCursorPosition 関数は windows.h ヘッダーファイルからインクルードされるさらに先で下のように定義されています。

 
SetConsoleCursorPosition
BOOL WINAPI SetConsoleCursorPosition(
  HANDLE hConsoleOutput,
  COORD  dwCursorPosition
);
  • 第1引数の HANDLE hConsoleOutput には、操作したいコンソールウィンドウへのハンドル(ポインタ)を渡します
  • 第2引数の COORD dwCursorPosition には出力する文字の座標を渡します。

今回は、コンソールの最も左上である ( 0, 0 ) に設定したいので、 下のように、 position 辺陬を設定する位置として渡しています。

 
console_pos_ex.c
SetConsoleCursorPosition( hStdout, position );

簡単なアニメーション

文字の表示位置を変更することできると、 通常はどんどん文字が表示されて画面が下方向にスクロールしていくところを、 画面はスクロールさせずに固定し、アニメーションなどの表現を行うことが出来ます。

 
eyeanimation.c
#include <stdio.h>
#include <time.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <windows.h>

static char scrbuf[60 * (20 + 1) + 2];
static HANDLE hStdout;

void printScreen( double time ) {
	SetConsoleCursorPosition(hStdout, (COORD){ 0, 0 });
	double tx = cos((double)time / (0.5)) * 8 + 30;
	double ty = sin((double)time / (0.5)) * 8 + 10;
	double tr = 3;
	int c = 0;
	for (int y = 0; y < 20; y++) {
		for (int x = 0; x < 60; x++) {
			double ry = ((double)y - 10);
			double rx = ((double)x - 30);
			double r = sqrt(rx * rx * 0.3 + ry * ry);
			if (r < 9) {
				// 目玉黒目の内側を計算
				if (sqrt((tx-x)*(tx-x)*0.2 + (ty-y)*(ty-y)) < tr ) {
					scrbuf[c++] = '*';
				} else {
					scrbuf[c++] = ' ';
				}
			} else {
				scrbuf[c++] = '#';
			}
		}
		scrbuf[c++] = '\n';
	}
	scrbuf[c++] = '\0';
	printf(scrbuf);
	printf("あと%2.1lf秒でアニメーションを終了します。", 4.0 - time);
	fflush(stdout);
}

int main() {
	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
	clock_t start = clock();
	double timer;
	do {
		timer = (double)(clock() - start) / CLOCKS_PER_SEC;
		printScreen(timer);
		Sleep(1);
	} while (timer < 4.0);
}

実行すると下のように、まるで目玉がぐりぐり動いているようなアニメーションとなります。

コンソールプログラムでアニメーション、目玉ぐりぐり

4秒間アニメーションが続いたら自動的にプログラムは終了します。

仕組み

このプログラムは printScreen 関数を繰り返し呼び出し、 起動してからの時間(ミリ秒)に合わせて、表示する内容を上書き表示しています。

まずは main 関数からの確認です。

 
eyeanimation.c
int main() {
	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

41 行目ではコンソールの表示位置を操作する対象の標準出力先(コンソール)への handle を 取得しています。 変数 hStdout は今回はグローバル変数として確保しているので、このソースコードのどこからでも参照出来るようにしています。

 
eyeanimation.c
	clock_t start = clock();
	double timer;
	do {
		timer = (double)(clock() - start) / CLOCKS_PER_SEC;
		printScreen(timer);
		Sleep(1);
	} while (timer < 4.0);

続いて 42 行目でプログラムが始まってからの時刻をミリ秒単位で計測するため、 プログラムの開始時間を記録しています。

43 ~ 47 行目では、プログラムが始まってから 4.0 秒が経過するまでの間、 do ~ while 文で処理を繰り返しています。

一定の時間繰り返す処理では、Windows OS の CPU をこのプログラムが占有しないよう、 Sleep 関数を入れることに注意しています。

45 行目では、変数 timer に、「42行目で clock()を呼び出した時刻からの経過時間(秒)」を 計算して代入しています。

46 行目で、自作の printScreen 関数に、プログラムが開始してからの経過時刻(秒)を引数で渡して呼び出しています。

画面表示部分

画面表示は printScreen 関数の中で処理しています。

 
eyeanimation.c
void printScreen( double time_ms ) {
	SetConsoleCursorPosition(hStdout, (COORD){ 0, 0 });

10 行目で次に文字を出力する画面上の位置を (0,0) 、つまり一番左上に設定しています。 これで毎回 printScreen 内で出力する内容は左上から始まり、 前回表示した内容は上書きされることになります。

 
eyeanimation.c
	double tx = cos((double)time / (0.5)) * 8 + 30;
	double ty = sin((double)time / (0.5)) * 8 + 10;
	double tr = 3;

11~13行目では、目玉の黒目部分を表す円の中心点(tx, ty)と半径 tr を定義&計算しています。 ぐるぐる反時計回りに、黒目が画面を見渡すように移動していく表現としたかったため、 時間(変数 time)に合わせて三角関数で位置が移動していきます。

アニメーションの速度は 0.5 という定数です。この値を変えると、アニメーション速度が調整できます。

続いて、画面に表示する内容のバッファを編集するプログラム部分です。

画面バッファは 横 60 x 縦 20 マスが想定されており、 横方向成分は x、縦方向成分は y として、各マスのバッファを決定していきます。

 
eyeanimation.c
	int c = 0;
	for (int y = 0; y < 20; y++) {
		for (int x = 0; x < 60; x++) {
			double ry = ((double)y - 10);
			double rx = ((double)x - 30);
			double r = sqrt(rx * rx * 0.3 + ry * ry);
			if (r < 9) {
				// 目玉黒目の内側を計算
				if (sqrt((tx-x)*(tx-x)*0.2 + (ty-y)*(ty-y)) < tr ) {
					scrbuf[c++] = '*';
				} else {
					scrbuf[c++] = ' ';
				}
			} else {
				scrbuf[c++] = '#';
			}
		}
		scrbuf[c++] = '\n';
	}
	scrbuf[c++] = '\0';

画面バッファはchar 型の配列変数 scrbuf で定義しており、 このバッファに各マスのデータを書き込んでいき、 最後に printf 関数で画面に一度に表示します。

注意点としては、y 成分が変わるバッファの位置に、それぞれ改行コード('\n')を入れる点です。 改行コードの1バイトが、毎回 y 成分が変わる箇所に入るので、 成分 (x, y) のデータは scrbuf[ y * (60 + 1 ) + x ] で表されます。

最後に、標準出力をちゃんとここで画面に反映させることを手動で指定するため、 fflush 関数を使用しています。

 
eyeanimation.c
fflush(stdout);

これで簡単なコンソールアニメーションを実現する基本的な流れが出来ました。

参考文献

(英語)docs.microsft.com - SetConsoleCursorPosition function - https://docs.microsoft.com/en-us/windows/console/setconsolecursorposition

イチからゲーム作りで覚えるC言語
第3章2 コンソールサイズの変更 : PREV
NEXT : 第3章4 文字の色、背景の色を変更 :
 
 
送信しました!

コメント、ありがとうございます。

なんかエラーでした

ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。

Twitter:@NodachiSoft_jp
お名前:
 
連絡先:
 
メッセージ:
 
戻る
内容の確認!

以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください

お名前:
 
連絡先:
 
メッセージ:
 
Roboto からの操作ではないという確認のため確認キーを入れてください。
確認キー=95
戻る
 / 
送信確認へ
コメント欄
コメント送信確認へ

関連ありそうな記事(5件)です!

第4章3 C言語コンソール上で迷路脱出プログラム・迷路の自動生成

#C11仕様#C言語#ゲームプログラミング✎ 2021-05-15
C言語のコンソールゲームで迷路脱出ゲームプログラムの作り方を確認します。迷路は自動生成されます
広告領域
追従 広告領域
目次
第3章3 文字の出力位置を変更
第3章3 文字の出力位置を変更
概要
概要
画面サイズ変更
画面サイズ変更
コード解説
コード解説
SetConsoleCursorPosition 関数
SetConsoleCursorPosition 関数
簡単なアニメーション
簡単なアニメーション
仕組み
仕組み
画面表示部分
画面表示部分
参考文献
参考文献
Nodachisoft © 2021