iPX社員によるブログ

iPX社員が"社の動向"から"自身の知見や趣味"、"セミナーなどのおすすめ情報"に至るまで幅広い話題を投下していくブログ。社の雰囲気を感じ取っていただけたら幸いです。

DXライブラリでプログラミングしてみました

はじめましての方は初めまして。
今回の記事担当の白羽と申します。

今回私はDXライブラリというツールの紹介をしていこうと思います。

DXライブラリについて

今回はDXライブラリというツールで”こんなことが出来る”というものを実際にプログラムを作成しながらご紹介していこうと思います。
DXライブラリは、文字の表示、画像の表示などが関数ひとつで表現できるツールです。
(もちろんそれだけではありませんが・・)
私としては、ゲーム作成に特化したツールという認識をしています。
というわけで、今回はDXライブラリの紹介もかねて、マインスイーパーを作っていこうと思います。

まずは要素分けから

まず、どのようなツール、言語を使うにしても、まずやらなければならないことは
これから作ろうとしているものがどのようなものかを明確にすることです。
ただ、今回はマインスイーパーがどのようなものかはわかっていることとして(まぁ有名ですし)、マインスイーパーを構成する要素を並べるところから始めようと思います。

必要
① マス目
② 爆弾
③ 数字(隣接するマス目の爆弾数を表示する)
④ 旗(爆弾があると考えられる場所につける目印のこと)


あったらよいもの
⑤ カウンタ(スコアカウンタ、タイムカウンタなど)
⑥ 残り爆弾数の表示

大体こんなものでしょうか?

今回のマインスイーパーの定義

今回作成するマインスイーパーの定義をここで定めておきます。
今回は
1:マス目の数は25とする。
2:マス目の中を左クリックしたら
2-1:爆弾があった場合は、ゲームオーバー
2-2:爆弾がなかった場合は、隣接した8方向にある爆弾の数を表示する
3:マス目の中を右クリックしたら、旗を立てる。
4:すべての爆弾に旗を立てたらクリア
5:マス目以外を左クリックしたら、何も起こらない

とします。
定義なんて書いてどうするのかと思う方もいるかもしれませんが、
この後の文章や図の理由づけにもなりますし、このような定義を明確にすることが
ものづくりにおいて必要なことでもあると考えているのでここで記述しました。

では、まずはマインスイーパーになければならない’必要’な要素から作っていきます。

プログラミング開始

まず、マス目の座標と、爆弾の有無を構造体で定義しておきます。構造体についてはDXライブラリの機能ではないため、今回は説明しないこととします。
ただ、この後の理解のために今回使用した構造体のコードだけ張っておきます。
typedef struct{
int x1, y1,x2,y2; // マス目位置
int bomb; //爆弾の有無(0で爆弾なし、1で爆弾有)
int number; //数字表示用
int flag; //旗の定義
} ms ;
例:今回の構造体定義

①マス目を作る

ではマス目の作成に入ります。
マス目に必要な要素は、
①:マス目の枠
②:枠に応じたマウス判定
の2つです。
②番でいう判定とは、ゲーム中にマウスクリックを行ったときに
どの枠をクリックしたのか判定する、というものです。

まずは①のマス目の枠から始めます。
これは視覚の問題だけであり、必要な場所に必要な数だけ枠(マインスイーパーなので四角の枠)を描画すればよいことになります。
今回はDXライブラリの関数 [DrawBox] を使っていきます。
この関数は、四角を描画する関数です。
1,2番目の引数がそれぞれ描画する四角の左上のx,y座標、
3,4番目の引数が四角の右下のx,y座標、
5番目が色(DXライブラリのGetColor関数であらかじめ取得しておく)、
6番目が四角を塗りつぶすかどうかのフラグtとなっています。
例:DrawBox( 50 , 50 ,100 , 100 , color, TRUE)
この関数を、for文を用いて必要な四角の数だけ呼び出せば(今回の定義では25回)、マス目の枠の完成です。

f:id:ipx-writer:20151125231526p:plain
図1:DrawBox関数によるマインスイーパー風?マス目枠例
マインスイーパーっぽくないですね・・(^^;
今回はDXライブラリの紹介がメインなので、見た目にはこだわらない方針とします。
もしここを参考にプログラムを作ろうという方がもしもいらっしゃいましたら、
マインスイーパーについてちゃんと学んでから作成してください。(仕組みとか、見た目とか)

次は②:枠に応じたマウス判定を作成します。
まずはクリックしたという判定を行います。
ここではGeTMouseInput関数を使用します。
例:if( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 )//左クリックしたとき
例のMOUSE_INPUTLEFTのように、GetMouseInputは特定の定義値とAND演算の形で記述する必要があります。
次にクリックしたときの座標を取得します。
GetMousePointという関数を使用します。
例:GetMousePoint( &MouseX , &MouseY );
一つ目の引数がマウスのX座標のint型ポインタ、
二つ目の引数がマウスのY座標のint型ポインタとなっています。

ここまでの作業を終えれば、マス目は完成です。

爆弾を用意する

次に爆弾の準備を行います。
まず、構造体に爆弾用の変数を定義してあるため、マス目作成の過程で書いたクリックした判定と爆弾の判定を組み合わせるところから始めます。
まず、必要な要素は
1マス目に爆弾を用意する。
2マス目を押したクリック判定
の2つです。1の爆弾は
爆弾があれば1、爆弾がなければ0とします。

ただし、あらかじめ変数を入れるようなことをすると爆弾の位置が固定されてしまうため乱数を用意する関数[GetRand]を用いて爆弾を用意します。この関数は0から引数までの値のいずれかの整数を返します。
関数例:brand=GetRand(24);//0から24までの値のいずれかを取得する。

今回のプログラムでは以下のように定義しました。
(b_item=爆弾の数、mdata=構造体、mdata.bomb=構造体に用意した爆弾用変数)
例:for(i=0;i < b_item;i++){ //爆弾設定
brand=GetRand(24);
if(mdata[brand].bomb==0)
{mdata[brand].bomb=1;}
else {i-=1;continue;}
}
GetRandが爆弾を用意しており、もし爆弾のある場所に再度爆弾を入れようとした場合はelse文で爆弾をキャンセルする形にしています。これにより、爆弾の位置がランダムとなり、さらにif文の処理により、爆弾が同じ個所に設置されない⇒想定している爆弾の数(上記コードのb_item)=実際の爆弾の数、となるようにしています。

爆弾の説明はこのぐらいにして、次はマウスクリックと爆弾の組み合わせに入ります。
といってもこちらは大したことをするわけでもないのですが(^^;
既に構造体に四角の四隅のデータを入れているため、四隅のデータを用いて”四角の中である”という判定を行います。やることは
X座標が四角の左側<四角の右側
かつ
Y座標が四角の上側<四角の下側
となるようにします。

f:id:ipx-writer:20151125231534p:plain
図2:左上のマス目枠に対する判定の可視化
マウスクリックしたときは、図2のような4つの線で囲われた範囲であるかどうかを判定します。そして、各マス目枠に対して判定を行い、どこかで判定にかかればマス目枠の中でクリックしたと言えます。

後はクリックしたマス目に爆弾があることを表示するだけです。
と言っても、クリックしたときにマス目の中であった⇒爆弾があった⇒ゲームオーバー
とするだけですが。
DXライブラリようの関数も特にないので、ここは特に説明しません。そもそもif文ひとつで判定できるので・・・・

f:id:ipx-writer:20151125231915p:plain
図3:ゲームオーバー画面
今回のプログラムでは上図のようになります。爆弾の絵があると良いのですが、
絵の著作権などの問題もありますので、今回は文字と色で表現しています。

数字を表示できるようにする

では次に、マス目枠内に表示される数字を作成します。
ここで必要なのは数字を表示することと、隣接するマスの判定の2つです。
数字の表示はDXライブラリの関数[DrawFormatString]を使用します。
関数例:DrawFormatString( 350,350 , color, "%d,%d",765,72 ) ;
DrawFormatStringは書式付で文字を表示する関数です。書式なしの関数も存在しますが、DrawFormatStringで文字だけを表示することも可能です。そのあたりはお好みで。
図3の’爆’の文字もこの関数を使用しています。

次に隣接するマスの判定ですが、これはDXライブラリというよりもロジックの問題なので、ここではあえて触れません。ただ、考え方としては
隣接するマスの座標を用いたり、それぞれのマスにあらかじめ番号を振っておき、その番号ごとの隣接関係を定義して判定したり、などの方法が考えられます。また、決して方法は一つではありません。そして、こういった作業は’ロジックを構築する’練習に良いのではないか?と、私がマインスイーパーを作成しながら思っているため、ここの説明はこのあたりにして、次に行きます。

旗を置けるようにする

今度はマインスイーパーでよく見る旗の作成です。
今回は右クリックを押すと旗を立てる、と定義します。
と言っても、クリック判定についてはすでに説明しているため、
やることは先に説明したことと同じです。
あえて違うとすれば、
if( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 )//左クリックしたとき
                ⇓
if( ( GetMouseInput() & MOUSE_INPUT_RIGHT ) != 0 )//右クリックしたとき

このように判定が変化するぐらいで、やることは同じです。何か拍子抜けしますが、同じことを説明しても意味がないので、旗の画面例だけ出しておきます。

f:id:ipx-writer:20151125231922p:plain
図4:旗作成後の画面
爆弾と同じく、画像関連の問題があるため、今回も文字を使用しています。
前述したとおり、やることは左クリックによる爆弾探しと変わりありませんが、
既に開いたところ(爆弾がないと確定しているところ)では何もしないなどの処理を加える必要があります。もっとも、この辺りはロジック構築に関わってくるため、あえて説明はしませんが。

ゲームの終了条件を作る

さて、これで最初に必要な要素として分類した
①マス目
②爆弾
③数字(隣接するマス目の爆弾数を表示する)
④旗(爆弾があると考えられる場所につける目印のこと)
がすべて完成しました。あとは、ゲームを進めて問題なく終わることが出来れば完成です。
ここで考える終わりには、ゲームクリアによる終了と、失敗(ゲームオーバー)による終了の2つが存在します。
まずは、今回のマインスイーパーにおけるそれぞれの定義を決定します。
ゲームクリア:爆弾以外のマスをすべて開く
ゲームオーバー:爆弾のマスを開く

今回は成功・失敗の定義をこのように定義しました。”今回は”というのは、必ずしもその定義にのっとるとは限らない、と私が考えているからです。たとえば、マインスイーパーの目的がすべての爆弾を発見することであれば、爆弾があると考えられる全ての場所に旗を立てることがクリア条件になります。
何が違うんだと考える方もいるかもしれませんが、私個人としてはこの違いはロジックの違いにつながると考えています。
たとえば、ゲームクリアの条件が‘爆弾以外のマスをすべて開く’ならば
開いたマスの総数=全てのマスの数 - 爆弾の数を満たせばクリアとなります。
しかし、クリア条件が’全ての爆弾を発見する’ならば、全ての爆弾の上(または全ての爆弾の上だけに)旗を立てればよいということになります。
・・なんだかあまり変わらないような?
とにかく、あらゆることに対して定義を明確にすることが構築するべきロジックにも直結する、ということを言いたかったのです。少なくとも、私はそう考えています。
むしろ、マインスイーパーを作成しながらそういうことを強く実感しました
やっぱり一番大変なのはロジックを考えることなんだなって。

話は少しそれましたが、ゲームクリア・ゲームオーバーの図はそれぞれ図5(ここから少し下にあります)と図3(それなりに上にあります)を参照してください。

f:id:ipx-writer:20151125231931p:plain
図5:クリア時の画面

というわけで、’DXライブラリの紹介’という名目でのマインスイーパー製作が完了いたしました。
完成した感想ですが、私自身がしばらくC言語に触っていなかったため、急にCに手を出したところ、配列外に値を入れようとしたり、if文の条件文の=が一つになっていたためエラーが発生して原因究明に30分ほどかかるなどの事態もありましたが・・・
まぁ完成もしましたし、マインスイーパーを通じてDXライブラリでこんなことが出来る!であったり、ロジック構築の重要性であったり、いろいろなことを書けたので、これでよか
ったと考えます。

それでは、ご拝読ありがとうございました。