トップページ > 過去ログ > 記事閲覧
画像をボタンに
名前:ligaty 日時: 2007/12/08 19:43

初めまして。 現在ADVのゲームエンジンを作っているのですが 、画像をボタンにするところで困っています。 ただの四角い画像をボタンにするのなら各座標を取ってマウスの座標と比べればいいというのはわかるのですが、これが○などの形になればどう処理すればいいかわかりません。 かといって四角い画像のみにするのもおかしいですし・・・ 自分なりに考えた結果アルファチャンネル画像を使えば何とかなるのかもしれないと思ったのですが、そこから先見当がつきません。 どうかわかる方がいらっしゃればご教授願いたいです

Page: 1 |

Re: 画像をボタンに ( No.1 )
名前:優柔不断 日時:2007/12/08 20:38

円形のボタンについてはこんな感じでいいと思います。 #include"DxLib.h" using namespace std; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int x,y; ChangeWindowMode(TRUE); if(DxLib_Init()==-1)return 1; SetDrawScreen(DX_SCREEN_BACK); while(ProcessMessage()!=-1){ DrawCircle(100,100,50,GetColor(255,255,255),FALSE); GetMousePoint(&x,&y); if((((x-100)*(x-100))+((y-100)*(y-100)))<50*50&&(GetMouseInput()&MOUSE_INPUT_LEFT)){ DrawString(0,0,"押されています",GetColor(255,255,255)); }else{ DrawString(0,0,"押されていない",GetColor(255,255,255)); } ScreenFlip(); ClearDrawScreen(); } DxLib_End(); return 0; } とりあえず適当にやってみましたのでコンパイルして実行してみてください。
Re: 画像をボタンに ( No.2 )
名前:ligaty 日時:2007/12/08 22:12

なるほど、方程式で現せられるものならばそのようにやればいいのですね。 しかし、私が考えているのはある画像自体をボタンにすることであって、それだけでは実装で競うにありません。 とはいえ又一つ勉強になりました、ありがとうございます
Re: 画像をボタンに ( No.3 )
名前:EMIYA 日時:2007/12/08 23:36

こんばんわligatyさん。 私もADV用のエンジンを作ってるんですが、メッセージウィンドウ周りや、コンフィグなどでの あーいうそこまで大きくないボタンを作るのであれば、四角形や円形の判定で大まかにチェックした方がいいですね。 ADVなので、判定チェックが多少ずれてても、ボタンあたりを押して、 ちゃんと判定が通るなら多少ボタンから判定がはみ出ても気にはならないと思いますよ。 まぁ、実際にADVのゲームをプレイして、色々どういう風に作られてるか、研究してみるといいですよ。
Re: 画像をボタンに ( No.4 )
名前:通りすがり 日時:2007/12/09 23:01

よほど特殊な形状では無い限り、矩形で大雑把に当たり判定を取るくらいでいいのではないかな、と。 アクションやシューティングじゃないですし、操作に違和感が無ければ問題ないと思います。 一応、思いつく方法を書いておきます。 ・矩形を複数用いて形状を作成する int CheckCollision(int x, int y, RECT *rect, int arraysize); のような関数を作成し、rect配列内をarraysize分だけ判定を取ります。 x,yがrect内にある矩形の一つにでも含まれていれば真を返すようにします。 また、工夫次第で三角形・円・多角形なんかを含めた多重領域判定をさせることもできます。 ・当たり判定計算用のサーフェスを作る まず、描画可能サーフェスを用意します(サイズはメインウィンドウと同じで、白地)。 次に、当たり判定を持つ画像を使用する場合、次のようにします。  ・画像をメインスクリーンに描画  ・同時に、当たり判定取得用サーフェスにも描画(このとき、成分に背景色を含まないようにする) そして、当たり判定を取得したいときは、判定取得用サーフェスの該当座標をGetPixelで読みます。 白であれば偽、それ以外であれば真とします。 ただしGetPixel自体が重いため、乱用は効きません。 直接メモリアクセスできれば良いのですが、描画可能スクリーンがメモリアクセスできたかどうかはちょっと記憶に無いです。
Re: 画像をボタンに ( No.5 )
名前:優柔不断 日時:2007/12/10 23:18

別にGetPixelしなくても0と1で構成した当たり判定用の二次元配列を用意して、 座標の位置の要素を参照して1,0で当たり判定してはどうでしょうか?
Re: 画像をボタンに ( No.6 )
名前:ligaty 日時:2007/12/11 18:23

汎用性のあるものを作りたいと考えているのですが・・・ 優柔不断さんの方法が簡単そうなんですが、 画像を読み込んだときに上手いこと2次元配列を作れるかどうかよくわからないです。 できれば画像の情報から取得できればと思っています
Re: 画像をボタンに ( No.7 )
名前:通りすがり 日時:2007/12/12 00:58

画像の情報からマスク配列を取得する方法は、取りあえず二つ考えられます。 まず前提として、可変サイズの配列を確保する必要がありますので、 これはnewかmallocで作ります(終了時に解放を忘れずに) ・GetPixelで1点1点調べる 一旦バックバッファに画像を描画し、総当たりで調べます。 取得したデータは最初に確保した配列に格納します。 欠点として、ロード時間が長くなることが挙げられます(画像のサイズによっては洒落にならない時間がかかる) ・グラフィックメモリ領域を直接調べる サンプルプログラムにある、直接メモリアクセスを利用して調べます。 GetPixelと異なりロード時間はほぼゼロに出来ますが、グラフィック領域が どのように情報管理されているかの知識が必要です。 (Bitmapファイルの構造を調べれば把握できます) また、元のファイル形式がBitmapであれば直接ファイルをバイナリもしくはAPIで読んで 調べることもできます(メモリアクセスと同等) 汎用性という意味では、GetPixelでも二次元配列でも大差ないと思います。 ただ、両者にそれぞれ欠点が考えられますので、記載しておきます。 ・GetPixel法  調べたい座標の数が増えると処理が重くなる(数百程度が目安) ・配列法  上手く処理を組まないと、当たり判定の演算頻度と画像サイズによっては重くなる(ループ処理のため)  ボタンの拡大縮小・変形が非常にやりづらい ※ 処理は、オブジェクトが移動したときのみ判定を更新するなどで軽くできます   しかし、大サイズあるいは複数の画像が毎フレーム動いたりすると描画に比べかなり重くなります <訂正> 考えてみると、GetPixel法で描画可能サーフェスを作る必要はありませんでした。 当たり判定の処理と実際の描画処理を分割することで、バックバッファで当たり判定を取ることが出来ます。 (具体的には、判定のある画像の描画→処理一般(判定含む)→全画像の描画→フリップ→…) 結局、調べたい座標の数か、扱う画像サイズ(と数)のどちらを優先するかで選ぶのが良いかと思われます。
Re: 画像をボタンに ( No.8 )
名前:通りすがり 日時:2007/12/12 01:04

<訂正の訂正> なんやかんやで、次々と改善点が思いついて修正がキリが無くなってきました(笑) (上のも10回以上訂正していたりします) 詰まるところ、恐らく一番高速だろう方法がまとまったので書きます。 ・フレーム処理の初期化とバックバッファのクリア ・まずバックバッファに当たり判定を持つ画像を描画 ・バックバッファをロックして直接メモリアクセス ・調べたい座標のデータを取って当たり判定 ・ロック解除 ・ここまでに行いたい処理を全て実行 ・バックバッファをクリアして、ゲーム画面の描画 ・フリップ ・最初に戻る これなら配列操作のためにループを回す必要も無く、GetPixelの負荷もかかりません。 描画能力に余裕がある限りの画像を扱え、いくらでも座標を調べることができます。 また、判定画像の回転や変形なども容易に行うことができます。 <補記> 座標を調べる手順は次の通りです。 ・背景色を決めておく(例えばR0G0B0とする。画像(ボタン)にはこの色は含まないようにする) ・クリアしたバックバッファに当たり判定を持った画像(ボタン)のみを描画 ・バックバッファから、当たり判定を取りたい座標のカラーコードを取得する (32bit、800×600のスクリーンであれば、点(x, y)のカラーコードは  ロックしたメモリの先頭から数えてアドレス (y*800 + x) * 32/8 から4Byte分が該当します  GetPixelであれば、実行するだけです) ・取得したカラーコードがR0G0B0以外であれば、描画した画像が存在する=当たり判定は真となります。 (GetPixelではRGB値が直接は分かりませんが(計算が必要)、プログラム初期化時に背景色の カラーコードを取得しておき、それと比較することで簡便に処理できます) もしマウスカーソルの当たり判定を取得するだけであれば、メモリアクセスという面倒臭い手続きを行わなくても 毎フレーム、カーソル座標に対してGetPixelを実行すれば済みます。 (毎フレーム1回のGetPixelであれば、ロックして直接アクセスしても処理負荷は恐らく同じです(ほぼゼロ)) メモリアクセスは、1フレームに複数の点の当たり判定を行いたいとき (画面内のオブジェクトなど、マウス以外の複数の点がボタンなどに当たっているかどうかを調べたい場合など)や、 面積を持った物体とボタンが重なっているかどうかを調べたいときに有効です。
Re: 画像をボタンに ( No.9 )
名前:優柔不断 日時:2007/12/11 23:41

これは、ボタンの形状と色の関係が、明確に分かれている場合のみ使える方法ですが… ボタンごとに関数をつくり、関数ポインタの配列にに代入しておく 関数の型はint func(void);が適していると思います。 ※1 裏画面への描画 ↓ 毎フレームで、クリックをチェックしておく *クリックがあったら ↓ どのボタン(画像)が押されたのかチェックする。 ↓ そのボタンの関数が格納された配列要素から関数を呼び出す。 *呼び出し先 マウスのクリック座標を取得する。 ↓ その点の色をGetPixelで取得する。 ↓ その色がボタンの範囲の色であれば、関数は真を返す。それ以外であれば偽を返す。 *呼び出し元 それを適当な変数に代入し、その結果によって適切な処理をする。 ↓ ScreenFlip(); ClearDrawScreen(); ※1へ戻る。 これだとGetPixel()は1回で済みます。
Re: 画像をボタンに ( No.10 )
名前:通りすがり 改め TNT 日時:2007/12/12 02:02

説明がもの凄く煩雑になってしまい、分かりにくいかと思いますので、 百聞は一見に如かずということでソースを張ります。 (最初からそうしろという声が聞こえてきそうですが…) #include "DxLib.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ ChangeWindowMode(TRUE); if(DxLib_Init() == -1) return -1; SetDrawScreen(DX_SCREEN_BACK); int gh; int BGColorCode, ColorCode; int MouseX, MouseY; int MouseLeftCurrent = 0, MouseLeftLast = 0; // マウスが押された瞬間の判定用 int MouseLeftDownedFlag; int HitFlag; // ボタンの読み込み gh = LoadGraph("button.bmp"); // 背景色のカラーコード取得 ClearDrawScreen(); BGColorCode = GetPixel(0, 0); while(ProcessMessage() != -1){ if(CheckHitKey(KEY_INPUT_ESCAPE)) break; // マウス情報の取得 GetMousePoint(&MouseX, &MouseY); MouseLeftLast = MouseLeftCurrent; MouseLeftCurrent = GetMouseInput() & MOUSE_INPUT_LEFT; if(MouseLeftCurrent && MouseLeftCurrent != MouseLeftLast){ MouseLeftDownedFlag = 1; }else{ MouseLeftDownedFlag = 0; } // ボタンのみの描画 ClearDrawScreen(); DrawGraph(0, 0, gh, TRUE); // マウス座標のカラーコード取得 ColorCode = GetPixel(MouseX, MouseY); // 当たり判定 BGColorCode != ColorCode ? HitFlag = 1 : HitFlag = 0; // マウスが領域外に出ていたら当たり判定は偽 if(MouseX < 0 || MouseX > 639 || MouseY < 0 || MouseY > 479) HitFlag = 0; // ここから通常描画 ClearDrawScreen(); DrawGraph(0, 0, gh, TRUE); if(HitFlag){ // 光るエフェクト SetDrawBlendMode(DX_BLENDMODE_ADD, 128); DrawGraph(0, 0, gh, TRUE); SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0); } if(HitFlag && MouseLeftDownedFlag) printfDx("Button Downed\n"); ScreenFlip(); } DxLib_End() ; // DXライブラリ使用の終了処理 return 0 ; // ソフトの終了 } 適当な形の画像button.bmpを作成して、実行してみてください。 マウスオーバーと左ボタンダウンに反応するようにしてあります。 (画像が真っ白だと、マウスオーバーには見かけ上反応しません。なお、背景色は真っ黒(R0G0B0)です) <補足> 複数のボタンが存在し、どのボタンが押されたかを識別したい場合は ボタンを1つ描画するごとに当たり判定をチェックすることで可能です。 (偽だった判定が真に変わった場合、最後に描画されたボタンが押されたことになる) ただし、ボタンが重なると下のボタンが真を、上のボタンが偽を返すというpriorityの逆転が生じるため、 ちょっとした工夫が必要です。 (例えば、真を返したらその座標を背景色で塗る。そうすると、同じ場所にボタンが来たらまた識別することができる) <修正> ClearDrawScreen();が一箇所抜けていたのを修正しました
Re: 画像をボタンに ( No.11 )
名前:ligaty 日時:2007/12/12 01:46

TNTさん非常にわかりやすい例まで書いていただいて大変感謝しています。 TNTさんの方法をとらせてもらおうと思います。 また考えてくださったほかの皆さんもありがとうございました。 大変勉強になりました

Page: 1 |