萌エロこむ

アニメ・ゲーム・エロゲ・声優関係で面白いor萌エロな情報・動画をお届けします

【プログラム】 本家『DXライブラリ置き場 サンプルプログラム 10.落ちものゲーム基本』を画像を付けて考えてみる

DXライブラリ置き場 サンプルプログラム
こちらのプログラムを見てなんとなく学びつつ考えていたんですが、図と自分なりに整理した文章が欲しくなったので。
※注意 素人なので何か致命的な勘違いをしている可能性もあります。(DXライブラリって何?という人は前記事参照)

DXライブラリ置き場 サンプルプログラム『10.落ちものゲーム基本』のプログラムについて考えます。


とりあえず『SetGraphMode( 640 , 480 , 16 ) ;』の前の行に
『ChangeWindowMode( TRUE ) ;』を付け加えてウィンドウモードにして、Microsoft Visual C++ 2010 Express(無料)で『デバッグなしで開始』。

DXライブラリ置き場 サンプルプログラム上部にある『サンプルプログラム実行用プロジェクト』を使用しています)

そうすると次のような画面が出てきます。




赤い枠線の部分を考察。

DXライブラリ置き場 リファレンスページ DrawBox


// 枠を描画

DrawBox( STAGE_X - 24 , STAGE_Y , STAGE_X ,
STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE ,
GetColor( 255 , 0 , 0 ) , TRUE ) ;


DrawBox( STAGE_X + WORLD_WIDTH * BLOCK_SIZE , STAGE_Y ,
STAGE_X + 24 + WORLD_WIDTH * BLOCK_SIZE , STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE ,
GetColor( 255 , 0 , 0 ) , TRUE ) ;


DrawBox( STAGE_X - 24 , STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE ,
STAGE_X + 24 + WORLD_WIDTH * BLOCK_SIZE , STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE + 24 ,
GetColor( 255 , 0 , 0 ) , TRUE ) ;



変数ばかりでイメージしにくいのにで、置換(#define)の数値と合わせて図に。



※画像1

一つ目のDrawBoxが左、二つ目が右、三つ目が下の枠線を描画しているのだと思われます。
GetColorの数値を変えて確認。



// 枠を描画
DrawBox( STAGE_X - 24 , STAGE_Y , STAGE_X ,
STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE ,
GetColor( 255 , 0 , 0 ) , TRUE ) ;

DrawBox( STAGE_X + WORLD_WIDTH * BLOCK_SIZE , STAGE_Y ,
STAGE_X + 24 + WORLD_WIDTH * BLOCK_SIZE , STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE ,
GetColor( 0 , 255 , 0 ) , TRUE ) ;

DrawBox( STAGE_X - 24 , STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE ,
STAGE_X + 24 + WORLD_WIDTH * BLOCK_SIZE , STAGE_Y + WORLD_HEIGHT * BLOCK_SIZE + 24 ,
GetColor( 0 , 0 , 255 ) , TRUE ) ;








この枠は『枠内がゲーム範囲だよ』という事を示す為の描画であり、DrawBoxを使わず画像でも代用できます。
DrawBoxを使う利点は、変数の数字一つ書き換えるだけで幅を変えられるという事でしょうか。
代わりに画像を使ってみます。




コチラはPNG画像。中央の白い部分は透過処理。
赤い枠線だけでは侘しいので

こちらのフリー素材と組み合わせて背景に。
ソースから三つのDrawBox関数を消し、代わりに
『DrawGraph( 0 , 0 , waku , TRUE ) ;』の一文を加えます。さらに、

// ゲームを初期化
InitGame() ;


の下の行に『waku = LoadGraph( "Data/otimono4.png" ) ;』を、
冒頭の宣言郡に『int waku ;』を加えます。
そして上記の画像を『otimono4.png』にリネームしてDataフォルダに入れて実行。
DXライブラリ置き場 リファレンスページ LoadGraph
DXライブラリ置き場 リファレンスページ DrawGraph
新・C言語 ~ゲームプログラミングの館~ [DXライブラリ]1.3章 画像を表示してみよう



成功。




InitGame関数

次に、メインループ前の

// ゲームを初期化
InitGame() ;


について考察。

void InitGame( void )
{

int i , j ;

// 前回時間をセット
OldTime = GetNowCount() ;

// アクティブブロックの位置をセット
ActiveX = WORLD_WIDTH / 2 ;
ActiveY = 2 ;

// アクティブブロックを生成
CreateNewActiveBlock() ;

// マップブロックの初期化
for( i = 0 ; i < WORLD_HEIGHT ; i ++ )

for( j = 0 ; j < WORLD_WIDTH ; j ++ )

Block[ j ][ i ] = 0 ;

}



fc2ブログ記事だとTabキーによるインデント(空白)が反映されなくて少し見辛かったので、
代わりにfc2の「高機能テキストエディタ」のインデント機能を使用。
こちらはコピペした場合、空白はできません。

CreateNewActiveBlock関数が呼び出されます。

// 新しいブロックの生成
void CreateNewActiveBlock( void )
{

int i ;

// ランダムに3つブロックをセット
for( i = 0 ; i < 3 ; i ++ )

ActiveBlock[ i ] = GetRand( BLOCKTYPE_NUM - 1 ) + 1 ;

}



GetRand( BLOCKTYPE_NUM - 1 ) + 1 ;というのが分かりづらいですね。

DXライブラリ置き場 リファレンスページ GetRand
DXライブラリ質問掲示板 GetRand()関数

宣言 int GetRand( int RandMax ) ;

概略 乱数を取得する

引数 RandMax : 取得する乱数の最大値
戻り値 0 から RandMax で指定した数値のどれかの数値

解説  乱数を得ます。この関数は 0 から RandMax で指定した数値の どれかの数値を返します。


つまりBLOCKTYPE_NUM(ブロックの種類の数)は#defineで5に変換されるので、
GetRand( BLOCKTYPE_NUM )だとGetRand(5)になり、これだとブロック種類が五つの筈が0~5の六種類になってしまうので
GetRand( 5 - 1 )として五種類に。
GetRand( 4 ) + 1の最後の+ 1は……0~4を1~5にする為のもの?

for( i = 0 ; i < 3 ; i ++ )文で三回繰り返し、ActiveBlock[ 0 ]、ActiveBlock[ 1 ]、ActiveBlock[ 2 ]に、
それぞれ1~5の数字が入ってると仮定。

それを確認する為に次に少し飛んで、ScreenDraw関数内の『// アクティブブロックを描画』を考察。

for( i = 0 ; i + ActiveY >= 0 && i < 3 ; i ++ )
{

k = ActiveBlock[ i ] - 1 ;

DrawBox( STAGE_X + ActiveX * BLOCK_SIZE , STAGE_Y + ( ActiveY - i ) * BLOCK_SIZE ,

STAGE_X + ActiveX * BLOCK_SIZE + BLOCK_SIZE , STAGE_Y + ( ActiveY - i ) * BLOCK_SIZE + BLOCK_SIZE ,
GetColor( BlockColor[ k ][ 0 ] ,BlockColor[ k ][ 1 ] ,BlockColor[ k ][ 2 ] ) , TRUE ) ;

}



k = ActiveBlock[ i ] - 1 ;によって再び- 1されて0~4となった数字がkに代入。
kが使われるのはGetColor( BlockColor[ k ][ 0 ] ,BlockColor[ k ][ 1 ] ,BlockColor[ k ][ 2 ] ) , TRUE ) ;の部分。

// ブロックの種類ごとの色データ
int BlockColor[ 5 ][ 3 ] =
{
{ 128 , 128 , 128 } , { 255 , 100 , 100 } ,{ 255 , 255 , 0 } ,//0灰色、1赤色、2黄色
{ 255 , 0 , 255 } , { 0 , 255 , 255 } //3紫、4水色
} ;


配列の[ 5 ]とは0~4の事なので、kに0~4のランダム数字が入るのは正解です。

DrawBoxは変数だらけで面倒そうですが、色を付けて一まとめにすれば分かり易い。
要するに一つのブロックを描写するために、一列目は左上、二列目は一列目に一ブロック分を足しただけです。

※画像1の状況に当てはめるとfor文一週目は一番下のブロックを描写しています。



黄色なのでActiveBlock[ 0 ]の中身は2だった様です。

for文二週目は ( ActiveY - i ) のiが1になるので、一つ上のブロックが描写されます。



ActiveBlock[ 1 ]の中身も同じく2で、ActiveBlock[ 2 ]の中身は0だった様です。








次は、メインループのKeyInput関数(キー入力処理)について。

// キー入力に応じて処理をする
if( Key & PAD_INPUT_DOWN ) MoveActiveBlock( 0 , 1 ) ;//もし↓を押したら
if( ( Key & ~OldKey ) & PAD_INPUT_LEFT && ActiveX > 0 ) MoveActiveBlock( -1 , 0 ) ;//もし←を押して、なおかつActiveX(操作ブロック)が0より大きければ
if( ( Key & ~OldKey ) & PAD_INPUT_RIGHT && ActiveX < WORLD_WIDTH - 1 ) MoveActiveBlock( 1 , 0 ) ;//もし→を押して、なおかつActiveXがWORLD_WIDTH(ステージの幅)- 1より小さければ



MoveActiveBlock関数(キー入力処理)が呼び出される。

int MoveActiveBlock( int MoveX , int MoveY )


もし↓を押した場合、MoveXには0がMoveYには1が代入される。
MoveActiveBlock関数から、その場合のみの流れを引用するなら

int MoveActiveBlock( int MoveX , int MoveY )

{

int NewX , NewY ;


// 移動後の座標をセットする
NewX = MoveX + ActiveX ;// ActiveX ActiveY アクティブブロックの一番下のブロックの位置
NewY = MoveY + ActiveY ;

// 上下移動の処理
if( MoveY != 0 )//もしMoveYが0じゃなければ実行
{

// 画面の一番下のブロック位置まで来ていたらブロックを固定させる
if( NewY >= WORLD_HEIGHT )//WORLD_HEIGHT ステージの高さ16
{

LockActiveBlock( ActiveX , ActiveY ) ;// ActiveX ActiveY アクティブブロックの一番下のブロックの位置

// 移動を無効にする
MoveY = 0 ;

}
else
// 各3つのブロックが画面上のブロックに当たっていないか調べる
if( CheckHitActiveBlock( NewX , NewY ) == - 1 )//※1
{

// あたっていたらブロックを固定する
LockActiveBlock( ActiveX , ActiveY ) ;

// 移動を無効にする
MoveY = 0 ;

}

}

// 座標を移動する
ActiveX += MoveX ;
ActiveY += MoveY ;

// 終了
return 0 ;

}




次に※1

// 各3つのブロックが画面上のブロックに当たっていないか調べる
if( CheckHitActiveBlock( NewX , NewY ) == - 1 ))//※1


について考えます。

NewX , NewYは、現在値であるActiveX ActiveYが移動したとして、その先に衝突するブロックや壁がないか?
という事を確かめる為の『仮定値』です。
今回の例では、一つ↓に行った場合はどうなる?という意味でMoveYが+1され、
NewY(仮定値)とActiveY(現在値)が違っています。



何故左から5列目にあるのにActiveX=4なのかというと、
一番左のマスは0、その次は1というふうに数え始めているからです。

CheckHitActiveBlock関数の定義。

int CheckHitActiveBlock( int x , int y )//NewX , NewY
{

int i ;

// 3つあるブロックがそれぞれ画面上のブロックに当たっていないか調べる
for( i = 0 ; y - i >= 0 && i < 3 ; i ++ )
{
// 当たっていたらここで終了
if( Block[ x ][ y - i ] != 0 ) return -1 ;
}

// 当たっていない場合だけ0を返す
return 0 ;

}


int x , int yにそれぞれ仮定値であるNewX , NewYを代入。

ActiveBlockは動かしているブロックで、Blockは止まっているブロックです。
Block配列は全マスに存在しており、初期化関数InitGame内で既に全マスに0(ブロック無し)が代入されています。

……って、
ああっ!
成程、ここで気付いた!
なんでさっきCreateNewActiveBlock関数内でActiveBlock[ i ] = GetRand( BLOCKTYPE_NUM - 1 ) + 1 ;で1~5にした後、
ScreenDraw関数内でわざわざk = ActiveBlock[ i ] - 1 ;によって再び0~4にしていたのかを。
そんな無駄に穴掘ってから埋めるような真似をしていた理由が分かった。

LockActiveBlock関数内で

// ブロックの固定
Block[ x ][ y - i ] = ActiveBlock[ i ] ;//x=ActiveX現在値


Blockに0~4、というか0を渡したらダメだったんだ。0=ブロック無しの状態だから。
試しに+ 1と- 1を消して実行した結果、灰色のブロックが繋がってないのに置いた途端に消えたのはそういう事だったんだ多分。
あー……なんかすっごいスッキリした。割と長い時間意味不明だったので。

つまり色データの流れとしては、
1, CreateNewActiveBlock関数で新しい三つのブロックを生成。それぞれのブロックに1~5のランダムの数字を与える。
2, 1~5にしていた場合、ActiveBlock(動くブロック)からBlock(固定ブロック)にそのまま渡せる。0を渡すとそのマスはブロック無しの状態と判断され空白になってしまう。
3, 最終的にScreenDraw関数内で描画する時には0~4にしなければならないので、DrawBoxで描画する直前にif文で『そのマスが0でなければ』という意味の『if( Block[ j ][ i ] != 0 )』で条件分岐してから- 1して0~4にして描画。



最後の方は飛ばし飛ばしな感じの文章になりましたが、長くなったのでここまで。
続きの落ちゲー記事を書くかどうかは未定です。




関連記事
拍手する
[タグ] DXライブラリ
[ 2012/10/29 ] プログラム | TB(0) | CM(0)
コメントの投稿












管理者にだけ表示を許可する
トラックバック
この記事のトラックバックURL

プロフィール

れいじ

Author:れいじ
二次元寄りの情報・雑記・動画紹介サイト。たまにアニメ感想も。
萌え系中心です、おそらく。
現在、相互リンクRSS募集中です。
何かありましたら左下のメールフォ-ムよりお願いします。

近況

試験的に二次寄りアンテナというものを作ってみました。

ブログTOPページへ

Lc.ツリーカテゴリー
FC2カウンター
メールフォーム

名前:
メール:
件名:
本文: