Visual Basic 中学校 投稿プログラム |
水着!カムバック!
投稿者:y4yamaさん
水着!カムバック!はいろいろな形のブロックを組み合わせて三角形の水着を組み立てるゲームです。
細かいところまで作りこまれており単体でゲームとして楽しむこともできます。プログラムもいろいろなテクニックが満載されており大いに参考になります。
ダウンロード
TestTri.lzh(48.4KB)
VB2005で作成されたソースコード。プログラマ向けです。 ソースコードを編集するためにはVB2005が必要です。Express Editionでも大丈夫です。 改変したプログラムの公開は可能ですが、末尾にあるメッセージの趣旨に従ってください。
ダウンロード TestTriExe.lzh(34.0KB) exeファイルのみ。遊んで見たい人向けです。実行するには.NET Framework 2.0以上が必要です。 |
V太:博士!ゲーム が届きました!
■画像1
B子:あれ?なんか、私が出ているわ。
なになに…。弟がばらばらにしたお姉さんの水着を元に戻すと言うパズルゲーム…?!ひょっとして、ぼくが弟で、B子ちゃんがお姉さんなのかな?
あらあら。面白い設定ね。
要するにバラバラの部品を三角形にうまくあてはめていけばいいみたいだね。ドラッグによる移動も快適だし、右ドラッグだと図形の回転もできるのか。ずいぶんスムーズに動くなぁ。
開始時の三角形がバラバラになるアニメーションもよくできているわ。
■画像2
それにしても難しい…。
■画像3
最初の画面でいろいろと設定できるから、はじめは簡単な設定にするといいわね。特に「駒の回転なし」をチェックするかどうかは大きな違いだわ。
ほら、できたわ。
■画像4
むむ。B子、やるな。
パステルカラーがきれいだし、細かいところもよくできているわね。遊んでいるだけなら気楽でいいけど、実際にこれをプログラムしようと思ったらちょっとすぐにはできないわね。
こ、これは、ついに噂のDirectX登場ですか?!
博士:いやいや。このゲームは標準のVBの機能だけでつくってあるぞ。DirectXなどは一切使っておらん。
そういうことなら、プログラムを盗むぞ〜。どう言う仕組みになっているのかForm1を開いて見よう。
あれ?Form1にはPictureBoxとかがほとんど張り付いていないわね。あのたくさんの図形はどうやって表示しているのかしら。
ふむ。このゲームではたくさんの図形をすべてグラフィックスメソッドによって描画しておる。VBのグラフィックス機能は性能は低いが、かなりの高機能なのでいちいち図形の数だけコントロールを配置しなくてもいろいろなことができるのじゃ。
そうすると、すべてをプログラムで制御するのかしら?
プログラムの中が…理解不能です。
作者のy4yamaさんの以下のメモ書きも同梱されているわ。
[おおまかな役割] C駒mgr.vb
C駒1.vb 1つの駒についての管理のクラスです C盤面.vb Gridはないので、水着の管理のクラスです Cアニメ.vb アニメ関係をカプセル化Structureを使って、凝った作りになってます Module1.vb 汎用ルーチンは、ここに。
おおまかな役割は以上です。 |
おおまかな役割分担はいいとしても、具体的なところはいきなり見てもわからないかもしれんの。個々の図形はC駒クラスのインスタンスであり、これはC駒mgrクラスの「駒」という配列で管理されておる。
C駒クラスは1つの駒を制御する便利な機能が入っておって、たとえば駒を描くDrawメソッドや駒の背景色を表す駒の色プロパティ、それに回転を行う駒Rotateメソッドなどがある。駒に機能を追加したければC駒クラスを拡張すればよいようになっているのでわかりやすいのぉ。
ちなみにクラス名の先頭を大文字のCにするのはC言語系の伝統じゃ…。VBではあまり見かけんの。
うーん。C駒クラスを拡張すればいいって言われても具体的なところがさっぱりです。
ふむ。C駒クラスは実際の描画機能はGraphicsPathクラスを中心としたVBの機能でプログラムされておる。GraphicsPathは今回のような複雑な図形を表現するのに最適なクラスじゃ。
ぼく知っています。GraphicsPathで指定した形をコントロールのRegionプロパティに設定するとコントロールの形を変えることができるんですよね?
ほぉ、なかなか勉強しておるの。その通りじゃ。じゃが今回はコントロールを使用せずにGraphicsPathの機能だけを最大限に活用しておる。
いきなりじゃと大変じゃから短い例を紹介しよう。
フォームに大き目にPictureBoxを配置して下のコードを貼り付けるだけで試せるので是非実際にやってみて欲しい。
Private path_arr(19) As
Drawing2D.GraphicsPath Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load '▼三角形 '三角形の頂点を定義 Dim Points(2) As Point Points(0) = New Point(100, 0) '上 Points(1) = New Point(0, 173) '左下 Points(2) = New Point(200, 173) '右下 '三角形を生成 path_arr(0) = New Drawing2D.GraphicsPath path_arr(0).AddLines(Points) path_arr(0).CloseFigure() '▼ドーナッツ path_arr(1) = New Drawing2D.GraphicsPath path_arr(1).AddEllipse(200, 200, 100, 100) path_arr(1).AddEllipse(230, 230, 40, 40) path_arr(1).CloseFigure() '描画(=Paintイベントを呼び出す) PictureBox1.Invalidate() End Sub |
Dim
bef_pt As Point
'ドラッグ開始地点。随時更新。 Dim CapturedPath As Drawing2D.GraphicsPath 'ドラッグ中のGraphicsPathオブジェクト Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown 'ドラッグ開始 CapturedPath = 駒を探す(e.Location) bef_pt = e.Location '現在位置を記録 End Sub |
Private Sub PictureBox1_MouseMove(ByVal
sender As Object,
ByVal e As
System.Windows.Forms.MouseEventArgs) Handles
PictureBox1.MouseMove If IsNothing(CapturedPath) Then 'ドラッグ中でなければ何もしない Return End If Dim dx, dy As Single Dim TransMatrix As New Drawing2D.Matrix '移動距離算出 dx = e.X - bef_pt.X : dy = e.Y - bef_pt.Y TransMatrix.Reset() TransMatrix.Translate(dx, dy) CapturedPath.Transform(TransMatrix) '平行移動 bef_pt = e.Location PictureBox1.Invalidate() '描画 End Sub |
Private Sub PictureBox1_MouseUp(ByVal
sender As Object,
ByVal e As
System.Windows.Forms.MouseEventArgs) Handles
PictureBox1.MouseUp 'ドラッグ終了 CapturedPath = Nothing End Sub |
Private Sub PictureBox1_Paint(ByVal
sender As Object,
ByVal e As
System.Windows.Forms.PaintEventArgs) Handles
PictureBox1.Paint For Each Path As Drawing2D.GraphicsPath In path_arr If IsNothing(Path) Then Continue For End If e.Graphics.FillPath(Brushes.BurlyWood, Path) e.Graphics.DrawPath(Pens.Black, Path) Next End Sub |
Private
Function 駒を探す(ByVal
loc1 As Point) As
Drawing2D.GraphicsPath For i As Integer = path_arr.Length - 1 To 0 Step -1 If IsNothing(path_arr(i)) Then Continue For End If If path_arr(i).IsVisible(loc1) Then Return path_arr(i) End If Next Return Nothing End Function |
■リスト1
あんまり、短くないですね…。
この例は「水着!カムバック!」のプログラムに似せてあるがところどころ違いがある。たとえば、 このプログラムでは実際の描画処理はPictureBox1のPaintイベントで行っておるが、「水着!カムバック!」ではdisp_pathメソッド内 から呼び出される各クラスのDrawメソッド内で行っておる。
でも、そんなことしたらウィンドウを最小化したり、何かのウィンドウが上に重なったときに描画した内容が消えてしまうんじゃないかしら?
そこで、「水着!カムバック!」ではメモリ上にビットマップを展開して描画内容が保存されるように工夫してある。この処理はFormのetc初期化メソッド内にある。ただし、上記の例ではこの仕組みは使っておらん。
念のためにその部分を抜粋しておくぞ。
bmp1 = New
Bitmap(PictureBox1.Width, PictureBox1.Height) PictureBox1.Image = bmp1 |
■リスト2
駒を探すメソッドの戻り値も違いますね。たしかにところどころ違いがあるようです。
まぁ、こまかい違いはおいておいてじゃ。良くできている点はGraphicsPathオブジェクトの移動や回転にTransformメソッドを使用している点じゃの。Transformメソッドは図形の移動にたいして非常に強力なアプローチを提供するのじゃ。
でも、上の例では移動しかできないですね。 回転もやってみたいなぁ。
それから、マウスをクリックした位置にGraphicsPathオブジェクトが存在するか確認するのにIsVisibleメソッドを使用している点も便利じゃの。このメソッドがなかったらクリックした位置にGraphicsPathオブジェクトが存在するか調べるのは至難の業じゃ。
この例では三角形とドーナッツ型の2つの図形を生成しているけど、「水着!カムバック!」では大きな三角形をランダムに分割する処理が入っているのね。分割処理も難しそうだわ。
一通り見たところで、今度はこのプログラムの課題に移ろうか。
課題なんてあるんですか? オブジェクト指向的な発想で組み立てられているし、できあがりもとてもきれいに思えますけど。
B子ちゃんはどう思うかの?
うーん。 たしかに構造はしっかりした感じがするんだけど、変数やプロシージャの命名規則がわかりにくいわ。日本語と英語・ローマ字の使い分けに規則性がないからプログラムを見ているとなんだか疲れるわ。
全部日本語にしちゃえばわかりやすくていいね。
うーん。全部日本語だと入力するときにIMEを気にしなければいけなくなるからかえってやりにくいわ。私は列挙体は日本語にすることもあるけどあとは省略やアンダーバーを含まない英語にするわ。
それに単語の先頭を大文字にするプログラマも多いようじゃな。とは言え、命名規則は個人の指向もあるし大きなな問題にはならんじゃろう。
博士はどんな点が気になるんですか?
クラス同士の連携方法がちょっとのぉ。この構造だとForm1(Gameクラス)が他のクラスを制御する主人の役割になる。それは別にいいんじゃが、他のクラス同士で連携するときにForm1の共通変数を経由するような構造になっておるのが気になる。
…と言いますと?
たとえば、C駒.頂点が中にないメソッド内に次のような記述がある。
If Game.盤面.path_mizu.IsVisible(_path.PathPoints(p)) Then |
■リスト3
これはC盤面クラスで定義されているpath_mizu変数へのアクセスなのじゃが、盤面.path_mizuと書くことはせずにGame.盤面.path_mizuと書いてる。
どうして盤面.じゃなくてGame.盤面.になるのかしら?
それはクラス同士が互いに連携できていないからじゃ。もっと言うとクラス同士がお互いのインスタンスを直接参照できないようになっているんじゃ。じゃがVB2005から導入されたフォームの「既定のインスタンス」という性質を使用すればフォームとクラスは簡単に連携できる。それで、クラス同士を連携させるシーンで直接クラスを呼び出すのではなくフォームを経由させるのじゃ。
そのためにForm1をみると、連携する必要がある宣言はPrivateではなくFriendになっておる。
ははぁ。じゃVB.NET2003以前ではこの手法は使用できないんですね?
でも、それのどこがいけないんです?確かにフォームを経由するためにほんのちょっとだけ余分にコードを書く必要はありますけど、それだけじゃないですか?VB.NET2003でも動くようにすべきでしょうか?
その通り。VB2005で作っているのにいちいちVB.NET2003のことを気にする必要はない。それにこのゲームは立派に完成しておるので修正する必要もないじゃろう。
わしが言いたいのはあくまでも勉強のためじゃ。この仕組みだとForm上の変数の定義に必要以上に気を使うことになるし、いろいろなクラスがフォームの変数にアクセスすることになるので、バグがあったときの追跡も大変じゃ。
なにしろ、どこからでもフォームの変数にアクセスする構造なので、「ある変数の値がおかしい」ということになっても、どこでおかしい値をセットしているか調べるの大変になるのじゃ。
じゃあ、どうすればいいんですか?
みんなにはクラス同士が直接連携するための方法を勉強して欲しい。具体的にはプロパティやコンストラクタ・イベント・デリゲートなどクラス同士を連携させるためのさまざまな方法があるんじゃが、話がちょっと長くなるんで初級講座 第49回 イベントの作成の7.クラス間の連携としてのイベントを読んでほしい。
はーい。
それから、アニメーション機能や複雑なロジックについてはあまり触れなかったが、実によくできておるのでにたような機能を実装するときにはこのプログラムが大いに参考になることじゃろう。せっかくだれでもソースプログラムが見られるようになっているのじゃから遠慮なく活用して欲しい。
最後に。さっきの博士のプログラムをもとにクラスを利用したオブジェクト指向風のプログラムを作ってみたのでよかったらダウンロードしてみてください。
ほぉ。こりゃすごいの。クラス化によって機能の追加も簡単になるのぉ。「水着!カムバック」に近づいてきたのじゃ。
■画像5
ワーオ。きれい だわ。これでプログラムもわかりやすくなっているなんてすごいじゃない?どれどれ?
Dim
Polygons As New
PolygonCollection Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim Path As Drawing2D.GraphicsPath '▼三角形 '三角形の頂点を定義 Dim Points(2) As Point Points(0) = New Point(100, 0) '上 Points(1) = New Point(0, 173) '左下 Points(2) = New Point(200, 173) '右下 '三角形を生成 Path = New Drawing2D.GraphicsPath Path.AddLines(Points) Path.CloseFigure() Polygons.Add(New Polygon(Path)) Polygons(0).BackColorBrush = New Drawing2D.LinearGradientBrush(New Point(0, 0), New Point(PictureBox1.Width, PictureBox1.Height), Color.DarkBlue, Color.Violet) '▼ドーナッツ Path = New Drawing2D.GraphicsPath Path.AddEllipse(200, 200, 100, 100) Path.AddEllipse(230, 230, 40, 40) Path.CloseFigure() Polygons.Add(New Polygon(Path)) Polygons(1).BackColorBrush = New Drawing2D.HatchBrush(Drawing2D.HatchStyle.Cross, Color.Red, Color.Pink) '描画(=Paintイベントを呼び出す) PictureBox1.Invalidate() End Sub |
Private Sub PictureBox1_MouseDown(ByVal
sender As Object,
ByVal e As
System.Windows.Forms.MouseEventArgs) Handles
PictureBox1.MouseDown '図形をキャプチャ(捕獲)します。 Polygons.Capture(e.Location) End Sub |
Private Sub PictureBox1_MouseMove(ByVal
sender As Object,
ByVal e As
System.Windows.Forms.MouseEventArgs) Handles
PictureBox1.MouseMove If Polygons.IsCaptured = False Then 'キャプチャ(捕獲)されている図形がない場合は何もしません。 Return End If 'キャプチャ(捕獲)されている図形を移動します。 Polygons.CapturedPolygon.Move(e.Location) PictureBox1.Invalidate() End Sub |
Private Sub PictureBox1_MouseUp(ByVal
sender As Object,
ByVal e As
System.Windows.Forms.MouseEventArgs) Handles
PictureBox1.MouseUp 'キャプチャ(捕獲)されている図形を解放します。 Polygons.Release() End Sub |
Private
Sub PictureBox1_Paint(ByVal
sender As Object,
ByVal e As
System.Windows.Forms.PaintEventArgs) Handles
PictureBox1.Paint 'すべての図形を描画します。 For Each Pol As Polygon In Polygons Pol.Draw(e.Graphics) Next End Sub |
■リスト4
このプログラムでは図形をPolygonというクラスで表現しておる のじゃな。「水着!カムバック」でいうところのC駒クラスじゃな。複数の図形の管理するためのPolygonCollectionクラス というコレクションを作成しておるところはなかなかじゃ。
このコレクションクラスは、「水着!カムバック」でいうところの駒mgrクラスね。
図形の移動はPolygonクラスのMoveメソッド にしました。描画はDrawメソッドです。こうやって図形に関する機能は図形のメソッドを呼び出すことで実現 できるようにしたので、図形は図形のことだけに専念してプログラムすればいいようになるんです。
それに、各メソッドの内容はフォーム側からはブラックボックスになっているのでフォームはフォームのことだけに専念してプログラムすればいいんです。どうです?すばらしいできでしょう?
みんなも完全版をダウンロードしてみてください。
ダウンロード ObjectiveGraphicsPath.lzh(12.0KB)
V太やるわねー。
私はもっとすごいの作ってやるわ!
ふぉふぉふぉ。y4yamaさんにも負けんようにみんなでがんばろうではないか。
今回のプログラムには参考になる面がたくさん含まれておる。やはり実際に動く完成したプログラムを使って勉強するとVBの理解も深まるものじゃ。みんなにもぜひ参考にしてほしい。
投稿していただいた y4yamaさん、すばらしいプログラムを送っていただいて本当にありがとうございます!
今後もみなさんからのプログラムの投稿をお待ちしています。
y4yamaさんからのメッセージにも目を通してみてください。
y4yamaさんからのメッセージ'VB2005の基本は「Visual Basic 中学校」で勉強済みですよネ'でもプログラムを作る気持ちはあっても、なかなかグラフィックはうまく 'いかないし、マウス操作も・・・、と入門時点でつまづくものです 'そこで、初心者のかた向けに、基本的な方向を知る一助になればと 'パズルを作りはじめました。 'でもなかなかVB6(やn88basic,MS-C)のクセ?が抜けないので、VB2005らしいと '言えるかどうか・・。クラスってとっつきにくいデスね。 'で、タイトルは、「水着!カムバック!」 (何でも良かったのですが,ネ〜) 'シチュエーションは、Form2のコメントをお読みください。 ' '遊び方は、後でこじつけたものですから、単純ですが、Randomに発生する駒は 'なかなかのものです。それと、完成の判定は実にシンプルです '三角を2個使うことで、単純なものでも難しいゲームになるもんだなぁ〜 'と後で感心したり・・1個で完成[終わり]でなく、繰り返して完成個数を勝負しよう '・・・とか、アイデアは後から出てきました・・・ ' 'ソフトの改変・発表はご自由にどうぞ。 'ソフトの知的所有権は「Visual Basic 中学校」管理人に帰属するものとします。 '(従って、y4yamaの権利は放棄しています。) 'ソフトを改変した場合の希望は、 'サイト名として、「Visual Basic 中学校」 '原作者の名前として「y4yama」を入れてください。 '全コードは一応、私y4yamaのオリジナルですが、(整数順配列のランダム化(shuffle_arr) 'については流用をしました) 'この手のパズルは世に氾濫してると思いますので、逆に他の商売物の著作権を '侵害しないように気をつけないと・・ ' 'Windows XP SP2, VB2005 Express Edition で作りました。 '確認のため、TestTri.exeだけをコピーして、Windows98SEで動かしたら、 'ロードの時間はかかるし、プログレスバーの逆向きは無理のようですが、 'マウスのドラッグによる駒の動きはなめらかでした。 '------------------ Pic_sail1(リソースのsail3.gif) について
------------------- |
VBやってると、何でもないメモ書きをするときでも先頭に ' をつけちゃいますよね…。