* FormViewを使って作る
マップエディタはSDIの上にボタンを配置して作ります。
ボタンが配置できるSDIプロジェクトの作り方は、
1 MFCAppwizard(exe)を選択する >> OK
2 1/6 SDIを選択する >> 次へ
3 2/6 次へ
4 3/6 次へ
5 4/6 チェックをすべてはずす(はずさなくてもOK) >> 次へ
6 5/6 次へ
7 6/6 Viewを選択すると、基本クラスの選択が出来るようになるのでそこでCFormViewを選択します。
これでSDI上でボタンを扱えるようになりました。
--------------------------------------------------------------------------------
次はファイルを選択して読み込みます。
読み込むべきファイルは、背景CGのパーツが入ったビットマップとパーツの並びの入っているマップデータです。
読み込みはメニューを開いてファイルを選択します。
ファイル名は何でもかまいませんが、マップファイルの拡張子には map を使う事にします。
メニューの出し方とハンドルの作り方は
1 リソースビューを開いてMenuを選択します
2 IDR_MAINFRAMEを選択するとメニューのウィンドウが出るので
メニューのファイルとヘルプ以外の項目を消します(消さなくてもOK)
項目の上で右クリックして切り取りを選択すると消えます。
3 ファイルをクリックするとプルダウンメニューが出るので新規作成をダブルクリックして
選択します。
するとメニューアイテムプロパティダイアログが開くので
キャプションを「マップファイルの読み込み」に変更します。
これでファイルのプルダウンメニューはマップファイルの読み込みがでます。
同じようにマップファイルの書き込み、パーツCGの読み込みを作ります。
項目は
1 マップファイルの読み込み
2 マップファイルの書き込み
3 パーツCGの読み込み
4 アプリケーションの終了
の4つがあればOKです。
項目の追加をしたい場合はプルダウンメニューの最後の空白をクリックして追加
します。
途中に項目を割り込ませる方法はわかりません。
これでファイルをどうしたいかという選択が出来ました。
--------------------------------------------------------------------------------
つぎは具体的にファイルを選択してメモリに読み込む方法です。
マップファイルはマップ用の配列を用意してそこに読み込みます。
パーツ用CGはビットマップを用意してそこに読み込みます。
このパーツ用CGはゲームで使うときにリソースで読み込むやつです。
さっきのプルダウンメニューをもう一度開きます。
マップファイルの読み込みをダブルクリックするとメニューアイテムプロパティ
ダイアログが開きます。
IDが「ID_FIEL_NEW」になってますか?
もともとは新規作成だったのでこれになっているはずです。
このIDは、ユニークな名前(他に使っていない名前)なら変更してもOK
説明のためここではそのままにしておきます。
ではメニューアイテムプロパティを閉じましょう。
マップファイルの読み込みのIDがわかったところで
そのハンドルを作成して実際のファイル読み込みを作ります。
クラスウィザードを立ち上げるてオブジェクトウィンドウを見ると
「ID_FILE_NEW」があるのでそれをクリックすると
メッセージに
COMMAND
UPDATE_COMMAND_UI
が出るので
COMMANDを選択してから関数の追加をクリック、
メンバ関数名のダイアログが出るのでOKを押す。
メンバ関数名はやはりユニークな名前なら変更しても良し。
メンバ関数のダイアログが閉じたらコードの編集をクリックすると
ハンドルが出来ています。
忘れないうちにこのハンドルがメニューのマップファイルの読み込みで
あるというコメントを書込んでおきましょう。
ファイルを選択するダイアログ(ファイル選択ダイアログ)を出してファイルを選択します。
//----- 開くファイルを選択する
CFileDialog dlg( TRUE, // TRUE=読み込み FALSE=書き込み
"map", // デフォルトファイル拡張子
"mapdata", // デフォルトファイル名
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, // オープンフラグ
"Map Files (*.bmp)|*.bmp|All Files (*.*)|*.*||", // フィルタ
NULL); // 親ハンドル
if(dlg.DoModal() != IDOK)return; // ファイルを選択していない or キャンセルの場合は帰る
これで dlg.GetPathName() で選択したファイル名を取得できます。
キャンセルしたり、ファイルを選択しないでダイアログを徒じたりした場合は最後の
行で判断してリターンします。
ファイルの読み込みはバイナリファイルの読み込みでやったように読み込みます。
--------------------------------------------------------------------------------
ここでマップファイルを読み込んでメモリに保存し、パーツデータ
(これも読み込む必要がある)を使って表示されている部分(マップ全体は
大きすぎて表示できないから)を画面に書きます。
この部分をマッピング部と呼ぶことにしましょう。
しかし、ここで注意が必要です。
マップエディタウィンドウにいきなり画面にかいてみると、再描画されません。
つまり他のウィンドウに隠れてから再び前に出たときに画面の中が壊れたままになってしまいます。
これはOnDrawの中で描画しないと自動的に再描画されないからですね。
したがって、ここで描画するのはメモリ上のビットマップにします。
OnDrawでは、このビットマップをウィンドウに(pDCに)書込むように
するとGoodな訳です。
マップだけでなく、パーツ、選んだパーツも同じようにメモリ上にビットマップ
を作成しておきます。
このメモリ上のビットマップの事をバッファといいます。
スピードを要求されるゲームで使う場合は背景1枚、キャラクタ1枚、背景とキャラクタ
を合成したもの2枚用意します。
合成した画面を2枚もつのは交互に表示するためでこれをダブルバッファと呼びます。
--------------------------------------------------------------------------------
ファイルの読み書きはOKですがよくよく考えるとMAPデータがどういう構造になっているか
決めないとファイルを書込むことができないじゃありませんか?
そのとおりですね。
では上のコードを実行するまえにデータ構造を決めましょう。
データは16ビット(WORD)データを使います。中味は
0000−0000−0000−0000
0000−000 上位7ビットアトリビュート領域
0−0000−0000下位9ビットパーツ番号
というようにします。
パーツ番号は0x1FFまで使えるので最大512個持てる訳です。
実際はシーンごとに使うパーツも変わると考えるとそんなにたくさん必要ないかも
しれません。
マップを作るときに使う配列は、動的に確保することにします。
動的確保というのは使うときに新しくメモリを確保し、いらなくなったら破棄
する、という使い方です。
いままで普通に使っていた int Map[10][20];
は自動変数と呼びます。
これは事実上の動的確保をしているのですが、
int Map[4][5]={
{......};
の様に中に実際に値をいれるとそれはアプリケーションが動作している期間はずっと
メモリ上にある訳です。
動的に確保するためには
int *pX = new int[400]; // int 型の400個の配列を確保する
というふうに書きます。
必要なくなったら
derete [] pX; でメモリを解放します。
これで確保したメモリはたんなる1列の配列なのでアクセスするときは
*(pX + (y * width) + x)
でx、yの2次元としてアクセスします。
--------------------------------------------------------------------------------
* マウスをクリックした位置の取得
マウスをクリックした位置を取得するには、まず、マウスクリックのイベントハンドル
を作った後、マウスカーソルの位置を取得する、という方法を使います。
マウスクリックのハンドルを作成するには
1 クラスウィザードを起動する
2 クラス名のところを、マウスクリックを受け取るクラス(ここではVIEW)
にして、メッセージのWM_LBUTTONDOWNを
ダブルクリックする(タイマや、キーハンドルを作のと同じ手順)
3 メンバ関数にON_WM_LBUTTONDOWNができているので
そこをクリックするとハンドルのコードが出来ます。
そこでマウスカーソルの位置を取得すると、クリックした位置がわかるわけです。
マウスカーソルの位置を取得するコードは
GetCursorPos(&point); // マウスクリックの位置を取得する
ScreenToClient(&point);
// point.x << X位置
// point.y << Y位置
マウスクリックした場合、それがパーツウィンドウの場合はパーツを選択し、
マッピングウィンドウの場合は選択したパーツを配置する、という動作を
行う。
--------------------------------------------------------------------------------
* パーツとパーツを置いたCGの表示
パーツをえらんだら選んだパーツをパーツウィンドウの上に表示します。
パーツをマッピングしたら、それを反映してマッピング部を更新します。
CGを表示するのに場所を移動しなければいけませんが、ここではスクロールバー
を使ってみます。 これですね。
スクロールバーの使い方参照
ついでに初めてのコントロールを使うときにどういう手順で使い方を知るかを
説明します。
スクロールバーというモノがある、という知識は必要です。
大体ウィンドウズのアプリで使われている部品はたいていそろっていると
考えてよいでしょう。
で、コントロールのウィンドウに無ければパレットに乗っていないコントロールを追加します。
プロジェクト >> プロジェクトに追加 >> コンポーネントおよびコントロール >>
DeveloperStudioComponentを選択します。
中にあったらめっけもの。
次にヘルプを開きます。
ある程度プログラムをやっていると、スクロールバーはきっとつまみの位置を持っていて
それを取得するメンバ関数がある、と見当が付きます。
始めはキーワードを使います。
キーワードで出ない場合や、サンプルコードがほしい場合に検索を使います。
ヘルプでスクロールバーですが、たいていの場合はクラスになっているので頭にCを
つけて
CScrollBar と入力します。
すると、いくつか項目が出てくるので、そのなかの クラスメンバを選びます。
コントロールの持っている機能は、ここから見渡すことが出来ます。
そこを選択すると、予想どおり、
GetScrollPos スクロール ボックスの現在位置を取得します。
というのがありました。
そのほかにも使えそうな機能がありますね。
個々のメンバ関数の使い方はそのリンクをクリックすればわかりますね。
--------------------------------------------------------------------------------
ではスクロールバーをつけてみます。
コントロールからスクロールバーの水平、垂直を選んで一つづつFormに
置きます。
下と右側にしましょう。
このような部品を配置するのはなかなか難しいものです。微妙な位置を決定できません。そんなときはコントロールを選択してカーソルキーでちょっとづつ動かします。コントロールキーを押しながらカーソルキーを押すとサイズがちょっとづつ変わるのでちょうど良いサイズにします。
クラスウィザードを起動してIDC_SCROLLBAR1、2がある事を
確認します。
1を選んでメンバ変数を追加します。
メンバ変数名は、m_Scb1にしましょう。
たての方は m_Scb2 にします。
カテゴリは、両方ともコントロールを選択します。
OKを押します。
--------------------------------------------------------------------------------
* 保存
マッピングが終わる、あるいは中断するときにはファイル保存ダイアログを
開いて出来たマップファイルを保存すればマップエディタが完成です。
動作見本