ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

C言語とC++言語コミュの壁マリオ

  • mixiチェック
  • このエントリーをはてなブックマークに追加
ちょっとしたアクションゲームを作ろうと思うんですが、ちらつきを消すことはできないでしょうか?
ソースをさらします。
お願いします。

#include <windows.h>
#include <stdio.h>
#include <conio.h>

int locate( short x , short y )
{
COORD cd = {x*2,y} ;
return SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ) , cd ) ;
}

int draw();

int map[11][10] ={
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,4,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0,0,1},
{1,0,0,0,0,0,2,2,0,1},
{1,0,0,0,2,0,2,2,3,1},
{1,1,1,1,1,1,1,1,1,1}
};

int x = 1, y = 8, jump = 0, coin = 0;
long time = 0;

int main(){
while(true){
time += 2;
if(_kbhit()){
map[y][x] = 0;
switch(_getch()){
case 77:
if(map[y][x+1]!=1){x ++;}
break;
case 75:
if(map[y][x-1]!=1){x --;}
break;
case 72:
jump = 3;
break;
}
}

if(time % 3 == 0){
map[y][x] = 0;

//重力
if(map[y+1][x]!=1 && jump == 0){y++;}
//ジャンプ
if(jump != 0){
if(map[y-1][x]!=1){
y--;
jump--;
}else{jump = 0;}
}
}

switch(map[y][x]){
case 2:
printf("game over!");
_getch();
return 0;
case 3:
coin ++;
break;
case 4:
printf("clear!");
_getch();
return 0;
default:
break;
}

map[y][x] = 9;
draw();
}

_getch();
}

int draw(){
for(int i=0;i<10;i++){
for(int j=0;j<11;j++){
locate(i,j);
switch(map[j][i]){
case 1:
printf("■");
break;
case 2:
printf("*");
break;
case 3:
printf("◎");
break;
case 4:
printf("Д");
break;
case 9:
printf("★");
break;
default:
printf(" ");
break;
}
}
printf("\n");
}
locate(13,6);printf("coin = %d", coin);
return 0;
}

コメント(34)

スペースがなくなって激しく見にくいです・・・orz
お願いします
確たる根拠と言うか知識があるわけではないのですが・・・
結論から言うと、putcharなどで書いている限りちらつきを消すのは不可能だと思いますよ。
ていうか、かなり難しいというかトリッキーなことをしないといけないというか。
putcharは文字を出力しているだけ。これを画面に描画しているのは、Windowsだとcmd.exeになりますかね。
cmd.exeは画面のちらつきを考慮せずにきた文字は片端から画面に描画してしまう・・・とおもいます。
解決になるか分かりませんが、二次元配列は[0][0],[0][1]の順でメモリに配置されるので一番関数draw()の描画処理のような場合、右のインデックスからインクリメントしていった方がメモリアクセス速度が向上し、結果速度が早くなるそうです。
アクションゲームならば、GDIかDirectXを使って作った方がいいような気がしますが…。
一般的にちらつきの原因はメインループにある事が多いですb
ちらつきの原因は
描画し終わる前に表示されてることが原因かと
GUIなどでは別バッファに書き込んで描画し終わった段階で
前面に表示します。これをダブルバッファリングといいます
コンソールではダブルバッファリングできないから
厳しいのでは・・・
無理やりやるとしたら別バッファにputcharして全部書き終わったタイミングで
バッファをswapさせるみたいになりそうです

でもそれをやる労力を考えるのならOpenGLないしはWin32API、DirectXなどの
グラフィックAPIを使ったほうがいいと思います

とりあえず改定してみました。

#include <windows.h>
#include <stdio.h>
#include <conio.h>

int locate( short x , short y )
{
COORD cd = {x*2,y} ;
return SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ) , cd ) ;
}

void draw(), judge(), input(), move();

int map[11][10] ={
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,4,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0,0,1},
{1,0,0,0,2,0,0,2,0,1},
{1,0,0,0,2,0,2,2,3,1},
{1,1,1,1,1,1,1,1,1,1}
};

int x = 1, y = 8, jump = 0, coin = 0, end = 0;
long time = 0;

int main(){
while(true){
time += 2;
if(_kbhit()){
map[y][x] = 0;

input();

judge();

map[y][x] = 9;
draw();
}

if(time % 3 == 0){
map[y][x] = 0;

move();
}
if(end == 1){return 0;}
}
}

void draw(){
for(int i=0;i<10;i++){
for(int j=0;j<11;j++){
locate(i,j);
switch(map[j][i]){
case 1:
printf("■");
break;
case 2:
printf("*");
break;
case 3:
printf("◎");
break;
case 4:
printf("Д");
break;
case 9:
printf("★");
break;
default:
printf(" ");
break;
}
}
printf("\n");
}
locate(13,6);printf("coin = %d", coin);
}

void judge(){
switch(map[y][x]){
case 2:
printf("game over!");
_getch();
end = 1;
break;
case 3:
coin ++;
break;
case 4:
printf("clear!");
_getch();
end = 1;
break;
}
}

void input(){
switch(_getch()){
case 77:
if(map[y][x+1]!=1){x ++;}
break;
case 75:
if(map[y][x-1]!=1){x --;}
break;
case 72:
jump = 3;
break;
}
}

void move(){
//重力
if(map[y+1][x]!=1 && jump == 0){
y++;

judge();

map[y][x] = 9;
draw();
}
//ジャンプ
if(jump != 0){
if(map[y-1][x]!=1){
y--;
jump--;
}else{jump = 0;}

judge();

map[y][x] = 9;
draw();
}
}

キャラが動くときだけ描画します。

>>4さんの意見が簡単でよさげなのでそちらでもやってみようと思います。
#include <windows.h>
#include <stdio.h>
#include <conio.h>

int locate( short x , short y )
{
COORD cd = {x*2,y} ;
return SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ) , cd ) ;
}

void draw(), judge(), input(), move();

int map[11][10] ={
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,3,0,0,1,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0,0,1},
{1,0,0,0,2,0,0,2,0,1},
{1,0,0,0,2,0,2,2,3,1},
{1,1,1,1,1,1,1,1,1,1}
};

int x = 1, y = 8, jump = 0, coin = 0, end = 0;
long time = 0;

int main(){

for(int i=0;i<10;i++){for(int j=0;j<11;j++){if(map[j][i]==3){coin++;}}}//コインの枚数チェック
locate(4,13);printf("coin = %d", coin);
draw();

while(true){
time += 1;
if(_kbhit()){
map[y][x] = 0;
locate(x,y);printf(" ");

input();

judge();

map[y][x] = 9;
locate(x,y);printf("★");
}

if(time % 500 == 0){
map[y][x] = 0;

move();
}
if(end == 1){return 0;}
locate(4,14);printf("time = %d", time/500);
}
}

void draw(){
for(int i=0;i<10;i++){
for(int j=0;j<11;j++){
locate(i,j);
switch(map[j][i]){
case 1:
printf("■");
break;
case 2:
printf("*");
break;
case 3:
printf("◎");
break;
case 4:
printf("Д");
break;
default:
printf(" ");
break;
}
}
printf("\n");
}
locate(2,16);printf("★を動かしてcoin◎を全て取りましょう。");
locate(2,17);printf("全て取るとゴールДが現れます。");
locate(2,18);printf("棘*に当たるとゲームオーバーです。");
}

void judge(){
switch(map[y][x]){
case 2:
printf("game over!");
_getch();
end = 1;
break;
case 3:
coin --;
locate(4,13);printf("coin = %d ", coin);
if(coin == 0){
map[1][7] = 4;
locate(4,13);printf("ok! ", coin);
draw();
}
break;
case 4:
printf("clear!");
_getch();
end = 1;
break;
}
}

void input(){
switch(_getch()){
case 77:
if(map[y][x+1]!=1){x ++;}
break;
case 75:
if(map[y][x-1]!=1){x --;}
break;
case 72:
if(map[y+1][x]==1){jump = 4;}
break;
case 27:
end = 1;
break;
}
}

void move(){
//重力
if(map[y+1][x]!=1 && jump == 0){
locate(x,y);printf(" ");

y++;

judge();

locate(x,y);printf("★");
}
//ジャンプ
if(jump != 0){
locate(x,y);printf(" ");

if(map[y-1][x]!=1){
y--;
jump--;
}else{jump = 0;}

judge();

locate(x,y);printf("★");
}
}

完成しました!
完璧です。
皆さんありがとうございました。
特に>>4さん!
細かく説明しなくとも、ちゃんと分かってくれる
的確なアドバイスは難しい。でもお役に立てて嬉しいです。
解決後に何ですが,一応ダブルバッファリング的な解決法も.

(1)充分な長さの char buf[] を用意しておく
(2)map[][]からの変換は buf に行う
(3)locate(0,0); puts(buf); で一括描画.
○periaさま、こーじさま
こんな感じのdraw()を改造したり、draw_line()という関数を作ってみました〜。
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

#include <string.h>


static char *convert_character[ 5 ] = {
" ", /* 0 */
"■", /* 1 */
"*", /* 2 */
"◎", /* 3 */
"Д", /* 4 */
};

static void draw_line( int map[ 11 ][ 10 ], int y );

static void draw(){
int i;
for( i=0;i<11;i++){
draw_line( map, i );
}
locate(2,16);printf("★を動かしてcoin◎を全て取りましょう。");
locate(2,17);printf("全て取るとゴールДが現れます。");
locate(2,18);printf("棘*に当たるとゲームオーバーです。");
}


static void draw_line( int map[11][10], int y )
{
char buf[ 11*2+1 ];
int a;
int x;

buf[ 0 ] = '\0';
for(x=0;x<10;x++){
a = map[y][x];
strcat( buf, convert_character[ ( a >= 1 && a <= 4 ) ? a : 0 ] );;
}

locate( 0, y );
printf("%s\n", buf);
}

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
strcat()をポインターで書き換えればさらに高速&能率的&エレガントになると思います。
誰かstrcat()ではないポインター版も作ってください!

とはいうものの最近のコンソールアプリケーションはlocate()文の中身みたいにウィンドウズプログラミングの中で操作するのか、、、時代に取り残されてるなぁ、、、自分。

staticはCではjavaとは違い、複数のファイルを扱うとき、自分の存在しているファイルからしか、
利用できなくなる。C++でいうprivateに考え方は近い、、、クラスではなくファイル間でprivateなんだ。

下から7行目のstrcat()は
strcpy( &buf[ x * 2 ], convert_character[ ( a >= 1 && a <= 4 ) ? a : 0 ] );

のほうが高速になる。ポインターにわざわざしなくても、このくらい最適化すればいいかなぁ、、、
○こーじさま
もしコンソールではなく、将来グラフィックキャラクターを扱うように成れたら、
加速度や摩擦、その辺もプログラムが出来るとマリオに近づきます。

過去初代ファミコンで1ドット単位でキャラを動かしていた当時のこだわりは
凄いものだと思います。

「Windowsゲームプログラミング」 赤坂玲音著/SOFTBANK

という本をお勧めです。僕自身は全部読んでないですが、
BitBlt()やTransparentBlt()が使えると世界が広がっていきます。
Windowsプログラミングがもっともっと楽しくなります。
たしか、カッコの使い方でどなたかが指摘していましたが、

 if(end == 1){return 0;}   <<<<<-----

これはまぁまぁまぁ状況によっては許せたとしても、

 if(map[y-1][x]!=1){
   y--;
   jump--;
 }else{jump = 0;}   <<<<<-----

これはかなり許し難いですね。
これがオススメの一例です。

 if(map[y-1][x]!=1){
   y--;
   jump--;
 }else{
   jump = 0;     <<<<<-----
 }

locate文かぁ…
何もかもみな懐かしい…
○19 Dの食卓塩さま
Wikiで見たけど、ウィンドウズにCURSESってあるの?え?Linuxを使え?そうですね。
> 朝野善果一(よかいち)さん
いや、たぶん Cygwin…

昔のわたしなら、エスケープシーケンスばしばしで書いてたかも…
○22 farsseさま
printf( "\x1b[%d;%dH★", y, x );
こんな感じ?

僕もよく使ってたエスケープシーケンス。

Cygwinか、、、勉強しなきゃ、、、
○20 お気楽さま
>}else{jump = 0;}   <<<<<-----
なぜこれが許しがたいのか、、、
なんでも一行にまとめて直感的にすることも、いいと想うんだけど、、、

プログラミングを楽しむ意味で、僕はこういう表記好きなんだよな、、、。

デバッグしずらいという意見もあるけど、あくまで個人1人で開発してるから、
自由にやるのもある意味、、、仕事じゃないんだから、いいと想う。

とくにアマチュアならば自由でいいと思う。動けばいいのだ。(問題発言をあえて言ってみる)
ま、将来チームを組んでやるなら、お気楽さまのいう事は100%正しいですが、、、あえて言ってみる。
自分で書いて自分で読むソースなら勝手ですが、こうやってmixiに披露するのなら、
他人にとっての読みやすさも考えたほうがお得ではないかと。
>朝野善果一(よかいち) さん

書式の自由度が高いことがC言語の特徴ですし、
全否定はしていませんよ。
機能の単位を表現する時にどっちが適しているかは、
そのコードの流れの雰囲気にもよるかな。

ただ、
今、手元に手頃なCの開発環境が無く、記憶も曖昧なのですが、
1行にまとめすぎると、ステップ(トレース)やブレークポイントを張るときに困ったかと。
例えば、

 if(a == 0) b=c else c=b;

この行にステップ(トレース)ポイントがある時に次へステップ(トレース)した場合、
どっちが実行されたのか分かりにくいとか。
then(true) 側にも else(false) 側にもブレークポイントが張れませんし。
# 困ったことが…あったような…なかったような…
# この部分は記憶が曖昧です。

もっとも、よかいちさん指摘の文に関しては、
1行1ステップ(Cではステップとは言わないか?)なので困らないかもしれませんが、
解析している時に、カッコの対応関係が直感的にわかりづらかったもので。
むしろこんな感じで一行に。
(((map[y-1][x] != 1) && ((y-- + jump--) != NULL)) || ((jump = 0) == 0));
○saitohさま
たしかに、みんなに見てもらうから郷に行っては郷に従え。

でもトリッキーな方法に目覚めると、プログラムって楽しいねと感じたりもする自分が3流。

○お気楽さま
ああ、デバッカーってあまり使ったことないのでそんな弊害があったのか。

識別子ごとにマークするデバッカーってないかなぁ、、、

いやーデバッカーを使わないスタイルになったのはTURBO C Ver2.0(すみません昔話です)
を使ってた頃、統合環境のデバッカーつかうとメモリ不足になってmalloc()動的メモリの確保が
出来なくなって、プログラムが動かんという状態に陥り、それ以来デバッカを使わない統合環境を
落としてからソフトを実行する習慣がついてしまいました。それからプログラマーとして3流になった
気がします。

○火星男爵さま
&& ||の特性を生かしたエレガントな方法!素敵過ぎて、難しいです(汗
LISPのような()ですがLISPというより昔のBASICっぽい物を感じます(笑

○こーじさま
すみません、年寄りどものたわごとで、話に入りずらいと思いますが、、、楽しませていただいています
ソースコードレベルで見た目がトリッキーでもコンパイラから吐き出されるアセンブラコードやバイナリが意図したもの(高速とか省資源とか)になってなければ意味がないような?
ソースコードレベルでは見やすく、吐き出されるバイナリが高性能であるためのトリックならいいんですがー。

しかしいつも完成したコードと触れ合えるわけでもないですし、良い点、悪い点とその理由を誰かが指摘するのが妥当なのではないかと思います。
○kawaさま
たしかに出力されるバイナリが最適化され、高速&能率的でなければトリッキーなソースは意味が無いかもしえません。

ただプログラムを楽しむ、遊び楽しみながら作るという意味において、ソースコードをいじくりまわす事に
喜びを感じる。結果より過程を楽しむ、そしてソースコードをどんどんどんどん作りこむいじくりまわす。

納期のない趣味の自分は、やっぱ仕事としてのプログラマーに合わない気がします。

お金よりも自分にとって価値のあるもののためにプログラムを組みたい。自分は仕事に向いていないね。
>>17
>>staticはCではjavaとは違い、複数のファイルを扱うとき、自分の存在しているファイルからしか、
>>利用できなくなる。C++でいうprivateに考え方は近い、、、クラスではなくファイル間でprivateなんだ。 これは正しい表現なのでしょうか?
staticと宣言するとグローバル変数同様に静的に変数のサイズが計算され、スタックや動的領域とは異なる空間に領域が確保されますよね。

と書いててこの話が関数やメソッドの話をしていることに気づきました。そもそも関数とかだとstaticつけなくても外部から見えないような気もしますが(´・ω・`)
○kawaさま
複数のファイルをリンクして関数を呼び出すときはexternをつけて宣言すれば、他のファイルの関数を
呼び出すことができます。グローバル変数も参照できて、同じだと思います。

staticつけなくても外部から見えない、、、?そうなのかなぁ、、、自信がなくなってきた。

ログインすると、残り8件のコメントが見れるよ

mixiユーザー
ログインしてコメントしよう!

C言語とC++言語 更新情報

C言語とC++言語のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング