コンソールアプリでの裏画面処理

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら

コンソールアプリでの裏画面処理

#1

投稿記事 by » 16年前

現在コンソール上(コマンドプロンプト上?)で動くテトリスを作成しようと思っているのですが、
まず手始めに適当に描画をしてみようと思い無限ループのなかで、

clrscr()で画面消去

描画

という流れでテストしてみたのですが、画面がチカチカとちらつきます。
こちらのゲームプログラミングの館をすでに拝見済みでしたのでとりあえず裏画面処理を
しないといけないのかなと思い、少し調べてみたのですが、どうにもWin32APIなどを学ば
ないといけないような、そういった解説が多かった気がします。
APIの事自体をあまりよく知らないので思い違いかもしれませんが。
そもそも裏画面処理というものも漠然とした理解しかないのですが。。。

以上のことから質問したいことは、コンソールアプリでの連続した描画を行うにはAPIの勉強をしなければ難しいでしょうか? ということです。
何かアドバイス頂ければ幸いです。

コンパイラ:BorlandC++ Compiler
開発環境:BCC Developer
OS:Windows XP

Re:コンソールアプリでの裏画面処理

#2

投稿記事 by » 16年前

一応以下がテストに使用したソースです。
BorlandC++ Compiler以外(私が確認した限りではVisualC++2005)ではclrscr()関数が
使用できないみたいですね……。
※一応VisualC++で動くものを添付しておきました。多分動くと思います...
※使用している画面を消去する関数の内容は全く理解していません。
#include <stdio.h>
#include <conio.h>
#include <windows.h>

#define _MAIN_
#define SQUARE "■"
#define SPACE " "

void fall();

int main(void){

	fall();

	return(0);
}


void fall(){
	int a=0;
	
	typedef struct{
		int flag;
	}BLOCK;
	BLOCK block[20][8];
	
	memset( &block, 0, sizeof(block) );
	block[a][4].flag=1;
	
	while(1){
		clrscr();
		char key=0;
			if( kbhit() )
				key = getch();
			if(key =='r')
				a++;
				
		block[a-1][4].flag=0;
		block[a][4].flag=1;	
			
		for(int i=0; i<20; i++){
			for(int j=0; j<8; j++){
				if(block[j].flag==0)
					printf("0");
				else if(block[j].flag==1)
					printf("%s", SQUARE);
			}
			printf("\n");
		}
		Sleep(20);						
	}
}

バグ

Re:コンソールアプリでの裏画面処理

#3

投稿記事 by バグ » 16年前

コンソールアプリの場合、裏画面という概念は無いと思った方がいいですね。
しかし、全て消して、書き直していたのではどうしても画面がちらつきます。
ですから、必要な部分だけを書き換えるようにするとかなりマシになります。

自作なので、バグがあるかもしれませんが…、添付したファイルにコンソールアプリを作成する際に便利な機能をクラス化したものが入っています。

このクラスのメンバ関数であるSetCursorPostion関数で、カーソルを任意の位置に移動できますので、そこでprintfやstd::coutで描画してください。

詳しい仕組みが知りたい場合は、またご質問ください(^-^)

バグ

Re:コンソールアプリでの裏画面処理

#4

投稿記事 by バグ » 16年前

前記のConsoleクラスの使用例のサンプルソースです。
全てを書き換えてはいますが、同じ位置に描画しているので、思ったよりはチラつかないのではないでしょうか?

Re:コンソールアプリでの裏画面処理

#5

投稿記事 by » 16年前

バグさん、アドバイスありがとうございます。
さっそく頂いたサンプルソースを動かしてみましたが、、、おお、これはすごい。
「思ったよりはチラつかない」という記述があったので、少しはチラつくのかな……と思っていましたが全く問題ないです。
上に掲載したテスト用のソースで再度テストしてみても問題ありませんでした。
これで描画の問題は完璧に解決できそうです。ありがとうございます。
バグさんのConsole.hとConsole.cppはリバーシを作る時に色の指定部分だけ使用させてもらっていたのですが、
まさかこんな事も出来るとは思いませんでした。
事前にこのような形になっていなければ、とても今の自分の力では不可能でした^^;

とりあえず描画の問題は解決したのですが、いくつか質問したいことがあります。
お時間のある時にでも教えて頂ければ幸いです。

1.SetCursorPostionの動作について
このSetCursorPostionについてなのですが、この関数は描画する際、一度カーソル位置にある文字列なりなんなりを消去してから描画、
という動作をしているのでしょうか?
例えるなら
バックスペースで削除→入力(描画)
といった感じでしょうか。

2.局所的な色の変更方法
SetForegroundColor関数についてなのですが、今の所ある一部分だけ色を変えたいという場合には
console.SetForegroundColor(Red);
printf("■");
console.SetForegroundColor(Gray);
という感じで一度色を変更した後に再度デフォルトの色(この場合はグレイ)に戻しているのですが、
この方法が一般的でしょうか?
もっと効率的というか、わざわざデフォルトの色に戻す記述を入れない方法などあるかなと思いまして。

3.GetForegroundColor()について
この関数は、名前から察するに現在設定されている色の情報を取得する関数のような気がするのですが、
それであっているでしょうか?
確認してみようとは思ったのですが、引数の型がConsoleColor型(?)
だったりとちょっと理解できませんでした。
ビット演算(?)をしているような気もしますし、もしAPI絡みのお話などが出てくるようであれば、
とりあえずこれは色を取得する関数なのかどうか、という部分だけお答え頂ければと思います。

4.ConsoleColorについて
あらかじめDarkBlueやRedといった具合に定義されていると思いますが、
これに新たな色を追加することは出来るのでしょうか?
例えばピンクや紫といった感じです。
テトリスを作るだけなら今ある色で十分なのですが、少し気になったもので。。。

5.SetWindowSize()について
これは恐らく名前の通り、windowサイズを任意を大きさにしてくれるものなのかなと思い、
x,yがウインドウの左上の座標でwidth,heightでウインドウの大きさを指定するものだという仮定のもと動かしてみましたが、
特に何も変わりませんでした。
大雑把な質問で申し訳ないのですが、これはどういった事をする関数なのでしょうか?

6.SetBackgroundColor()について
いい加減多くなってきたのでこの辺りで最後に。。。
このSetBackgroundColorなのですが、コンソール画面上全体は指定した色で塗りつぶすことは出来ないでしょうか?
一応適当にスペースなどを画面いっぱいに敷き詰めれば可能なのかもしれませんが、
もっと効率的な方法などはあるでしょうか?

以上です。
テトリスを作るうえでは描画の問題が解決したので、SetBackgroundColor()等などについては知らなくても問題はないのですが、
色々試しているうちに疑問が出てきてしまいまして^^;
質問事項が6つと多くなってしまいましたが、ちょちょいと簡単にでもいいのでお答え頂ければありがたいです。

バグ

Re:コンソールアプリでの裏画面処理

#6

投稿記事 by バグ » 16年前

>>1.SetCursorPostionの動作について
>>このSetCursorPostionについてなのですが、この関数は描画する際、一度カーソル位置にある文字列なりなんなりを消去してから描画、 という動作をしているのでしょうか?
例えるならバックスペースで削除→入力(描画)といった感じでしょうか。

printf関数やstd::coutなどの内部処理的にはそうなのかもしれませんが、SetCursorPosition関数自体は、カーソル(文字入力待ち位置で点滅していますよね?)の位置を移動させているだけです。描画までは行っておりません。

>>2.局所的な色の変更方法
>>SetForegroundColor関数についてなのですが、今の所ある一部分だけ色を変えたいという場合には
>>console.SetForegroundColor(Red);
>>printf("■");
>>console.SetForegroundColor(Gray);
>>という感じで一度色を変更した後に再度デフォルトの色(この場合はグレイ)に戻しているのですが、
>>この方法が一般的でしょうか?
>>もっと効率的というか、わざわざデフォルトの色に戻す記述を入れない方法などあるかなと思いまして。

今回添付したバージョンに、新たにColorStringという関数を追加しましたので、そちらを利用ください。

>>3.GetForegroundColor()について
>>この関数は、名前から察するに現在設定されている色の情報を取得する関数のような気がするのですが、
>>それであっているでしょうか?
>>確認してみようとは思ったのですが、引数の型がConsoleColor型(?)
>>だったりとちょっと理解できませんでした。
>>ビット演算(?)をしているような気もしますし、もしAPI絡みのお話などが出てくるようであれば、とりあえずこれは色を取得する関数なのかどうか、という部分だけお答え頂ければと思います。

そうですね。現在設定されている文字の色を取得する関数です。
ConsoleColor型は私が用意したオリジナルの列挙型です。今回添付したファイルの中にreadme.txtというファイルがありますので、そのファイルの一番最後にConsoleColor列挙型について書いておきましたので、そちらを御覧下さい。
ビット演算を使用しているのは、前景色と後景色の両方の情報が1つの変数に格納されているので、それを分離させる為です。

>>4.ConsoleColorについて
>>あらかじめDarkBlueやRedといった具合に定義されていると思いますが、これに新たな色を追加することは出来るのでしょうか?
>>例えばピンクや紫といった感じです。
>>テトリスを作るだけなら今ある色で十分なのですが、少し気になったもので。。。

現状のWindowsの仕様ではコンソールアプリで使用できる色は16色だけです。
その内容も変更できませんので、今よりもグラフィカルなアプリケーション開発を行いたいのであれば、コンソールアプリでは無理です(^_^;)
あ、紫はありますよ。Magentaがそうですね。

>>5.SetWindowSize()について
>>これは恐らく名前の通り、windowサイズを任意を大きさにしてくれるものなのかなと思い、
>>x,yがウインドウの左上の座標でwidth,heightでウインドウの大きさを指定するものだという仮定のもと動かしてみましたが、特に何も変わりませんでした。
>>大雑把な質問で申し訳ないのですが、これはどういった事をする関数なのでしょうか?

その認識であっています。Windowの大きさを変更する関数です。
大きさが変わらなかったのは、恐らく変更不可能なサイズ(大き過ぎor小さ過ぎ)を指定されたのではないかと思います。指定できるサイズは動作環境によって変わりますので、戻り値で関数の成否を確認してください。
この関数に限らず、全ての関数の戻り値はBOOL型になっており、TRUEが返った場合は成功、FALSEが返った場合は失敗となっています。

あ、それから、引数の扱いがちょっと分かりづらかったので今回の新バージョンでは、引数をWidthとHeightだけにしました。

>>6.SetBackgroundColor()について
>>いい加減多くなってきたのでこの辺りで最後に。。。
>>このSetBackgroundColorなのですが、コンソール画面上全体は指定した色で塗りつぶすことは出来ないでしょうか?
>>一応適当にスペースなどを画面いっぱいに敷き詰めれば可能なのかもしれませんが、もっと効率的な方法などはあるでしょうか?

APIのコンソールに関する関数を調べていたら、いいのがありましたので今回の新バージョンで追加してみました。FillScreenという関数です。指定した色で画面を塗りつぶします。スペースを敷き詰めるよりは早いんじゃないかと思われます。

lbfuvab

Re:コンソールアプリでの裏画面処理

#7

投稿記事 by lbfuvab » 16年前

全体の色を変えるなら
system("color 0e");
とかでも良いんじゃないですか?

バグ

Re:コンソールアプリでの裏画面処理

#8

投稿記事 by バグ » 16年前

それでもいいんですが、systemコマンドだと前景と背景を同色に設定できないようなので、そこだけ少し不便ですね。

Re:コンソールアプリでの裏画面処理

#9

投稿記事 by » 16年前

>バグさん
新しく追加されたColorStringですが、表示する文字列の指定も一緒に行うため、
一行で済むのでとても分かりやすくすっきりしますね。大変ありがたいです。
色についてですが、コンソールアプリではそのような仕様があるのですゥ。
でもコンソールアプリを作るうえで、あまり贅沢な事はしないと思うので16色もあれば十分かもしれませんね^^;
SetWindowSize()についてですが、小さい値にするとちゃんと変更出来ました。
普通に640×480などを指定していました。
縦方向には割と画面いっぱいまで表示してくれるようですが、
横方向はデフォルトの大きさくらいが限界のようですね。
今回の事でBOOL型のありがたさを知りました……クラスを自作(?)したりする時にはBOOL型で返却値を確認出来るようにすると便利なのですね。
返却値の判定が間違っていたらどうしようもないのかもしれませんが。。。
どうにかこれでテトリスを作る事は出来そうです。
色々と教えて頂き、本当にありがとうございます。
コンソールアプリでもいろんな事が出来そうだと、
私のなかでのコンソールアプリに対する可能性が広がった気がします(^_^;)

>lbfuvabさん
なるほど、このような方法もあるのですね。
systemコマンドはsystem("cls")しか知らなかったので、どう使えばいいんだろう……と適当にいじっていたら、system("color #")と入力して実行すると使い方のような物が表示されたのでビックリしましたw
systemコマンドは若干呼び出し、実行共に遅いようなのでその点だけが難点だと思っていましたが、最初に一回呼び出すだけなら全然問題ないですね。
色々と使えそうなものがないか調べてみたいと思います。
アドバイスありがとうございます。

バグ

Re:コンソールアプリでの裏画面処理

#10

投稿記事 by バグ » 16年前

>>SetWindowSize()についてですが、小さい値にするとちゃんと変更出来ました。
>>普通に640×480などを指定していました。

ああ、なるほど、ピクセルでの指定と勘違いされていたんですね。
コンソールアプリの場合は、ピクセル単位ではなく、キャラクター(半角文字)単位で指定する事がほとんどです。

ですので、SetWindowSize関数の引数であるwidthとheightも描画できる文字数だと思ってください。
ちなみにSetCursorPosition関数も同じです(^-^)

>>縦方向には割と画面いっぱいまで表示してくれるようですが、横方向はデフォルトの大きさくらいが限界のようですね。

これはですね、実はSetBufferSize関数で大きくしてやる事が出来ます。SetBufferSize関数で最大幅を拡大して、そのあとでSetWindowSize関数でウインドウ自体の大きさを変更してやってください。

ちなみにウチの環境(解像度1280×1024)だと、width = 159、height = 53に設定してやると、フル画面(?)のコンソールウインドウになりました(笑)

この関数の使用上の注意としては、現在設定されているウインドウの大きさ(GetWindowSize関数で確認できます)以下の数値は設定できないということだけ覚えておいてください。(※つまり、小さくしたい場合はあらかじめSetWindowSize関数で変更したい大きさにウインドウを小さくしておいてから、SetBufferSize関数で設定するということになります。)

Re:コンソールアプリでの裏画面処理

#11

投稿記事 by » 16年前

SetBufferSize関数での最大幅の拡大を試してみました。
うちの環境は1280×800ですが、最大サイズはwidth160,height41でした。
フル画面にしたのに相変わらず左端の方でこじんまりと描画していたので、
せっかくなので思いっきりど真ん中で描画したりしてみましたw
これだけ大きく表示出来れば、すごろくや人生ゲームといったボードゲーム、
神経衰弱なんかも作りやすそうですね。

閉鎖

“C言語何でも質問掲示板” へ戻る