|
|
連載:放課後VB教室
第4回 図形の描画とSP1と「ゆるキャラ」メーカー
羽山 博
2008/10/17 |
|
|
ボタンをクリックしたときに図形を動かすには
へへっ、いいこと考えたっ。このプログラムをうまく使って「ゆるキャラ」作れないかなーって。
「ゆるキャラ」って、「ひこにゃん」とか「いーねくん」みたいなアレ? そんなの作ってどうすんの?
もちろん、一獲千金なのだっ! 目の位置とかを変えて表情を変化させれば新しい「ゆるキャラ」がきっとできるはず。……あ、あれっ? だとすると、自動的に再描画させるんじゃなくて、ボタンをクリックしたときに再描画しなくちゃだ。例えば、ボタンをクリックしたら目玉を右に動かすとか……。
そうだね。そういう場合もあるはずだ。だが、そのときもやはり自分で描くのではなく、イベント・ハンドラに再描画してもらうようにする方がプログラムの構造がシンプルになる。ボタンをクリックしたときに描画のメソッドを呼び出すという方法ではなくて、描画のための適切な値を設定しておいて、Refreshメソッドを呼び出すんだ。Refreshメソッドはコントロールやその子コントロールを再描画するメソッドだ。
単に再描画するだけなら、ボタンのClickイベント・ハンドラに次の1行を書けばいい。
ボタンをクリックするたびに表情を変えたいなら、描画の位置やサイズを変数にしておけばいいね。つまり、ボタンをクリックしたら、それらの変数の値を変更してから、Refreshメソッドを呼び出せばいい。えーと、そうだな、RadioButtonコントロールを4つ、移動のためのButtonコントロールを4つ配置して、と。こんなプログラムが作れるね。
|
図5 顔のパーツを動かすプログラム |
|
|
左目、右目、左目玉、右目玉のいずれかを選択する。 |
|
|
移動させたい方向のボタンをクリックする。 |
|
口の形や目の色も変えられるといいんだけど、取りあえずは目と目玉だけが動かせるようにしよう。コードは長くなりそうだから、少しずつ書いていこうか。まずは、変数の宣言と初期値の設定だね。XとYをまとめて取り扱いたいから構造体を使おう。顔のパーツは配列で表すことにする。こんな感じかな。
Private Structure EyePosition
Public X As Integer
Public Y As Integer
End Structure
Private MyPosition(3) As EyePosition
' 0:左目、1:右目、2:左目玉、3:右目玉
Private Sub InitProc(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' 初期値の設定
MyPosition(0).X = 105 : MyPosition(0).Y = 125
MyPosition(1).X = 120 : MyPosition(1).Y = 128
MyPosition(2).X = 105 : MyPosition(2).Y = 126
MyPosition(3).X = 120 : MyPosition(3).Y = 129
End Sub
|
|
目や目玉の位置を変数にして初期値を設定する |
|
|
位置を表す構造体を作る。 |
|
|
構造体の配列を宣言する。 |
|
|
フォームがロードされたときに実行されるイベント・ハンドラ、顔のそれぞれのパーツの初期値を設定している。 |
|
うわ。急に難しくなったぁ。構造体っていったい??? 配列はプログラミングの授業で習ったから何となく分かるんだけどなぁ。
変数を個別に宣言してもいいんだけど、関連のあるものはまとめて宣言しておくとコードが書きやすくなるからね。そんなときに便利なのが構造体なんだ。構造体は複数の変数をまとめて取り扱いたいときに使う。上の
のコードを図にすればこんな感じかな(図6)。これは実際の変数の宣言ではなく、いわば新しいデータ型を作っているようなものだね。
|
図6 EyePosition構造体のイメージ |
Xという変数とYという変数をまとめて、EyePositionという名前の新しいデータ型であるかのように取り扱える。 |
上記リストの
では、このEyePositionというデータ型の配列を宣言している。配列のインデックスの最大値は3だから、以下のようなイメージで変数が作成される。Visual Basicで配列を宣言するときには、配列の要素数を指定するのではなく、インデックスの最大値を指定することに注意が必要だね。
|
図7 顔のパーツの位置を記憶するための配列 |
構造体の要素(メンバ)を指定するには、「構造体名.メンバ名」と書く。例えば、「MyPosition(1).Y」であれば、右目のY位置ということになる。 |
フォームのLoadイベント・ハンドラでは、これらの変数に初期値を設定して、最初の顔の状態を決めている。それが
のコード。簡単だね。
図にすると分かりやすいですね。じゃあ、再描画のためのコードはボクが書きます。これまでリテラルで指定していた引数の代わりに、この構造体のメンバを書けばいいだけですね。カンタンカンタン。
Private Sub DrawFace(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Dim g As Graphics = e.Graphics
g.DrawEllipse(Pens.Black, 100, 100, 50, 50)
g.DrawEllipse(Pens.Black, _
MyPosition(0).X, MyPosition(0).Y, 10, 10)
g.DrawEllipse(Pens.Black, MyPosition(1).X, _
MyPosition(1).Y, 10, 10)
g.FillEllipse(Brushes.Black, _
MyPosition(2).X, MyPosition(2).Y, 7, 8)
g.FillEllipse(Brushes.Black, _
MyPosition(3).X, MyPosition(3).Y, 7, 8)
g.DrawArc(Pens.Black, 110, 138, 8, 5, 10, 190)
End Sub
|
|
描画のためのコード |
目と目玉の位置に関する引数をリテラルから構造体のメンバに書き直しただけ。構造体のメンバの値を変更すれば、顔の表情も変わる。 |
分かった! ボタンをクリックしたら、MyPositionのメンバの値を変えて、Refreshメソッドを呼び出すんだ。でも、コードを書くのは、あたしにはちょっと難しいかも……。
大丈夫だよ。どのRadioButtonコントロールが選択されているかを調べて、配列のインデックスを決めればいいよ。例えば、[左目(1)]ラジオボタンのコントロール名がrbLeftEyeだとするね。だとすれば、rbLeftEyeのCheckedプロパティがTrueのときに、MyPositionのインデックスを0にすればいい。上下左右に移動させるには、XとYの値を変えればいいね。
Private Sub MoveEye(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUp.Click, btnRight.Click, btnLeft.Click, btnDown.Click
Dim idx As Integer
' 顔のパーツを選ぶ
If rbLeftEye.Checked Then
idx = 0
ElseIf rbRightEye.Checked Then
idx = 1
ElseIf rbLeftEyeBall.Checked Then
idx = 2
Else
idx = 3
End If
' クリックされたボタンによって位置を変える
If sender Is btnUp Then
MyPosition(idx).Y -= 1
ElseIf sender Is btnDown Then
MyPosition(idx).Y += 1
ElseIf sender Is btnLeft Then
MyPosition(idx).X -= 1
ElseIf sender Is btnRight Then
MyPosition(idx).X += 1
End If
Me.Refresh()
End Sub
|
|
ButtonコントロールのClickイベント・ハンドラ |
前半ではどのパーツの位置を変更するかを決めて、後半ではそのパーツの新しい位置を計算している。最後にRefreshメソッドを呼び出してフォームを再描画する。 |
オーケー。2人ともよくできました。ちょっと強引な感じのコードだけど、これでうまくいくね。
業務アプリInsider 記事ランキング
本日
月間