ExcelVBAゲームプログラミング?

初心者でもきっとできる!
Excelさえ持っていれば特別なソフトは不要!
すぐにでも始められる簡単ゲームプログラミング!
今すぐ始めよう!

サンプルやゲームのダウンロードができる別館も好評運営中です。
ご意見やご質問、ゲームの感想等は掲示板までお気軽に。是非、皆さんの声を聞かせてください。運営、開発の励みになります。



各種ダウンロードはコチラ ↓ 意見・感想・質問はコチラ ↓
影倉庫 Shadow warehouse サポート掲示板
ブログの全体像はコチラ ↓ リンクのページはコチラ ↓
サイトマップ 自分本位なリンク


スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。






Chapter.134 [ APIによる描画処理6:図形描画実践編 ]

■前回のおさらい

API による図形描画処理も、いよいよ本丸に近づいてきました。
前回は準備編と題して、標準モジュールに記述するコードを解説しましたが、様々な API の宣言を始め、なかなかわかりにくい内容だったのではないかと思います。
今回はフォームモジュールに記述するコードを解説することになりますが、前回の内容がきちんと理解できていれば、さほど難しくはないと思います。API を使う場合に常に言えることですが、API は扱いが難しいというより面倒であることが多いです。とにかく複雑というか、独特のルールに従って正しく記述を行なわないと動作しないわけです。

今回は、図形描画系の代表的な API である矩形・円形・直線を描画するそれぞれの API について詳しく解説します。文章量というか、コードの文量は前回と比較すればはるかに少なくなりますので、じっくり読み解いていただければと思います。

それではさっそく、フォームモジュールに記述するコードを見ていきましょう。


■ユーザーフォームの起動と終了

ユーザーフォームの起動と終了に、それぞれ処理を行なうようにコードを記述します。

VBA で利用できるユーザーフォームは、起動と同時にいくつかのイベントを発生させますが、今回はユーザーフォームが起動しアクティブになった瞬間に発生するイベント、Activate イベントを使います。

Private Sub UserForm_Activate()

    get_handle Me.Caption

End Sub

ユーザーフォームの起動関連イベントには、Activate 以外にも、もうひとつ Initialize がありますね。ただし今回は、絶対に Activate イベントを利用する必要があります。その理由、想像がつくでしょうか。

Initialize イベントは、実際にユーザーフォームが表示される前の段階、つまり、メモリ上にユーザーフォームのデータが展開される段階で発生します。ユーザーフォームの Initialize イベント中にコードを中断してみればわかりますが、イベントが発生した段階ではまだユーザーフォームは画面上に表示されていないわけです。

一方で、Activate イベントが発生するタイミングは、ユーザーフォームが表示されてから、実際にアクティブになったその瞬間です。画面上には既にユーザーフォームが表示されているわけですから、その点が Initialize イベントとは異なります。

先ほどのコードは、前回解説したウィンドウハンドルを取得するためのプロシージャを呼び出しているだけの簡素なものですが、肝心のユーザーフォームが表示されていない状態でウィンドウハンドルを取得しようとしても、うまくいくはずがないですよね。
ユーザーフォーム、つまりウィンドウが画面上にしっかり表示されてからハンドル取得の処理が実行されるようにするわけですから、あえて Activate イベントを使う必要があるのですね。

さて、続いてユーザーフォームの終了時の処理です。

Private Sub UserForm_Terminate()

    release_obj

End Sub

終了時の処理も、前回解説した終了処理プロシージャを呼び出しているだけですので、コードの量は非常に少ないです。呼び出しているプロシージャが実際にどんな処理をしているのかは、前回の記事を参照していただければと思います。


■やっと描画処理

さて、前回も含めて、長い長い前置きになりましたが、やっと実際の描画処理を行なっている部分に到達です。

以下に掲載するコードはユーザーフォームのクリックイベントを利用して、いくつかの図形を API で描画しているものです。一見すると何をやっているのかわからないかもしれませんが、そこはしっかり解説しますので、まずはコードを見てみましょう。

Private Sub UserForm_Click()

    Dim pen As Long
    Dim brush As Long
    Dim lpPoint As POINTAPI
    
    pen = p_create(1, PS_SOLID, RGB(255, 0, 0)) '①
    brush = b_create(BS_SOLID, RGB(255, 255, 0), 0) '②
    dPen = SelectObject(hdc, pen) '③
    dBrush = SelectObject(hdc, brush) '④
    Rectangle hdc, 10, 10, 60, 60 '⑤
    Ellipse hdc, 70, 10, 120, 60 '⑥
    MoveToEx hdc, 130, 35, lpPoint '⑦
    LineTo hdc, 180, 35 '⑧

End Sub

上から順番に見ていきます。

まず、変数の宣言ですが、このプロシージャのなかではみっつの変数を宣言しています。変数 pen と、変数 brush は、その名の通りペンとブラシのハンドルを格納するために使います。わかりにくいのは最後に宣言されている lpPoint という変数だと思いますが、こちらはエラー回避のために後々利用することになりますので、そこで詳しく解説します。

①と②は、前回解説した自前のオブジェクト生成プロシージャですね。
①では、ペンオブジェクトを生成していますが、今回の引数の指定では、1 ピクセルの赤い実線を指定してペンを生成しています。変数 pen に、生成されたペンオブジェクトのハンドルが入ります。
②はブラシですね。通常の塗り潰しブラシ(ソリッドブラシ)で、色は黄色です。変数 brush にブラシオブジェクトのハンドルが入りますね。

続いて③と④ですが、ここではデバイスコンテキストにオブジェクトを割り当てています。イメージとしては、ペンとブラシを持ち替えさせている感じですね。
前回の講座でも詳しく解説しましたが、SelectObject 関数はデバイスコンテキストにオブジェクトを選択させる機能があり、戻り値として、それまで選択されていたオブジェクトのハンドルを返してきます。重要なのは、プログラムが終了する最後の最後、プログラム内部で生成したオブジェクトを削除する処理を行なう際、一番最初にデバイスコンテキストがデフォルトで持っていたオブジェクトのハンドルが必要になる点です。なぜデフォルトのオブジェクトのハンドルが必要なのかと言えば、削除しようとするオブジェクトがデバイスコンテキストに選択状態になっていると、エラーが起こる可能性があるからです。
今回のプログラムでは、ペンとブラシをそれぞれひとつずつ生成していますが、これらのオブジェクトを削除する際には、デバイスコンテキストに別のオブジェクトが選択されている必要があります。このときに、デフォルトのオブジェクトのハンドルが必要になるのですね。
③と④の処理では、それぞれデフォルトのオブジェクトのハンドルを、変数 dPen と変数 dBrush に保持するように処理しています。このふたつの変数は、標準モジュールのほうで Public な変数として宣言してありますので、最後の終了処理で活用することができます。

さて、続いては⑤です。ここでは、Rectangle 関数という API が登場していますね。この関数の詳細は前回詳しく解説していますが、要するに矩形を描画する API です。
同様に⑥では Ellipse 関数という API を使っています。これは円(楕円)を描画する API ですね。

⑦はどうでしょうか。ここでは、MoveToEx 関数が登場していますね。この関数は現在の原点を設定する API で、直線を描画しようとする際などによく登場します。このあと出てくる⑧の LineTo 関数という API は、直線を描画することができる API ですが、この API は引かれる直線の終点しか指定することができません。直線の始点は、現在の原点と同義です。つまり、⑦の MoveToEx で直線の始点(現在の原点)を設定し、⑧の LineTo で直線の終点を指定するわけです。
直線の描画は、このように若干面倒な処理手順になりますが、こればかりは API の仕様なので、諦めるより他ありません。

補足コラム:MoveToEx関数の不思議
もうひとつ、⑦の部分でひとつ不可解な部分があるかと思います。
MoveToEx関数の第三引数で、lpPointという変数を使っていますね。しかし、lpPointという変数が、どこかで別の処理に使われている形跡はありませんね。一見すると、どうしてこの変数が必要なのか、よくわからないと思います。
実は、MoveToEx関数には、現在の原点を指定する機能のほかに、それまでの原点を調査する機能もあります。その際に利用されるのが、問題の第三引数、つまり今回の例で言えば変数lpPointがあてがわれている部分なのです。
MoveToEx関数は、原点を設定する際に、それまでの原点の座標を第三引数で指定されたPOINTAPI型の変数に格納してくれます。引数として与えた変数の内容が、関数によって実行と同時に書き換えられるわけです。
VBAを使っていると、関数が返すデータはイコールを使って代入するやり方が全てのように考えてしまいがちですが、APIでは、今回のMoveToEx関数のように引数から受け取った参照先にデータを格納する仕様が多く見られます。最初は理解しにくいかもしれませんが、今後、APIプログラミングやDirectXを用いた処理に本格的に取り組もうと考えているなら、このような仕様にも慣れておく必要があります。それほど、この手の仕様は当たり前で一般的なものです。
少しだけ難しい話をすれば、引数として変数が渡されるとき、それが参照渡しされていることが重要です。そうでなければ、MoveToEx関数が変数の中身に干渉することができなくなってしまうからですね。


さて、少し長くなりましたが、簡単にまとめてみましょう。
①と②で、ペンとブラシを生成しました。
③と④で、生成したオブジェクトをデバイスコンテキストに選択させました。
⑤では、矩形を描画。⑥では、円を描画しています。そして⑦と⑧の合わせ技で、直線を描画しています。

つまり、この一連の処理が実行されると、ユーザーフォーム上にはふたつの図形と直線が描画されます。このプロシージャはユーザーフォームのクリックイベントプロシージャですので、ユーザーフォーム上をクリックしたときに実行されます。

実行されたときのイメージは、以下のような感じですね。

・ユーザーフォームが表示される。
790.png



・クライアント領域をクリックすると図形が描画される。
791.png


■ペンやブラシをいろいろ変化させてみる

さて、ここからはオマケになりますが、ペンやブラシを生成する際に、いろいろ引数を変化させるとどんなことが起こるのか、いくつか例を挙げてみたいと思います。
ここでは、自作のプロシージャである p_create b_create に、それぞれどのような引数を与えたのか併せて掲載しますので、参考にしてみてください。


792.png

pen = p_create(1, PS_SOLID, RGB(0, 255, 0))
brush = b_create(BS_HATCHED, RGB(0, 0, 255), HS_VERTICAL)
・1ピクセル、実線、緑のペン
・縦線ハッチ、青のブラシ



793.png

pen = p_create(1, PS_DASHDOT, RGB(125, 0, 125))
brush = b_create(BS_HATCHED, RGB(0, 125, 125), HS_DIAGCROSS)
・1ピクセル、一点鎖線、濃い紫のペン
・斜め交差線ハッチ、青緑のブラシ



794.png

pen = p_create(3, PS_SOLID, RGB(125, 125, 0))
brush = b_create(BS_HOLLOW, 0, 0)
・3ピクセル、実線、黄土色のペン
・塗り潰し無しブラシ



このような感じで、いろいろとパラメーターを変化させてみることで、ある程度多彩な表現ができることがわかると思います。
ただ、このようなペンやブラシによる表現は、言い方は悪いですがそれほど表現豊かなものとはお世辞にも言えません。それに、実際にやってみるとわかりますが、実線以外のペンを生成する際、ペンの太さを 1 ピクセルよりも太くした場合に、思ったとおりの表現にならないケースも存在します。やはり、この辺は融通が利かないので、若干使いにくく感じてしまうと思います。

また、よーく観察するとわかりますが、ハッチブラシを使っている場合や、実線以外の線形を指定したペンで描画した場合などに、不思議な現象が起こっていることに気が付いたでしょうか。


793.png


この画像、特に一番右側の直線(一点鎖線)が引かれているところをよく見てください。線と線の切れ目のところは、背景となるユーザーフォームの色が透けているのではなく、白い色になってしまっているのがわかるでしょうか。
同様に、ハッチブラシの隙間の部分も、真っ白になってしまっていますね。

実は、これも API を使った図形や線の描画のやっかいな部分で、デバイスコンテキストの背景色が原因で、このようなことが起こってしまっているのです。
デバイスコンテキストには、背景色という概念があり、点線の隙間の部分や、ハッチブラシの隙間の部分などにはこの背景色が描画されるようになっています。もちろん、API を用いれば背景色に関する挙動についても変更することは可能なのですが、それはまた別の機会に、おいおい説明していくことにしましょう。


■まとめ

さて、簡単な図形を二・三描画するだけなのに、随分と長いテキストになってしまいましたね。何度も書いていることではありますが、本当に API 関連の処理は冗長になりがちですし、なんともわかりにくいので説明も比例して冗長になってしまいます。

しかし、ここはあえて考え方を変えてみましょう。
本当のことを言ってしまえば、いかに、普段何気なく使っている VBA が使いやすい言語であるか、ということなんですね。本来のウィンドウズプログラミングとは、このような複雑で面倒なものなのです。API を用いるということは、それなりに敷居が高いことですし、当然のように高いレベルの知識を要求されます。難しいからと投げ出してしまうのか、それとも難しいなりに理解しようと努力するのか。ここは正直な話、プログラマひとりひとりの、それぞれの個人の自由なのです。

多少難しいですが、全く理解できないほど強烈に難解である……というものでもないと私は思っています。それに、誰にでも、できる限り理解しやすいように、講座のテキストは書いているつもりです。せっかく VBA から API を利用することができるのですから、少しだけ我慢して、習得してしまうことを私はオススメします。

この API プログラミングという世界の先に、DirectX プログラミングというさらに果てしない可能性が広がっているなら、尚更です。こんなところで挫折してしまうのは、なんとも勿体無い。それに、厳しいことを言ってしまえば、このレベルの話についてこれないようでは、DirectX を扱うのは 無理 です。高みを目指すのならば、このような比較的簡単な API による描画処理などは、小さなステップのひとつとして乗り越えてほしいと思います。
前回と今回の講座で解説したコードをそのまま実装したサンプルファイルを、別館にアップしておきます。参考にするなり、パラメータをいろいろ変更して実験してみるなり、活用していただければと思います。

ゆっくりでいいのです。
焦らず、じっくり、取り組んでいきましょう。

サンプルダウンロード ⇒ コチラよりダウンロードできます。(別館)



■格言

イベントの発生順序に注意してコードを記述
API 独特の引数の使い方を理解する


簡単な図形を描画するだけでも大変ですが、こんなのは API 描画処理の序の口です。


関連記事






Comment

Name
E-mail
URL
Comment
Pass  *
Secret? (管理者にだけ表示)

メールフォーム

影斬に物申すという方はこちら

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

可能な限り要望には応えますが、必ず返信や回答ができることを、保障するものではありません。
ご了承ください。

Chapters

コンテンツ一覧


■Chapter 一覧■
    全てのChapterの一覧です。
    直接アクセスしたい方はこちらをご利用下さい。

    Chapter.1 [ 知っておくべき心得 ]
    Chapter.2 [ Excelってなんだろう ]
    Chapter.3 [ Excelの基本画面 ]
    Chapter.4 [ VBAとは? ]
    Chapter.5 [ モジュールについて ]
    Chapter.6 [ 変数 ]
    Chapter.7 [ 変数の型と宣言 ]
    Chapter.8 [ プロシージャとスコープ ]
    Chapter.9 [ ゲームつくる様々な手法 ]
    Chapter.10 [ ユーザーフォーム ]
    Chapter.11 [ プロパティウィンドウ ]
    Chapter.12 [ 乱数 ]
    Chapter.13 [ 条件分岐 ]
    Chapter.14 [ ゲーム画面のデザイン ]
    Chapter.15 [ コード記述の基本作法 ]
    Chapter.16 [ じゃんけんゲーム:1 名前をつける ]
    Chapter.17 [ じゃんけんゲーム:2 フォームの起動 ]
    Chapter.18 [ じゃんけんゲーム:3 乱数の種 ]
    Chapter.19 [ じゃんけんゲーム:4 イベント ]
    Chapter.20 [ じゃんけんゲーム:5 引数 ]
    Chapter.21 [ じゃんけんゲーム:6 役判定 ]
    Chapter.22 [ じゃんけんゲーム:7 予測と制限 ]
    Chapter.23 [ Withステートメント ]
    Chapter.24 [ 画像を表示させる ]
    Chapter.25 [ 画像表示の発展形 ]
    Chapter.26 [ 繰り返し処理 For文 ]
    Chapter.27 [ 繰り返し処理 Do~Loop文 ]
    Chapter.28 [ Exitステートメント ]
    Chapter.29 [ フォーム上の位置情報 ]
    Chapter.30 [ API基礎知識 ]
    Chapter.31 [ API補足知識 ]
    Chapter.32 [ メインループを考える ]
    Chapter.33 [ 同期処理の概念 ]
    Chapter.34 [ 移動処理その1:画面設定と考え方 ]
    Chapter.35 [ 移動処理その2:DoEvents ]
    Chapter.36 [ 移動処理その3:キー入力判定API ]
    Chapter.37 [ 条件分岐のさらなる探求 Select Case ]
    Chapter.38 [ アニメーション ]
    Chapter.39 [ 配列変数 ]
    Chapter.40 [ ゲームの初期化 ]
    Chapter.41 [ シューティングゲーム1:ゲーム設計 ]
    Chapter.42 [ シューティングゲーム2:メインプロセス ]
    Chapter.43 [ シューティングゲーム3:構造体 ]
    Chapter.44 [ シューティングゲーム4:定数 ]
    Chapter.45 [ シューティングゲーム5:プレイヤーキャラクター ]
    Chapter.46 [ シューティングゲーム6:ショットを撃つ① ]
    Chapter.47 [ シューティングゲーム7:ショットを撃つ② ]
    Chapter.48 [ シューティングゲーム8:Mod演算子の活用 ]
    Chapter.49 [ シューティングゲーム9:敵キャラクター登場 ]
    Chapter.50 [ シューティングゲーム10:衝突判定 ]
    Chapter.51 [ シューティングゲーム11:衝突の実体 ]
    Chapter.52 [ シューティングゲーム12:敵の攻撃 ]
    Chapter.53 [ シューティングゲーム13:爆発エフェクト ]
    Chapter.54 [ シューティングゲーム14:残機数表示① ]
    Chapter.55 [ シューティングゲーム15:残機数表示② ]
    Chapter.56 [ シューティングゲーム16:スコアの表示 ]
    Chapter.57 [ シューティングゲーム17:タイトル画面 ]
    Chapter.58 [ シューティングゲーム18:ボスキャラクター ]
    Chapter.59 [ シューティングゲーム19:最後の仕上げへ ]
    Chapter.60 [ シューティングゲーム20:いよいよ完成STG ]
    Chapter.61 [ カードゲームで使えるめくり効果 ]
    Chapter.62 [ ラジアンと角度 ]
    Chapter.63 [ ラジアンの活用:円運動 ]
    Chapter.64 [ ラジアンの活用:任意の角度へ移動する ]
    Chapter.65 [ APIによるサウンド再生:基礎 ]
    Chapter.66 [ APIによるサウンド再生:MIDIと多重再生 ]
    Chapter.67 [ APIによるサウンド再生:MCIコマンドとループ再生 ]
    Chapter.68 [ Function プロシージャ ]
    Chapter.69 [ 値渡しと参照渡し ]
    Chapter.70 [ デバッグ1:イミディエイトウィンドウ ]
    Chapter.71 [ デバッグ2:ローカルウィンドウ ]
    Chapter.72 [ デバッグ3:コード実行の中断 ]
    Chapter.73 [ オブジェクトってなんだ ]
    Chapter.74 [ プロパティ・メソッド・イベント ]
    Chapter.75 [ オブジェクト変数 ]
    Chapter.76 [ オブジェクトとコレクション ]
    Chapter.77 [ 特殊な繰り返し:For Each ]
    Chapter.78 [ エラー処理 ]
    Chapter.79 [ On Error と GoTo文 ]
    Chapter.80 [ Resumeステートメント ]
    Chapter.81 [ バイトとビット ]
    Chapter.82 [ ウィンドウメッセージとイベント ]
    Chapter.83 [ 文字列の基礎 ]
    Chapter.84 [ 文字列操作① ]
    Chapter.85 [ 文字列操作② ]
    Chapter.86 [ タイピングゲーム1:仕様を決める ]
    Chapter.87 [ タイピングゲーム2:キー入力検知 ]
    Chapter.88 [ タイピングゲーム3:文字列照合 ]
    Chapter.89 [ タイピングゲーム4:判定関数 ]
    Chapter.90 [ タイピングゲーム5:ゲーム画面設計 ]
    Chapter.91 [ タイピングゲーム6:問題文のソート ]
    Chapter.92 [ タイピングゲーム7:動的配列 ]
    Chapter.93 [ タイピングゲーム8:キーダウンイベント ]
    Chapter.94 [ タイピングゲーム9:正打数の表示 ]
    Chapter.95 [ タイピングゲーム10:タイムの表示 ]
    Chapter.96 [ クリックゲーム1:イベントの種類 ]
    Chapter.97 [ クリックゲーム2:画面設計 ]
    Chapter.98 [ クリックゲーム3:クリック座標検知 ]
    Chapter.99 [ クリックゲーム4:キャラクター準備 ]
    Chapter.100 [ クリックゲーム5:キャラクターの配置 ]
    Chapter.101 [ クリックゲーム6:キャラクター移動とNot演算子 ]
    Chapter.102 [ クリックゲーム7:クリックのヒット判定 ]
    Chapter.103 [ クリックゲーム8:ヒットマークエフェクト ]
    Chapter.104 [ クリックゲーム9:サウンド処理の実装 ]
    Chapter.105 [ クリックゲーム10:マウスカーソルの変更 ]
    Chapter.106 [ ブロック崩しゲーム1:仕様と概要を決める ]
    Chapter.107 [ ブロック崩しゲーム2:基本概念の確認 ]
    Chapter.108 [ ブロック崩しゲーム3:ベクトルとは ]
    Chapter.109 [ ブロック崩しゲーム4:変数や定数の宣言 ]
    Chapter.110 [ ブロック崩しゲーム5:初期化処理の実装 ]
    Chapter.111 [ ブロック崩しゲーム6:ブロックの配置 ]
    Chapter.112 [ ブロック崩しゲーム7:根幹処理とバーの処理 ]
    Chapter.113 [ ブロック崩しゲーム8:線分と線分の交差を判定 ]
    Chapter.114 [ ブロック崩しゲーム9:線分同士の交点 ]
    Chapter.115 [ ブロック崩しゲーム10:ボールの処理 ]
    Chapter.116 [ ブロック崩しゲーム11:最終調整して完成へ ]
    Chapter.117 [ テキストファイル操作基礎 ]
    Chapter.118 [ テキストファイル操作:読み込み編 ]
    Chapter.119 [ テキストファイル操作:CSV読み込み編 ]
    Chapter.120 [ テキストファイル操作:様々な読込編 ]
    Chapter.121 [ テキストファイル操作:バイナリ編 ]
    Chapter.122 [ テキストファイル操作:暗号化編 ]
    Chapter.123 [ テキストファイル操作:復号化編 ]
    Chapter.124 [ クラスモジュールとは ]
    Chapter.125 [ クラスモジュール:メソッド編 ]
    Chapter.126 [ クラスモジュール:プロパティ編 ]
    Chapter.127 [ クラスモジュール:イベント拡張編 ]
    Chapter.128 [ クラスモジュール:イベント自作編 ]
    Chapter.129 [ APIによる描画処理1:ハンドル ]
    Chapter.130 [ APIによる描画処理2:デバイスコンテキスト ]
    Chapter.131 [ APIによる描画処理3:ペン オブジェクト ]
    Chapter.132 [ APIによる描画処理4:ブラシ オブジェクト ]
    Chapter.133 [ APIによる描画処理5:図形描画準備編 ]
    Chapter.134 [ APIによる描画処理6:図形描画実践編 ]
    Chapter.135 [ APIによる描画処理7:画像描画の仕組み編 ]
    Chapter.136 [ APIによる描画処理8:ビットブロック転送編 ]
    Chapter.137 [ APIによる描画処理9:ラスタオペレーション ]
    Chapter.138 [ APIによる描画処理10:マスク描画 概念編 ]
    Chapter.139 [ APIによる描画処理11:マスク描画 実践編 ]


    コードやVBAに関する質問などはサポート掲示板(別館)までお気軽にどうぞ。




fc2 seotool Excel VBA ゲーム プログラミング 講座

Counter

twitter


Shadow BBS - 影掲示板

VBA 関連書籍



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。