素人がゲームプログラム作成に挑戦
ゲームを買うお金がないから、ゲームを作るというゲームに挑戦!言語はVisual Basic(VB)をメインにC++、C#、Javaは参考程度
Latest Entries
- --------
- カテゴリ : スポンサー広告
- コメント : -
- トラックバック : -
ランダムマップを作ってみる その5
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
ランダムマップを作ってみる その4の続きです。
さて、前回作成した部屋を通路で結んでいきます。
通常のローグライクでは部屋と部屋を結ぶ道は1本しか作らないようです。
これはこれでいいんですけど、今回はすべてランダムにしようと思います。
これは部屋から部屋を結ぶ道の数を乱数で決めるというのではなく、とある部屋の一点から次の部屋の一点を結ぶことで、通路の重なりを発生させランダムな道を作るということです。
つまり、運しだいですが3路とか4路のように通路に分岐が発生するということです。
部屋の中の一点は乱数により決めることとします。
その一点をリストに登録しておき、1番目の点と2番目の点を結び、2番目の点と3番目の点を結んでいきます。
最後の点の場合は、1番目の点と結ぶようにします。
これで、部屋を結ぶ通路がすべての部屋を循環することができます。
このとき、部屋の区画が狭ければ通路の重なりが発生します。
点と点を結ぶ方法としては、それぞれの点の中間点に向かって上下に進んで行き、中間点になったら横方向に結んでいく方法と、それぞれの点の中間点に向かって左右に進んで行き、中間点になったら縦方向に結んでいく方法を組み合わせていきます。
ただ、これもランダム性に欠けますから、中間点ではなく2点間の任意の点にします。
この処理でできあがる途中経過の通路はマップとは別のものに保存しておきます。
すべての通路ができあがったら、マップに書き写していきます。
では、実際のプログラムを作ってみます。
今回は区画を表示する必要がありませんから、区画表示用の処理についてはコメントにしておきます。
以上で、ランダムマップのクラス作成は完成です。
区画サイズを変更すれば大きな部屋もできますし、小さな部屋も作成することができます。
部屋が小さければ通路がより複雑になって、結構いい感じになったりします。
個人的は一般的なローグライクよりこっちのほうがお気に入りだったりします。
さて、前回作成した部屋を通路で結んでいきます。
通常のローグライクでは部屋と部屋を結ぶ道は1本しか作らないようです。
これはこれでいいんですけど、今回はすべてランダムにしようと思います。
これは部屋から部屋を結ぶ道の数を乱数で決めるというのではなく、とある部屋の一点から次の部屋の一点を結ぶことで、通路の重なりを発生させランダムな道を作るということです。
つまり、運しだいですが3路とか4路のように通路に分岐が発生するということです。
部屋の中の一点は乱数により決めることとします。
その一点をリストに登録しておき、1番目の点と2番目の点を結び、2番目の点と3番目の点を結んでいきます。
最後の点の場合は、1番目の点と結ぶようにします。
これで、部屋を結ぶ通路がすべての部屋を循環することができます。
このとき、部屋の区画が狭ければ通路の重なりが発生します。
点と点を結ぶ方法としては、それぞれの点の中間点に向かって上下に進んで行き、中間点になったら横方向に結んでいく方法と、それぞれの点の中間点に向かって左右に進んで行き、中間点になったら縦方向に結んでいく方法を組み合わせていきます。
ただ、これもランダム性に欠けますから、中間点ではなく2点間の任意の点にします。
この処理でできあがる途中経過の通路はマップとは別のものに保存しておきます。
すべての通路ができあがったら、マップに書き写していきます。
では、実際のプログラムを作ってみます。
今回は区画を表示する必要がありませんから、区画表示用の処理についてはコメントにしておきます。
Public Class MapClass
…(省略)
Public Sub New( )
MakeMap( )
End Sub
Public Sub Draw(ByVal g As Graphics)
…(省略)
End Sub
Sub MakeMap( ) 'マップ作成
…(省略)
DivSplit(dtmp) '区画の分割
MakeRoom( ) '部屋の作成
Makeway( ) '通路の作成
'↓区画領域をマップに出力する繰り返し・・・領域確認時しか不要
'For i As Integer = 0 To _dList.Count - 1
' dtmp = _dList(i)
' For y As Integer = dtmp.Top To dtmp.Bottom
' _mdata(y, dtmp.Left) = mCategory.Way
' _mdata(y, dtmp.Right) = mCategory.Way
' Next
' For x As Integer = dtmp.Left To dtmp.Right
' _mdata(dtmp.Top, x) = mCategory.Way
' _mdata(dtmp.Bottom, x) = mCategory.Way
' Next
'Next
End Sub
Sub DivSplit(ByVal dVal As DivStruct) '区画作成
…(省略)
End Sub
Sub MakeRoom( ) '部屋の作成
…(省略)
End Sub
Sub Makeway( ) '通路作成メソッド
Dim rTmp As List(Of Point) = New List(Of Point)
For i As Integer = 0 To _rList.Count - 1 '各部屋の任意のXY座標を決定する繰り返し
Dim x, y As Integer
x = _rnd.Next(_rList(i).X, _rList(i).Width) 'X軸座標の決定
y = _rnd.Next(_rList(i).Y, _rList(i).Height) 'Y軸座標の決定
rTmp.Add(New Point(x, y))
Next
For i As Integer = 0 To rTmp.Count - 1 '任意のXY座標から部屋同士を結ぶ通路を作成する繰り返し
If i < rTmp.Count - 1 Then 'カウント未満なら
WayMap(rTmp(i), rTmp(i + 1)) '1点目と2点目、2点目と3点目・・・を結ぶ
Else '最終カウントなら
WayMap(rTmp(i), rTmp(0)) '最後の点と最初の点を結ぶ
End If
Next
End Sub
Sub WayMap(ByVal p1 As Point, ByVal p2 As Point) '2点間の通路を作成するメソッド
Dim wMap(,) As Boolean = New Boolean(MHEIGHT, MWIDTH) { } '通路専用マップ
Dim wx, wy As Integer
If _rnd.Next(2) = 0 Then 'X軸方向
If p1.X < p2.X Then '点1のX座標が点2のX座標より左にあるなら
wx = _rnd.Next(p1.X, p2.X) '折り返し点(交差点)を決める
For x As Integer = p1.X To wx '折り返し点まで通路にする
wMap(p1.Y, x) = True
Next
For x As Integer = wx To p2.X '折り返し点から点2までを通路にする
wMap(p2.Y, x) = True
Next
Else '点1のX座標が点2のX座標より右にあるなら
wx = _rnd.Next(p2.X, p1.X)
For x As Integer = wx To p1.X
wMap(p1.Y, x) = True
Next
For x As Integer = p2.X To wx
wMap(p2.Y, x) = True
Next
End If
If p1.Y < p2.Y Then '点1のY座標が点2のY座標より上にあるなら
For y As Integer = p1.Y To p2.Y '点1から点2へ通路を作成
wMap(y, wx) = True
Next
Else '点1のY座標が点2のY座標より下にあるなら
For y As Integer = p2.Y To p1.Y '点2から点1へ通路を作成
wMap(y, wx) = True
Next
End If
Else 'Y軸方向
If p1.Y < p2.Y Then '点1のY座標が点2のY座標より上にあるなら
wy = _rnd.Next(p1.Y, p2.Y)
For y As Integer = p1.Y To wy
wMap(y, p1.X) = True
Next
For y As Integer = wy To p2.Y
wMap(y, p2.X) = True
Next
Else '点1のY座標が点2のY座標より下にあるなら
wy = _rnd.Next(p2.Y, p2.Y)
For y As Integer = wy To p1.Y
wMap(y, p1.X) = True
Next
For y As Integer = p2.Y To wy
wMap(y, p2.X) = True
Next
End If
If p1.X < p2.X Then
For x As Integer = p1.X To p2.X
wMap(wy, x) = True
Next
Else
For x As Integer = p2.X To p1.X
wMap(wy, x) = True
Next
End If
End If
For y As Integer = 0 To wMap.GetUpperBound(0) '通路マップをメインマップに出力
For x As Integer = 0 To wMap.GetUpperBound(1)
If wMap(y, x) = True Then
_mdata(y, x) = mCategory.Way
End If
Next
Next
End Sub
End Class
…(省略)
Public Sub New( )
MakeMap( )
End Sub
Public Sub Draw(ByVal g As Graphics)
…(省略)
End Sub
Sub MakeMap( ) 'マップ作成
…(省略)
DivSplit(dtmp) '区画の分割
MakeRoom( ) '部屋の作成
Makeway( ) '通路の作成
'↓区画領域をマップに出力する繰り返し・・・領域確認時しか不要
'For i As Integer = 0 To _dList.Count - 1
' dtmp = _dList(i)
' For y As Integer = dtmp.Top To dtmp.Bottom
' _mdata(y, dtmp.Left) = mCategory.Way
' _mdata(y, dtmp.Right) = mCategory.Way
' Next
' For x As Integer = dtmp.Left To dtmp.Right
' _mdata(dtmp.Top, x) = mCategory.Way
' _mdata(dtmp.Bottom, x) = mCategory.Way
' Next
'Next
End Sub
Sub DivSplit(ByVal dVal As DivStruct) '区画作成
…(省略)
End Sub
Sub MakeRoom( ) '部屋の作成
…(省略)
End Sub
Sub Makeway( ) '通路作成メソッド
Dim rTmp As List(Of Point) = New List(Of Point)
For i As Integer = 0 To _rList.Count - 1 '各部屋の任意のXY座標を決定する繰り返し
Dim x, y As Integer
x = _rnd.Next(_rList(i).X, _rList(i).Width) 'X軸座標の決定
y = _rnd.Next(_rList(i).Y, _rList(i).Height) 'Y軸座標の決定
rTmp.Add(New Point(x, y))
Next
For i As Integer = 0 To rTmp.Count - 1 '任意のXY座標から部屋同士を結ぶ通路を作成する繰り返し
If i < rTmp.Count - 1 Then 'カウント未満なら
WayMap(rTmp(i), rTmp(i + 1)) '1点目と2点目、2点目と3点目・・・を結ぶ
Else '最終カウントなら
WayMap(rTmp(i), rTmp(0)) '最後の点と最初の点を結ぶ
End If
Next
End Sub
Sub WayMap(ByVal p1 As Point, ByVal p2 As Point) '2点間の通路を作成するメソッド
Dim wMap(,) As Boolean = New Boolean(MHEIGHT, MWIDTH) { } '通路専用マップ
Dim wx, wy As Integer
If _rnd.Next(2) = 0 Then 'X軸方向
If p1.X < p2.X Then '点1のX座標が点2のX座標より左にあるなら
wx = _rnd.Next(p1.X, p2.X) '折り返し点(交差点)を決める
For x As Integer = p1.X To wx '折り返し点まで通路にする
wMap(p1.Y, x) = True
Next
For x As Integer = wx To p2.X '折り返し点から点2までを通路にする
wMap(p2.Y, x) = True
Next
Else '点1のX座標が点2のX座標より右にあるなら
wx = _rnd.Next(p2.X, p1.X)
For x As Integer = wx To p1.X
wMap(p1.Y, x) = True
Next
For x As Integer = p2.X To wx
wMap(p2.Y, x) = True
Next
End If
If p1.Y < p2.Y Then '点1のY座標が点2のY座標より上にあるなら
For y As Integer = p1.Y To p2.Y '点1から点2へ通路を作成
wMap(y, wx) = True
Next
Else '点1のY座標が点2のY座標より下にあるなら
For y As Integer = p2.Y To p1.Y '点2から点1へ通路を作成
wMap(y, wx) = True
Next
End If
Else 'Y軸方向
If p1.Y < p2.Y Then '点1のY座標が点2のY座標より上にあるなら
wy = _rnd.Next(p1.Y, p2.Y)
For y As Integer = p1.Y To wy
wMap(y, p1.X) = True
Next
For y As Integer = wy To p2.Y
wMap(y, p2.X) = True
Next
Else '点1のY座標が点2のY座標より下にあるなら
wy = _rnd.Next(p2.Y, p2.Y)
For y As Integer = wy To p1.Y
wMap(y, p1.X) = True
Next
For y As Integer = p2.Y To wy
wMap(y, p2.X) = True
Next
End If
If p1.X < p2.X Then
For x As Integer = p1.X To p2.X
wMap(wy, x) = True
Next
Else
For x As Integer = p2.X To p1.X
wMap(wy, x) = True
Next
End If
End If
For y As Integer = 0 To wMap.GetUpperBound(0) '通路マップをメインマップに出力
For x As Integer = 0 To wMap.GetUpperBound(1)
If wMap(y, x) = True Then
_mdata(y, x) = mCategory.Way
End If
Next
Next
End Sub
End Class
以上で、ランダムマップのクラス作成は完成です。
区画サイズを変更すれば大きな部屋もできますし、小さな部屋も作成することができます。
部屋が小さければ通路がより複雑になって、結構いい感じになったりします。
個人的は一般的なローグライクよりこっちのほうがお気に入りだったりします。
ランダムマップを作ってみる その4
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
ランダムマップを作ってみる その3の続きです。
前回のマップクラスで区画を分けることができるようになりました。
今回はそれぞれの区画の中に部屋を作成していきます。
部屋の作り方は簡単です。
区画リストの中から1つずつ区画を取り出し、区画の大きさ内での部屋を作成すればよいわけです。
部屋の大きさは、区画の大きさから余白を2マスとった大きさを最大とします。
余白を1マスにしてしまうと通路を作成するスペースがなくなってしまいます。
逆に1マスにして不規則な部屋を作るというのもありですね。
なお、作成した部屋は、通路を作成するときに再度利用しますからリストに保存しておきます。
以下、オレンジ色の箇所が今回の追加箇所となります。
これにより、以下のようなマップ画面が表示されます。

それぞれの区画内に部屋ができていることが確認できるはずです。
次回は、通路を作成していきます。
前回のマップクラスで区画を分けることができるようになりました。
今回はそれぞれの区画の中に部屋を作成していきます。
部屋の作り方は簡単です。
区画リストの中から1つずつ区画を取り出し、区画の大きさ内での部屋を作成すればよいわけです。
部屋の大きさは、区画の大きさから余白を2マスとった大きさを最大とします。
余白を1マスにしてしまうと通路を作成するスペースがなくなってしまいます。
逆に1マスにして不規則な部屋を作るというのもありですね。
なお、作成した部屋は、通路を作成するときに再度利用しますからリストに保存しておきます。
以下、オレンジ色の箇所が今回の追加箇所となります。
Public Class MapClass
Enum mCategory 'マスの種類
Wall = 0 '壁
Way = 1 '通路
End Enum
Structure DivStruct '区画構造体
Dim Top, Left, Bottom, Right As Integer
End Structure
Structure RoomStruct '部屋構造体
Dim X, Y, Width, Height As Integer
End Structure
Const MWIDTH As Integer = 64 '横マス数
Const MHEIGHT As Integer = 48 '縦マス数
Const MSIZE As Integer = 10 'マスサイズ
Const MINROOM As Integer = 10 '部屋の最小サイズ・・・変動したほうが面白い
Const MARGIN As Integer = 2 '余白
Const MINDIV As Integer = MINROOM + (MARGIN * 2) '最小区画サイズ
Dim _mdata(,) As mCategory 'マップデータ配列
Dim _dList As List(Of DivStruct) '区画リスト
Dim _rList As List(Of RoomStruct) '部屋リスト
Dim _rnd As Random '乱数オブジェクト
Public Sub New( )
MakeMap( )
End Sub
Public Sub Draw(ByVal g As Graphics)
…(省略)
End Sub
Sub MakeMap( ) 'マップ作成
Dim dtmp As DivStruct = New DivStruct
Dim rtmp As RoomStruct = New RoomStruct
_rnd = New Random
_mdata = New mCategory(MHEIGHT, MWIDTH) { } 'マップ配列作成
For y As Integer = 0 To _mdata.GetUpperBound(0) '行・・・Y軸
For x As Integer = 0 To _mdata.GetUpperBound(1) '列・・・X軸
_mdata(y, x) = mCategory.Wall 'すべて壁
Next
Next
_dList = New List(Of DivStruct) '区画リストオブジェクトの作成
_rList = New List(Of RoomStruct) '部屋リストオブジェクトの作成
'↓初期区画領域の設定
dtmp.Left = 0
dtmp.Top = 0
dtmp.Right = MWIDTH - 1
dtmp.Bottom = MHEIGHT - 1
DivSplit(dtmp) '区画の分割
MakeRoom( ) '部屋の作成
'↓区画領域をマップに出力する繰り返し・・・領域確認時しか不要
For i As Integer = 0 To _dList.Count - 1
…(省略)
Next
End Sub
Sub DivSplit(ByVal dVal As DivStruct) '区画作成
…(省略)
End Sub
Sub MakeRoom( ) '部屋の作成
Dim dtmp As DivStruct '区画構造体変数
Dim rtmp As RoomStruct '部屋矩形
For i As Integer = 0 To _dList.Count - 1 '各区画内に部屋領域を作成
dtmp = _dList(i)
rtmp.Width = _rnd.Next(MINROOM, dtmp.Right - dtmp.Left - (MARGIN * 2)) '横幅
rtmp.Height = _rnd.Next(MINROOM, dtmp.Bottom - dtmp.Top - (MARGIN * 2)) '縦幅
rtmp.X = _rnd.Next(dtmp.Left + MARGIN, dtmp.Right - MARGIN - rtmp.Width) '左端
rtmp.Y = _rnd.Next(dtmp.Top + MARGIN, dtmp.Bottom - MARGIN - rtmp.Height) '上端
rtmp.Width += rtmp.X '右端
rtmp.Height += rtmp.Y '下端
_rList.Add(rtmp) '部屋リストを追加
Next
'↓各部屋の領域内を通路とする繰り返し
For i As Integer = 0 To _rList.Count - 1
rtmp = _rList(i)
For y As Integer = rtmp.Y To rtmp.Height
For x As Integer = rtmp.X To rtmp.Width
_mdata(y, x) = mCategory.Way
Next
Next
Next
End Sub
End Class
Enum mCategory 'マスの種類
Wall = 0 '壁
Way = 1 '通路
End Enum
Structure DivStruct '区画構造体
Dim Top, Left, Bottom, Right As Integer
End Structure
Structure RoomStruct '部屋構造体
Dim X, Y, Width, Height As Integer
End Structure
Const MWIDTH As Integer = 64 '横マス数
Const MHEIGHT As Integer = 48 '縦マス数
Const MSIZE As Integer = 10 'マスサイズ
Const MINROOM As Integer = 10 '部屋の最小サイズ・・・変動したほうが面白い
Const MARGIN As Integer = 2 '余白
Const MINDIV As Integer = MINROOM + (MARGIN * 2) '最小区画サイズ
Dim _mdata(,) As mCategory 'マップデータ配列
Dim _dList As List(Of DivStruct) '区画リスト
Dim _rList As List(Of RoomStruct) '部屋リスト
Dim _rnd As Random '乱数オブジェクト
Public Sub New( )
MakeMap( )
End Sub
Public Sub Draw(ByVal g As Graphics)
…(省略)
End Sub
Sub MakeMap( ) 'マップ作成
Dim dtmp As DivStruct = New DivStruct
Dim rtmp As RoomStruct = New RoomStruct
_rnd = New Random
_mdata = New mCategory(MHEIGHT, MWIDTH) { } 'マップ配列作成
For y As Integer = 0 To _mdata.GetUpperBound(0) '行・・・Y軸
For x As Integer = 0 To _mdata.GetUpperBound(1) '列・・・X軸
_mdata(y, x) = mCategory.Wall 'すべて壁
Next
Next
_dList = New List(Of DivStruct) '区画リストオブジェクトの作成
_rList = New List(Of RoomStruct) '部屋リストオブジェクトの作成
'↓初期区画領域の設定
dtmp.Left = 0
dtmp.Top = 0
dtmp.Right = MWIDTH - 1
dtmp.Bottom = MHEIGHT - 1
DivSplit(dtmp) '区画の分割
MakeRoom( ) '部屋の作成
'↓区画領域をマップに出力する繰り返し・・・領域確認時しか不要
For i As Integer = 0 To _dList.Count - 1
…(省略)
Next
End Sub
Sub DivSplit(ByVal dVal As DivStruct) '区画作成
…(省略)
End Sub
Sub MakeRoom( ) '部屋の作成
Dim dtmp As DivStruct '区画構造体変数
Dim rtmp As RoomStruct '部屋矩形
For i As Integer = 0 To _dList.Count - 1 '各区画内に部屋領域を作成
dtmp = _dList(i)
rtmp.Width = _rnd.Next(MINROOM, dtmp.Right - dtmp.Left - (MARGIN * 2)) '横幅
rtmp.Height = _rnd.Next(MINROOM, dtmp.Bottom - dtmp.Top - (MARGIN * 2)) '縦幅
rtmp.X = _rnd.Next(dtmp.Left + MARGIN, dtmp.Right - MARGIN - rtmp.Width) '左端
rtmp.Y = _rnd.Next(dtmp.Top + MARGIN, dtmp.Bottom - MARGIN - rtmp.Height) '上端
rtmp.Width += rtmp.X '右端
rtmp.Height += rtmp.Y '下端
_rList.Add(rtmp) '部屋リストを追加
Next
'↓各部屋の領域内を通路とする繰り返し
For i As Integer = 0 To _rList.Count - 1
rtmp = _rList(i)
For y As Integer = rtmp.Y To rtmp.Height
For x As Integer = rtmp.X To rtmp.Width
_mdata(y, x) = mCategory.Way
Next
Next
Next
End Sub
End Class
これにより、以下のようなマップ画面が表示されます。

それぞれの区画内に部屋ができていることが確認できるはずです。
次回は、通路を作成していきます。
ランダムマップを作ってみる その3
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
ランダムマップを作ってみる その2の続きです。
では、ローグライク(不思議のダンジョン系)のマップ処理を作成していきます。
まず、マップ領域をいくつかの区画に分割していくことから始めます。
ローグライクでは部屋の数はランダムで決まります。
ということは、部屋を配置する区画の数もランダムで決まらなければなりません。
区画の数をランダムで決めるというのは、あらかじめ区画数を乱数で決めておき、分割していくということではありません。
1つの区画の大きさを乱数によって決めるということになります。
イメージとしては、大きな四角形内に縦か横のどちらかで線を引いて四角形の中に四角形を作っていき、それぞれの四角形が最小限の大きさになったら線を引くのをやめるという感じになります。
このやり方で出来上がったそれぞれの四角形を1つの区画領域として保存すれば区画の分割はできます。
なお、この考え方であれば区画領域同士が重なることはなくなります。
また、縦に線を引くか横に線を引くかはランダムで決めてしまえばいいでしょう。
さて、区画の分割のイメージができたところで実際にプログラムとして作成するにはどうするかを考えます。
分割のイメージは四角形の中に四角形を作っていくということです。
何回四角形を作っていくかは不明ですから、For〜NextではなくDo〜Loopで繰り返せばいいように思いがちです。
ですが、今回の考え方は二分木を作るような考え方となります。
二分木のようなものを作るときは、再帰処理を使った方が効率がよいでしょう。
今回の場合は、最低限の区画領域になるまで再帰していき、これ以上分割できないという大きさなったら区画として追加するように処理させます。
実際のプログラムは以下のようになります。
今回はデータを管理しやすくするために列挙型や構造体も使っています。
DivSplitメソッドは実際に四角形の中に四角形を描いていけばイメージしやすいかと思います。
このプログラムを実行すれば以下のようなマップが表示されます。

ただ、区画の分割(縦か横)を乱数によって行っていますから、場合によっては以下のような分割もできてしまいます。

まぁ、個人的にはこれはこれでありなんですけどね。
さて、以上で区画領域を作ることができました。
次回はこの区画領域内に部屋を作っていきます。
では、ローグライク(不思議のダンジョン系)のマップ処理を作成していきます。
まず、マップ領域をいくつかの区画に分割していくことから始めます。
ローグライクでは部屋の数はランダムで決まります。
ということは、部屋を配置する区画の数もランダムで決まらなければなりません。
区画の数をランダムで決めるというのは、あらかじめ区画数を乱数で決めておき、分割していくということではありません。
1つの区画の大きさを乱数によって決めるということになります。
イメージとしては、大きな四角形内に縦か横のどちらかで線を引いて四角形の中に四角形を作っていき、それぞれの四角形が最小限の大きさになったら線を引くのをやめるという感じになります。
このやり方で出来上がったそれぞれの四角形を1つの区画領域として保存すれば区画の分割はできます。
なお、この考え方であれば区画領域同士が重なることはなくなります。
また、縦に線を引くか横に線を引くかはランダムで決めてしまえばいいでしょう。
さて、区画の分割のイメージができたところで実際にプログラムとして作成するにはどうするかを考えます。
分割のイメージは四角形の中に四角形を作っていくということです。
何回四角形を作っていくかは不明ですから、For〜NextではなくDo〜Loopで繰り返せばいいように思いがちです。
ですが、今回の考え方は二分木を作るような考え方となります。
二分木のようなものを作るときは、再帰処理を使った方が効率がよいでしょう。
今回の場合は、最低限の区画領域になるまで再帰していき、これ以上分割できないという大きさなったら区画として追加するように処理させます。
実際のプログラムは以下のようになります。
今回はデータを管理しやすくするために列挙型や構造体も使っています。
Public Class MapClass
Enum mCategory 'マスの種類
Wall = 0 '壁
Way = 1 '通路
End Enum
Structure DivStruct '区画構造体
Dim Top, Left, Bottom, Right As Integer
End Structure
Const MWIDTH As Integer = 64 '横マス数
Const MHEIGHT As Integer = 48 '縦マス数
Const MSIZE As Integer = 10 'マスサイズ
Const MINROOM As Integer = 10 '部屋の最小サイズ・・・変動したほうが面白い
Const MARGIN As Integer = 2 '余白
Const MINDIV As Integer = MINROOM + (MARGIN * 2) '最小区画サイズ
Dim _mdata(,) As mCategory 'マップデータ配列
Dim _dList As List(Of DivStruct) '区画リスト
Dim _rnd As Random '乱数オブジェクト
Public Sub New( )
MakeMap( )
End Sub
Public Sub Draw(ByVal g As Graphics)
For y As Integer = 0 To _mdata.GetUpperBound(0) '行・・・Y軸
For x As Integer = 0 To _mdata.GetUpperBound(1) '列・・・X軸
Select Case _mdata(y, x)
Case mCategory.Wall '壁
g.FillRectangle(Brushes.Black, New Rectangle(x * MSIZE, y * MSIZE, MSIZE, MSIZE))
Case mCategory.Way '通路
g.DrawRectangle(Pens.White, New Rectangle(x * MSIZE, y * MSIZE, MSIZE, MSIZE))
End Select
Next
Next
End Sub
Sub MakeMap( ) 'マップ作成
Dim dtmp As DivStruct = New DivStruct
_rnd = New Random
_mdata = New mCategory(MHEIGHT, MWIDTH) { } 'マップ配列作成
For y As Integer = 0 To _mdata.GetUpperBound(0) '行・・・Y軸
For x As Integer = 0 To _mdata.GetUpperBound(1) '列・・・X軸
_mdata(y, x) = mCategory.Wall 'すべて壁
Next
Next
_dList = New List(Of DivStruct) '区画リストオブジェクトの作成
'↓初期区画領域の設定
dtmp.Left = 0
dtmp.Top = 0
dtmp.Right = MWIDTH - 1
dtmp.Bottom = MHEIGHT - 1
DivSplit(dtmp) '区画の分割
'↓区画領域をマップに出力する繰り返し・・・領域確認時しか不要
For i As Integer = 0 To _dList.Count - 1
dtmp = _dList(i)
For y As Integer = dtmp.Top To dtmp.Bottom
_mdata(y, dtmp.Left) = mCategory.Way
_mdata(y, dtmp.Right) = mCategory.Way
Next
For x As Integer = dtmp.Left To dtmp.Right
_mdata(dtmp.Top, x) = mCategory.Way
_mdata(dtmp.Bottom, x) = mCategory.Way
Next
Next
End Sub
Sub DivSplit(ByVal dVal As DivStruct) '区画作成
Dim dPrt As DivStruct = New DivStruct '親区画変数の作成
Dim dCld As DivStruct = New DivStruct '子区画変数の作成
Dim num As Integer '分割座標
dPrt = dVal '区画データの受け渡し
If dPrt.Bottom - dPrt.Top > MINDIV * 2 AndAlso dPrt.Right - dPrt.Left > MINDIV * 2 Then
dCld = dPrt '子区画にコピー
If _rnd.Next(2) = 0 Then '乱数が0なら横分割(Y軸)
num = _rnd.Next(dPrt.Top + MINDIV, dPrt.Bottom - MINDIV)
dPrt.Bottom = num '親の下端を書き換え
dCld.Top = num '子の上端を書き換え
Else '乱数が1なら縦分割(X軸)
num = _rnd.Next(dPrt.Left + MINDIV, dPrt.Right - MINDIV)
dPrt.Right = num '親の右端を書き換え
dCld.Left = num '子の左端を書き換え
End If
DivSplit(dPrt) '親区画をさらに分割
DivSplit(dCld) '子区画をさらに分割
Else '分割できる領域がなければ
_dList.Add(dPrt) '区画リストに追加
End If
End Sub
End Class
Enum mCategory 'マスの種類
Wall = 0 '壁
Way = 1 '通路
End Enum
Structure DivStruct '区画構造体
Dim Top, Left, Bottom, Right As Integer
End Structure
Const MWIDTH As Integer = 64 '横マス数
Const MHEIGHT As Integer = 48 '縦マス数
Const MSIZE As Integer = 10 'マスサイズ
Const MINROOM As Integer = 10 '部屋の最小サイズ・・・変動したほうが面白い
Const MARGIN As Integer = 2 '余白
Const MINDIV As Integer = MINROOM + (MARGIN * 2) '最小区画サイズ
Dim _mdata(,) As mCategory 'マップデータ配列
Dim _dList As List(Of DivStruct) '区画リスト
Dim _rnd As Random '乱数オブジェクト
Public Sub New( )
MakeMap( )
End Sub
Public Sub Draw(ByVal g As Graphics)
For y As Integer = 0 To _mdata.GetUpperBound(0) '行・・・Y軸
For x As Integer = 0 To _mdata.GetUpperBound(1) '列・・・X軸
Select Case _mdata(y, x)
Case mCategory.Wall '壁
g.FillRectangle(Brushes.Black, New Rectangle(x * MSIZE, y * MSIZE, MSIZE, MSIZE))
Case mCategory.Way '通路
g.DrawRectangle(Pens.White, New Rectangle(x * MSIZE, y * MSIZE, MSIZE, MSIZE))
End Select
Next
Next
End Sub
Sub MakeMap( ) 'マップ作成
Dim dtmp As DivStruct = New DivStruct
_rnd = New Random
_mdata = New mCategory(MHEIGHT, MWIDTH) { } 'マップ配列作成
For y As Integer = 0 To _mdata.GetUpperBound(0) '行・・・Y軸
For x As Integer = 0 To _mdata.GetUpperBound(1) '列・・・X軸
_mdata(y, x) = mCategory.Wall 'すべて壁
Next
Next
_dList = New List(Of DivStruct) '区画リストオブジェクトの作成
'↓初期区画領域の設定
dtmp.Left = 0
dtmp.Top = 0
dtmp.Right = MWIDTH - 1
dtmp.Bottom = MHEIGHT - 1
DivSplit(dtmp) '区画の分割
'↓区画領域をマップに出力する繰り返し・・・領域確認時しか不要
For i As Integer = 0 To _dList.Count - 1
dtmp = _dList(i)
For y As Integer = dtmp.Top To dtmp.Bottom
_mdata(y, dtmp.Left) = mCategory.Way
_mdata(y, dtmp.Right) = mCategory.Way
Next
For x As Integer = dtmp.Left To dtmp.Right
_mdata(dtmp.Top, x) = mCategory.Way
_mdata(dtmp.Bottom, x) = mCategory.Way
Next
Next
End Sub
Sub DivSplit(ByVal dVal As DivStruct) '区画作成
Dim dPrt As DivStruct = New DivStruct '親区画変数の作成
Dim dCld As DivStruct = New DivStruct '子区画変数の作成
Dim num As Integer '分割座標
dPrt = dVal '区画データの受け渡し
If dPrt.Bottom - dPrt.Top > MINDIV * 2 AndAlso dPrt.Right - dPrt.Left > MINDIV * 2 Then
dCld = dPrt '子区画にコピー
If _rnd.Next(2) = 0 Then '乱数が0なら横分割(Y軸)
num = _rnd.Next(dPrt.Top + MINDIV, dPrt.Bottom - MINDIV)
dPrt.Bottom = num '親の下端を書き換え
dCld.Top = num '子の上端を書き換え
Else '乱数が1なら縦分割(X軸)
num = _rnd.Next(dPrt.Left + MINDIV, dPrt.Right - MINDIV)
dPrt.Right = num '親の右端を書き換え
dCld.Left = num '子の左端を書き換え
End If
DivSplit(dPrt) '親区画をさらに分割
DivSplit(dCld) '子区画をさらに分割
Else '分割できる領域がなければ
_dList.Add(dPrt) '区画リストに追加
End If
End Sub
End Class
DivSplitメソッドは実際に四角形の中に四角形を描いていけばイメージしやすいかと思います。
このプログラムを実行すれば以下のようなマップが表示されます。

ただ、区画の分割(縦か横)を乱数によって行っていますから、場合によっては以下のような分割もできてしまいます。

まぁ、個人的にはこれはこれでありなんですけどね。
さて、以上で区画領域を作ることができました。
次回はこの区画領域内に部屋を作っていきます。
ランダムマップを作ってみる その2
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
ランダムマップを作ってみる その1の続きです。
まず、ゲームループとなるForm1クラスから作成していきます。
今回はマップを作成して表示するだけですから、アニメーション処理はありません。
よって、Paintメソッドの更新処理は空白となります。
次回、ランダムマップ作成クラスを作成していきます。
まず、ゲームループとなるForm1クラスから作成していきます。
今回はマップを作成して表示するだけですから、アニメーション処理はありません。
よって、Paintメソッドの更新処理は空白となります。
Public Class Form1
Dim _map As MapClass
Sub New( )
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent( )
' InitializeComponent( ) 呼び出しの後で初期化を追加します。
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer, True)
Me.ClientSize = New Size(640, 480)
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
_map = New MapClass
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
'更新
'描画
_map.Draw(g)
'フォーム再描画
Me.Invalidate( )
End Sub
End Class
Dim _map As MapClass
Sub New( )
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent( )
' InitializeComponent( ) 呼び出しの後で初期化を追加します。
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer, True)
Me.ClientSize = New Size(640, 480)
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
_map = New MapClass
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
'更新
'描画
_map.Draw(g)
'フォーム再描画
Me.Invalidate( )
End Sub
End Class
次回、ランダムマップ作成クラスを作成していきます。
ランダムマップを作ってみる その1
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
RPGゲームを作っていくと、トルネコとかシレンのような不思議のダンジョン系も作ってみたいと思ったりするわけです。
で、今回はローグライクに挑戦してみます。
といっても、まずはランダムマップを自動生成するところまでを目標とします。
このローグライクはC言語系であればプログラムがネット上に紹介されていたり、本で紹介されたりしているわけですが、VBでのプログラムはなかなか見当たりません。
VBでゲームを作るって人が少ないからでしょうかね。
さて、ランダムマップを作っていくにあたって、アルゴリズムを考えないと先には進めません。
アルゴリズムとして参考としたのが、ダンジョンゲームプログラミングという本です。
C言語系の本ですが、考え方だけは応用できるかと思います。
ローグライクのアルゴリズムはシンプルで以下のような概略となります。
これだけでランダムマップを作成することができます。
以下は実際に作成したマップです。


次回、プログラムを作成していきます。
で、今回はローグライクに挑戦してみます。
といっても、まずはランダムマップを自動生成するところまでを目標とします。
このローグライクはC言語系であればプログラムがネット上に紹介されていたり、本で紹介されたりしているわけですが、VBでのプログラムはなかなか見当たりません。
VBでゲームを作るって人が少ないからでしょうかね。
さて、ランダムマップを作っていくにあたって、アルゴリズムを考えないと先には進めません。
アルゴリズムとして参考としたのが、ダンジョンゲームプログラミングという本です。
C言語系の本ですが、考え方だけは応用できるかと思います。
ローグライクのアルゴリズムはシンプルで以下のような概略となります。
- マップをいくつかの区画に分ける
- 区画の中に部屋を作る
- 部屋と部屋を通路で結ぶ
これだけでランダムマップを作成することができます。
以下は実際に作成したマップです。


次回、プログラムを作成していきます。
4択クイズを作ってみる その9
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
4択クイズを作ってみる その8の続きです。
では、ゲームが始まったときに実行されるタイトルクラスを作成していきます。
タイトルクラスでは、タイトルの描画とメニューの描画を行います。
さらに、メニューの上にマウスが重なったときの処理、メニューを選択したときの処理を行います。
特に重要となるのがメニュー選択時の処理です。
この選択したメニューによりゲームに出題される問題の数が変化するようにしていきます。
ということは、プレイヤーがどのメニューを選択したのかをGameClassに返し、それをPlayClassに渡す処理をしなければならないわけです。
実際、4択クイズを作ってみる その8でのGameClassではMouseClickメソッドの中でタイトルオブジェクトから選択したレベル(コース)を取得し、PlayClassオブジェクトを作成するときに渡すようにしています。
タイトルオブジェクトから取得する方法にはFunctionを使うようにしています。
タイトルクラスの中にプロパティを作成し、プロパティから取得しても構いませんが、ちょっと無駄な処理となりそうでしたので今回はFunctionで行っています。
また、タイトルの描画処理では画像ではなく文字によるタイトル描画をやってみました。
これは、ただDrawStringメソッドを使った方法による文字の表示ではなく、パスを使った画像としての文字の表示方法となります。
パスを使うことで文字の縁取りを行うことができます。
パスを使うためには、System.Drawing.Drawing2D名前空間を参照設定しておく必要がありますので、Importsをしておきます。
では、プログラムを作っていきます。
Drawメソッドの中でDim sf As StringFormat = New StringFormatという変数を作成しています。
これは、パスのデータを座標を元にどの位置に表示するかを設定することのできるオブジェクトとなります。
今回は、横方向に中央寄せ、縦方向にも中央寄せとしています。
この設定がパスを作成するときのgp.AddString(str, _fm, FontStyle.Regular, 100, New Point(320, 120), sf)で有効となります。
中央寄せと中央寄せにすることで、パスの中心座標が指定した(320,120)となります。
これで以下のようなタイトル画面ができあがりです。
では、ゲームが始まったときに実行されるタイトルクラスを作成していきます。
タイトルクラスでは、タイトルの描画とメニューの描画を行います。
さらに、メニューの上にマウスが重なったときの処理、メニューを選択したときの処理を行います。
特に重要となるのがメニュー選択時の処理です。
この選択したメニューによりゲームに出題される問題の数が変化するようにしていきます。
ということは、プレイヤーがどのメニューを選択したのかをGameClassに返し、それをPlayClassに渡す処理をしなければならないわけです。
実際、4択クイズを作ってみる その8でのGameClassではMouseClickメソッドの中でタイトルオブジェクトから選択したレベル(コース)を取得し、PlayClassオブジェクトを作成するときに渡すようにしています。
タイトルオブジェクトから取得する方法にはFunctionを使うようにしています。
タイトルクラスの中にプロパティを作成し、プロパティから取得しても構いませんが、ちょっと無駄な処理となりそうでしたので今回はFunctionで行っています。
また、タイトルの描画処理では画像ではなく文字によるタイトル描画をやってみました。
これは、ただDrawStringメソッドを使った方法による文字の表示ではなく、パスを使った画像としての文字の表示方法となります。
パスを使うことで文字の縁取りを行うことができます。
パスを使うためには、System.Drawing.Drawing2D名前空間を参照設定しておく必要がありますので、Importsをしておきます。
では、プログラムを作っていきます。
Imports System.Drawing.Drawing2D
Public Class TitleClass
Const mWidth As Integer = 96 'メニュー幅
Const mHeight As Integer = 32 'メニュー高
Const px As Integer = 272 'メニュー表示X座標
Const py As Integer = 240 'メニュー表示Y座標
Const dy As Integer = 40 'メニュー表示シフトY座標
Dim mimg As Bitmap 'メニュー画像配列
Dim mRect( ) As Rectangle 'メニュー矩形配列
Dim mId As Integer '表示画像番号
Dim _fm As FontFamily = New FontFamily("HG丸ゴシックM-PRO") 'フォントの種類...HG丸ゴシックM-PRO
Dim _pen As Pen = New Pen(Color.Orange, 10) 'ペン
Public Sub New( )
mimg = New Bitmap(My.Resources.menu) 'メニュー画像の読み込み
mimg.MakeTransparent( ) '背景を透明化
mRect = New Rectangle(9) { } 'メニュー画像矩形配列の作成
mRect(0) = New Rectangle(0, 0, mWidth, mHeight)
mRect(1) = New Rectangle(0, 32, mWidth, mHeight)
mRect(2) = New Rectangle(0, 64, mWidth, mHeight)
mRect(3) = New Rectangle(0, 96, mWidth, mHeight)
mRect(4) = New Rectangle(0, 128, mWidth, mHeight)
mRect(5) = New Rectangle(96, 0, mWidth, mHeight)
mRect(6) = New Rectangle(96, 32, mWidth, mHeight)
mRect(7) = New Rectangle(96, 64, mWidth, mHeight)
mRect(8) = New Rectangle(96, 96, mWidth, mHeight)
mRect(9) = New Rectangle(96, 128, mWidth, mHeight)
mId = 0
End Sub
Public Sub Draw(ByVal g As Graphics)
Dim gp As GraphicsPath = New GraphicsPath
Dim sf As StringFormat = New StringFormat
Dim str As String = "Perfect" & vbCrLf & "Choice"
g.FillRectangle(Brushes.White, 0, 0, 640, 480) '背景を白で塗りつぶし
sf.Alignment = StringAlignment.Center '中央寄せ
sf.LineAlignment = StringAlignment.Center '縦中央寄せ
gp.Reset( )
gp.AddString(str, _fm, FontStyle.Regular, 100, New Point(320, 120), sf) 'パスの追加
gp.CloseFigure( ) 'パスを閉じる
g.SmoothingMode = SmoothingMode.AntiAlias
g.DrawPath(_pen, gp) '縁取り
g.FillPath(Brushes.Gold, gp) ’パスの塗りつぶし
If mId <> 1 Then
g.DrawImage(mimg, px, py, mRect(0), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py, mRect(5), GraphicsUnit.Pixel)
End If
If mId <> 2 Then
g.DrawImage(mimg, px, py + dy, mRect(1), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy, mRect(6), GraphicsUnit.Pixel)
End If
If mId <> 3 Then
g.DrawImage(mimg, px, py + dy * 2, mRect(2), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy * 2, mRect(7), GraphicsUnit.Pixel)
End If
If mId <> 4 Then
g.DrawImage(mimg, px, py + dy * 3, mRect(3), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy * 3, mRect(8), GraphicsUnit.Pixel)
End If
If mId <> 5 Then
g.DrawImage(mimg, px, py + dy * 4, mRect(4), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy * 4, mRect(9), GraphicsUnit.Pixel)
End If
End Sub
Public Sub MouseMove(ByVal p As Point)
If p.X >= 320 - mWidth / 2 And p.X <= 320 + mWidth / 2 Then
If p.Y >= py AndAlso p.Y <= py + mHeight Then
mId = 1
ElseIf p.Y >= py + dy AndAlso p.Y <= py + dy + mHeight Then
mId = 2
ElseIf p.Y >= py + dy * 2 AndAlso p.Y <= py + dy * 2 + mHeight Then
mId = 3
ElseIf p.Y >= py + dy * 3 AndAlso p.Y <= py + dy * 3 + mHeight Then
mId = 4
ElseIf p.Y >= py + dy * 4 AndAlso p.Y <= py + dy * 4 + mHeight Then
mId = 5
Else
mId = 0
End If
Else
mId = 0
End If
End Sub
Public Function MouseClick(ByVal p As Point) As Integer
Dim lv As Integer
If p.X >= 320 - mWidth / 2 And p.X <= 320 + mWidth / 2 Then
If p.Y >= py AndAlso p.Y <= py + mHeight Then
lv = 1
ElseIf p.Y >= py + dy AndAlso p.Y <= py + dy + mHeight Then
lv = 2
ElseIf p.Y >= py + dy * 2 AndAlso p.Y <= py + dy * 2 + mHeight Then
lv = 3
ElseIf p.Y >= py + dy * 3 AndAlso p.Y <= py + dy * 3 + mHeight Then
lv = 4
ElseIf p.Y >= py + dy * 4 AndAlso p.Y <= py + dy * 4 + mHeight Then
lv = 5
Else
lv = 0
End If
Else
lv = 0
End If
Return lv
End Function
End Class
Public Class TitleClass
Const mWidth As Integer = 96 'メニュー幅
Const mHeight As Integer = 32 'メニュー高
Const px As Integer = 272 'メニュー表示X座標
Const py As Integer = 240 'メニュー表示Y座標
Const dy As Integer = 40 'メニュー表示シフトY座標
Dim mimg As Bitmap 'メニュー画像配列
Dim mRect( ) As Rectangle 'メニュー矩形配列
Dim mId As Integer '表示画像番号
Dim _fm As FontFamily = New FontFamily("HG丸ゴシックM-PRO") 'フォントの種類...HG丸ゴシックM-PRO
Dim _pen As Pen = New Pen(Color.Orange, 10) 'ペン
Public Sub New( )
mimg = New Bitmap(My.Resources.menu) 'メニュー画像の読み込み
mimg.MakeTransparent( ) '背景を透明化
mRect = New Rectangle(9) { } 'メニュー画像矩形配列の作成
mRect(0) = New Rectangle(0, 0, mWidth, mHeight)
mRect(1) = New Rectangle(0, 32, mWidth, mHeight)
mRect(2) = New Rectangle(0, 64, mWidth, mHeight)
mRect(3) = New Rectangle(0, 96, mWidth, mHeight)
mRect(4) = New Rectangle(0, 128, mWidth, mHeight)
mRect(5) = New Rectangle(96, 0, mWidth, mHeight)
mRect(6) = New Rectangle(96, 32, mWidth, mHeight)
mRect(7) = New Rectangle(96, 64, mWidth, mHeight)
mRect(8) = New Rectangle(96, 96, mWidth, mHeight)
mRect(9) = New Rectangle(96, 128, mWidth, mHeight)
mId = 0
End Sub
Public Sub Draw(ByVal g As Graphics)
Dim gp As GraphicsPath = New GraphicsPath
Dim sf As StringFormat = New StringFormat
Dim str As String = "Perfect" & vbCrLf & "Choice"
g.FillRectangle(Brushes.White, 0, 0, 640, 480) '背景を白で塗りつぶし
sf.Alignment = StringAlignment.Center '中央寄せ
sf.LineAlignment = StringAlignment.Center '縦中央寄せ
gp.Reset( )
gp.AddString(str, _fm, FontStyle.Regular, 100, New Point(320, 120), sf) 'パスの追加
gp.CloseFigure( ) 'パスを閉じる
g.SmoothingMode = SmoothingMode.AntiAlias
g.DrawPath(_pen, gp) '縁取り
g.FillPath(Brushes.Gold, gp) ’パスの塗りつぶし
If mId <> 1 Then
g.DrawImage(mimg, px, py, mRect(0), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py, mRect(5), GraphicsUnit.Pixel)
End If
If mId <> 2 Then
g.DrawImage(mimg, px, py + dy, mRect(1), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy, mRect(6), GraphicsUnit.Pixel)
End If
If mId <> 3 Then
g.DrawImage(mimg, px, py + dy * 2, mRect(2), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy * 2, mRect(7), GraphicsUnit.Pixel)
End If
If mId <> 4 Then
g.DrawImage(mimg, px, py + dy * 3, mRect(3), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy * 3, mRect(8), GraphicsUnit.Pixel)
End If
If mId <> 5 Then
g.DrawImage(mimg, px, py + dy * 4, mRect(4), GraphicsUnit.Pixel)
Else
g.DrawImage(mimg, px, py + dy * 4, mRect(9), GraphicsUnit.Pixel)
End If
End Sub
Public Sub MouseMove(ByVal p As Point)
If p.X >= 320 - mWidth / 2 And p.X <= 320 + mWidth / 2 Then
If p.Y >= py AndAlso p.Y <= py + mHeight Then
mId = 1
ElseIf p.Y >= py + dy AndAlso p.Y <= py + dy + mHeight Then
mId = 2
ElseIf p.Y >= py + dy * 2 AndAlso p.Y <= py + dy * 2 + mHeight Then
mId = 3
ElseIf p.Y >= py + dy * 3 AndAlso p.Y <= py + dy * 3 + mHeight Then
mId = 4
ElseIf p.Y >= py + dy * 4 AndAlso p.Y <= py + dy * 4 + mHeight Then
mId = 5
Else
mId = 0
End If
Else
mId = 0
End If
End Sub
Public Function MouseClick(ByVal p As Point) As Integer
Dim lv As Integer
If p.X >= 320 - mWidth / 2 And p.X <= 320 + mWidth / 2 Then
If p.Y >= py AndAlso p.Y <= py + mHeight Then
lv = 1
ElseIf p.Y >= py + dy AndAlso p.Y <= py + dy + mHeight Then
lv = 2
ElseIf p.Y >= py + dy * 2 AndAlso p.Y <= py + dy * 2 + mHeight Then
lv = 3
ElseIf p.Y >= py + dy * 3 AndAlso p.Y <= py + dy * 3 + mHeight Then
lv = 4
ElseIf p.Y >= py + dy * 4 AndAlso p.Y <= py + dy * 4 + mHeight Then
lv = 5
Else
lv = 0
End If
Else
lv = 0
End If
Return lv
End Function
End Class
Drawメソッドの中でDim sf As StringFormat = New StringFormatという変数を作成しています。
これは、パスのデータを座標を元にどの位置に表示するかを設定することのできるオブジェクトとなります。
今回は、横方向に中央寄せ、縦方向にも中央寄せとしています。
この設定がパスを作成するときのgp.AddString(str, _fm, FontStyle.Regular, 100, New Point(320, 120), sf)で有効となります。
中央寄せと中央寄せにすることで、パスの中心座標が指定した(320,120)となります。
これで以下のようなタイトル画面ができあがりです。
画面スクロール その2
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
画面スクロール その1の続きです。
では、画面スクロール処理の作成を行っていきます。
今回のスクロール処理には、マウスクリックで自動スクロールとカーソルキーでの手動スクロールの2パターンを組み込んでいきます。
スクロールの考え方は、表示矩形のY座標を変化させていくことです。
自動スクロールの場合は、一定時間が経過すると変化させます。
手動スクロールの場合は、カーソルキーが押されている間だけ変化させていきます。
ちなみに、KeyDownイベントはキーを押しているときには常に動作していますので、カーソルキーが押されている間という繰り返し処理は不要です。
自動スクロールはクリックで上から下へ、もう一度クリックで下から上へ移動するようにします。
もちろん自動スクロール中は停止するまでクリックを無効にします。
というこで、プログラムの方を作成していきます。
自動スクロール時にはUpdateメソッドとScrollStartメソッドが実行されます。
手動スクロール時にはKeyDownメソッドが実行されます。
それ以外のメソッドは共通となります。
以上で画像の縦スクロール処理のできあがりです。
自動スクロール中に一時停止とかをやってもいいのかもしれませんが、そこらへんは仕様しだいでしょうかね。
あと、横スクロールをしたければX座標を動かせばよいだけですから改造は容易にできるかと思います。
では、画面スクロール処理の作成を行っていきます。
今回のスクロール処理には、マウスクリックで自動スクロールとカーソルキーでの手動スクロールの2パターンを組み込んでいきます。
スクロールの考え方は、表示矩形のY座標を変化させていくことです。
自動スクロールの場合は、一定時間が経過すると変化させます。
手動スクロールの場合は、カーソルキーが押されている間だけ変化させていきます。
ちなみに、KeyDownイベントはキーを押しているときには常に動作していますので、カーソルキーが押されている間という繰り返し処理は不要です。
自動スクロールはクリックで上から下へ、もう一度クリックで下から上へ移動するようにします。
もちろん自動スクロール中は停止するまでクリックを無効にします。
というこで、プログラムの方を作成していきます。
自動スクロール時にはUpdateメソッドとScrollStartメソッドが実行されます。
手動スクロール時にはKeyDownメソッドが実行されます。
それ以外のメソッドは共通となります。
Public Class ScrollClass
Const SC As Integer = 10 'スクロール量
Dim _img As Bitmap
Dim _rect As Rectangle
Dim _updown As Boolean 'Falseが上へ、Trueが下へ
Dim _anime As Boolean 'アニメーションフラグ
Dim _lasttime As Double
Dim _dy As Integer '表示Y座標
Public Sub New( )
_img = New Bitmap(My.Resources.scroll1) 'リソースから画像の読み込み
_rect = New Rectangle(0, 0, 640, 480) '初期表示矩形の作成
_anime = False
_updown = False
End Sub
Public Sub Update(ByVal nowTime As Double)
If _anime Then 'アニメーション中なら
If nowTime - _lasttime > 0.1 Then '一定時間経過していれば
If _updown Then '上から下へ
If _dy + SC < _img.Height - 480 Then
_dy += SC
Else
_anime = False
End If
Else '下から上へ
If _dy - SC > 0 Then
_dy -= SC
Else
_anime = False
End If
End If
_lasttime = nowTime
_rect = New Rectangle(0, _dy, 640, 480)
End If
End If
End Sub
Public Sub Draw(ByVal g As Graphics)
g.DrawImage(_img, New Rectangle(0, 0, 640, 480), _rect, GraphicsUnit.Pixel)
End Sub
Public Sub ScrollStart( )
If _anime = False Then
_updown = Not _updown '上下反転
If _updown Then '上から下へ
_dy = 0
Else '下から上へ
_dy = _img.Height - 480
End If
_rect = New Rectangle(0, _dy, 640, 480)
_lasttime = 0.0
_anime = True 'アニメーション開始
End If
End Sub
Public Sub KeyDown(ByVal k As Keys)
If _anime = False Then
Select Case k
Case Keys.Up '上キーが押されていたら
If _dy - SC > 0 Then
_dy -= SC
End If
Case Keys.Down '下キーが押されていたら
If _dy + SC < _img.Height - 480 Then
_dy += SC
End If
End Select
_rect = New Rectangle(0, _dy, 640, 480)
End If
End Sub
End Class
Const SC As Integer = 10 'スクロール量
Dim _img As Bitmap
Dim _rect As Rectangle
Dim _updown As Boolean 'Falseが上へ、Trueが下へ
Dim _anime As Boolean 'アニメーションフラグ
Dim _lasttime As Double
Dim _dy As Integer '表示Y座標
Public Sub New( )
_img = New Bitmap(My.Resources.scroll1) 'リソースから画像の読み込み
_rect = New Rectangle(0, 0, 640, 480) '初期表示矩形の作成
_anime = False
_updown = False
End Sub
Public Sub Update(ByVal nowTime As Double)
If _anime Then 'アニメーション中なら
If nowTime - _lasttime > 0.1 Then '一定時間経過していれば
If _updown Then '上から下へ
If _dy + SC < _img.Height - 480 Then
_dy += SC
Else
_anime = False
End If
Else '下から上へ
If _dy - SC > 0 Then
_dy -= SC
Else
_anime = False
End If
End If
_lasttime = nowTime
_rect = New Rectangle(0, _dy, 640, 480)
End If
End If
End Sub
Public Sub Draw(ByVal g As Graphics)
g.DrawImage(_img, New Rectangle(0, 0, 640, 480), _rect, GraphicsUnit.Pixel)
End Sub
Public Sub ScrollStart( )
If _anime = False Then
_updown = Not _updown '上下反転
If _updown Then '上から下へ
_dy = 0
Else '下から上へ
_dy = _img.Height - 480
End If
_rect = New Rectangle(0, _dy, 640, 480)
_lasttime = 0.0
_anime = True 'アニメーション開始
End If
End Sub
Public Sub KeyDown(ByVal k As Keys)
If _anime = False Then
Select Case k
Case Keys.Up '上キーが押されていたら
If _dy - SC > 0 Then
_dy -= SC
End If
Case Keys.Down '下キーが押されていたら
If _dy + SC < _img.Height - 480 Then
_dy += SC
End If
End Select
_rect = New Rectangle(0, _dy, 640, 480)
End If
End Sub
End Class
以上で画像の縦スクロール処理のできあがりです。
自動スクロール中に一時停止とかをやってもいいのかもしれませんが、そこらへんは仕様しだいでしょうかね。
あと、横スクロールをしたければX座標を動かせばよいだけですから改造は容易にできるかと思います。
画像スクロール その1
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
画面サイズより大きな画像を表示する際、縮小して表示するという方法や画面を切り替えて表示するという方法がありますが、今回は画面スクロールで表示する方法をやってみます。
この考え方は、一定時間ごとに表示する矩形座標を変更していくかカーソルキーが押されたときに表示する矩形座標を変更するように処理させればよいわけです。
たとえば、640×960の画像を縦スクロールさせたければ矩形座標(0,0,640,480)をスタート座標とし、一定時間ごとにY座標を増やしていけば下へ画像をスクロールすることができます。
逆に矩形座標(0,480,640,480)をスタート座標とし、一定時間ごとにY座標を減らしていけば上へ画像をスクロールすることができます。
どちらの場合も特定の矩形座標になった場合にはスクロールを停止させる必要があります。
たとえば、上から下へのスクロールを停止する矩形座標は(0,480,640,480)となり、逆に下から上へのスクロールを停止する矩形座標は(0,0,640,480)となります。
これらの座標で停止させなければ画像の存在しない領域を表示することになりますから気をつけなければなりません。
ちなみに、画像の高さが960ピクセルで固定ではなく、画像によってバラバラの場合は上から下へスクロールするときの停止矩形座標は(0,画像の高さ-480,640,480)とすれば対応することができます。
さらに、下から上へスクロールさせるときのスタート矩形座標は(0,画像の高さ-480,640,480)とすればどんな画像の高さであっても対応することができます。
今回は640×960の画像を利用していきますので、あらかじめ画像を用意しリソースに追加しておきます。
では、Form1クラスから作成していきます。
このクラスはいつものパターンで作成していきます。
Form1クラスではスクロール処理を行う_scrollオブジェクトを作成します。
マウスボタンがクリックされたときにはScrollStartメソッドを実行し、キー入力があった場合はKeyDownメソッドを実行するようにしておきます。
次回はスクロール処理を行うScrollClassを作成していきます。
この考え方は、一定時間ごとに表示する矩形座標を変更していくかカーソルキーが押されたときに表示する矩形座標を変更するように処理させればよいわけです。
たとえば、640×960の画像を縦スクロールさせたければ矩形座標(0,0,640,480)をスタート座標とし、一定時間ごとにY座標を増やしていけば下へ画像をスクロールすることができます。
逆に矩形座標(0,480,640,480)をスタート座標とし、一定時間ごとにY座標を減らしていけば上へ画像をスクロールすることができます。
どちらの場合も特定の矩形座標になった場合にはスクロールを停止させる必要があります。
たとえば、上から下へのスクロールを停止する矩形座標は(0,480,640,480)となり、逆に下から上へのスクロールを停止する矩形座標は(0,0,640,480)となります。
これらの座標で停止させなければ画像の存在しない領域を表示することになりますから気をつけなければなりません。
ちなみに、画像の高さが960ピクセルで固定ではなく、画像によってバラバラの場合は上から下へスクロールするときの停止矩形座標は(0,画像の高さ-480,640,480)とすれば対応することができます。
さらに、下から上へスクロールさせるときのスタート矩形座標は(0,画像の高さ-480,640,480)とすればどんな画像の高さであっても対応することができます。
今回は640×960の画像を利用していきますので、あらかじめ画像を用意しリソースに追加しておきます。
では、Form1クラスから作成していきます。
このクラスはいつものパターンで作成していきます。
Public Class Form1
Dim swatch As Stopwatch = New Stopwatch( ) 'ストップウォッチ
Dim _scroll As ScrollClass
Sub New( )
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent( )
' InitializeComponent( ) 呼び出しの後で初期化を追加します。
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer, True)
Me.ClientSize = New Size(640, 480)
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
swatch.Reset( )
swatch.Start( )
_scroll = New ScrollClass
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
Dim nowTime As Double = swatch.ElapsedMilliseconds / 1000.0 'Paintメソッドを実行した瞬間の時間
'更新
_scroll.Update(nowTime)
'描画
_scroll.Draw(e.Graphics)
'フォーム再描画
Me.Invalidate( )
End Sub
Private Sub Form1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
_scroll.ScrollStart( )
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
_scroll.KeyDown(e.KeyCode)
End Sub
End Class
Dim swatch As Stopwatch = New Stopwatch( ) 'ストップウォッチ
Dim _scroll As ScrollClass
Sub New( )
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent( )
' InitializeComponent( ) 呼び出しの後で初期化を追加します。
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer, True)
Me.ClientSize = New Size(640, 480)
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
swatch.Reset( )
swatch.Start( )
_scroll = New ScrollClass
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
Dim nowTime As Double = swatch.ElapsedMilliseconds / 1000.0 'Paintメソッドを実行した瞬間の時間
'更新
_scroll.Update(nowTime)
'描画
_scroll.Draw(e.Graphics)
'フォーム再描画
Me.Invalidate( )
End Sub
Private Sub Form1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
_scroll.ScrollStart( )
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
_scroll.KeyDown(e.KeyCode)
End Sub
End Class
Form1クラスではスクロール処理を行う_scrollオブジェクトを作成します。
マウスボタンがクリックされたときにはScrollStartメソッドを実行し、キー入力があった場合はKeyDownメソッドを実行するようにしておきます。
次回はスクロール処理を行うScrollClassを作成していきます。
4択クイズを作ってみる その8
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
4択クイズを作ってみる その7の続きです。
4択クイズゲームに必要なオブジェクト等ゲーム全般を管理するゲームクラスを作成していきます。
まず、4択クイズを作ってみる その2で考えたモデルでよると、ゲームクラスの中にはタイトルクラスと出題クラスが含まれていることがわかります。
2つのクラスはまったく別の処理を行うクラスです。
これはゲームの状態により切り替えて利用することになります。
この状態をモードとしてあらかじめゲームクラスで用意しておく必要があります。
通常、ゲームはタイトルから始まりますので、モードの初期設定はタイトルモードとなります。
さらにタイトルオブジェクトを作成しておかなければエラーになりますから、ゲームクラスを作成したと同時にタイトルオブジェクトも作成するようにしておきます。
モード移行のタイミングは、以下のようになります。
また、コースとしては10問、20問、・・・50問の5種類があります。
このコースの選択はタイトルクラスで行うわけですが、選ばれたコースは出題クラスで利用します。
コース情報の受け渡しポイントはタイトルモード中にクリックされたときとなります。
ただ、クリックされたコース情報はタイトルオブジェクトが把握していますから、ゲームクラスで利用するためにはタイトルオブジェクトへアクセスする必要があります。
このような場合はプロパティを作成して行えばよいでしょう。
プログラムは以下のようになります。
なお、タイトルクラスはTitleClass、出題クラスはPlayClassとしています。
4択クイズゲームに必要なオブジェクト等ゲーム全般を管理するゲームクラスを作成していきます。
まず、4択クイズを作ってみる その2で考えたモデルでよると、ゲームクラスの中にはタイトルクラスと出題クラスが含まれていることがわかります。
2つのクラスはまったく別の処理を行うクラスです。
これはゲームの状態により切り替えて利用することになります。
この状態をモードとしてあらかじめゲームクラスで用意しておく必要があります。
通常、ゲームはタイトルから始まりますので、モードの初期設定はタイトルモードとなります。
さらにタイトルオブジェクトを作成しておかなければエラーになりますから、ゲームクラスを作成したと同時にタイトルオブジェクトも作成するようにしておきます。
モード移行のタイミングは、以下のようになります。
- タイトルから出題に変化するときはタイトルのコースを選択したとき
- 出題からタイトルに変化するときはすべての問題の出題が終わったとき
また、コースとしては10問、20問、・・・50問の5種類があります。
このコースの選択はタイトルクラスで行うわけですが、選ばれたコースは出題クラスで利用します。
コース情報の受け渡しポイントはタイトルモード中にクリックされたときとなります。
ただ、クリックされたコース情報はタイトルオブジェクトが把握していますから、ゲームクラスで利用するためにはタイトルオブジェクトへアクセスする必要があります。
このような場合はプロパティを作成して行えばよいでしょう。
プログラムは以下のようになります。
Public Class GameClass
Enum MODE As Integer 'ゲームモード列挙型
title 'タイトルモード
play 'プレイモード
End Enum
Dim _gmode As MODE 'ゲームモード
Dim _title As TitleClass 'タイトルクラス
Dim _play As PlayClass 'プレイクラス(出題クラス)
Public Sub New( )
_gmode = MODE.title 'タイトルモードを設定
_title = New TitleClass 'タイトルオブジェクトを作成
End Sub
Public Sub Update(ByVal nowTime As Double, ByVal elapsedTime As Double)
Select Case _gmode
Case MODE.play 'プレイモードなら
If _play.EndFlag Then 'ゲーム終了なら
_play = Nothing 'プレイオブジェクトの解放
_title = New TitleClass
_gmode = MODE.title
Else 'ゲームプレイ中なら
_play.Update(nowTime, elapsedTime)
End If
End Select
End Sub
Public Sub Draw(ByVal g As Graphics)
Select Case _gmode
Case MODE.title 'タイトルモードなら
_title.Draw(g)
Case MODE.play 'プレイモードなら
_play.Draw(g)
End Select
End Sub
Public Sub MouseMove(ByVal p As Point)
Select Case _gmode
Case MODE.title 'タイトルモードなら
_title.MouseMove(p)
Case MODE.play 'プレイモードなら
_play.MouseMove(p)
End Select
End Sub
Public Sub MouseClick(ByVal p As Point, ByVal b As MouseButtons)
Select Case _gmode
Case MODE.title 'タイトルモードなら
Dim lv As Integer = _title.MouseClick(p) 'タイトルクラスよりレベルを取得
If lv > 0 Then '何らかのレベルを選択していたら
_gmode = MODE.play 'プレイモードを設定
_play = New PlayClass(lv) 'プレイオブジェクトを作成
_title = Nothing 'タイトルオブジェクトの解放
End If
Case MODE.play 'プレイモードなら
_play.MouseClick(p, b)
End Select
End Sub
End Class
Enum MODE As Integer 'ゲームモード列挙型
title 'タイトルモード
play 'プレイモード
End Enum
Dim _gmode As MODE 'ゲームモード
Dim _title As TitleClass 'タイトルクラス
Dim _play As PlayClass 'プレイクラス(出題クラス)
Public Sub New( )
_gmode = MODE.title 'タイトルモードを設定
_title = New TitleClass 'タイトルオブジェクトを作成
End Sub
Public Sub Update(ByVal nowTime As Double, ByVal elapsedTime As Double)
Select Case _gmode
Case MODE.play 'プレイモードなら
If _play.EndFlag Then 'ゲーム終了なら
_play = Nothing 'プレイオブジェクトの解放
_title = New TitleClass
_gmode = MODE.title
Else 'ゲームプレイ中なら
_play.Update(nowTime, elapsedTime)
End If
End Select
End Sub
Public Sub Draw(ByVal g As Graphics)
Select Case _gmode
Case MODE.title 'タイトルモードなら
_title.Draw(g)
Case MODE.play 'プレイモードなら
_play.Draw(g)
End Select
End Sub
Public Sub MouseMove(ByVal p As Point)
Select Case _gmode
Case MODE.title 'タイトルモードなら
_title.MouseMove(p)
Case MODE.play 'プレイモードなら
_play.MouseMove(p)
End Select
End Sub
Public Sub MouseClick(ByVal p As Point, ByVal b As MouseButtons)
Select Case _gmode
Case MODE.title 'タイトルモードなら
Dim lv As Integer = _title.MouseClick(p) 'タイトルクラスよりレベルを取得
If lv > 0 Then '何らかのレベルを選択していたら
_gmode = MODE.play 'プレイモードを設定
_play = New PlayClass(lv) 'プレイオブジェクトを作成
_title = Nothing 'タイトルオブジェクトの解放
End If
Case MODE.play 'プレイモードなら
_play.MouseClick(p, b)
End Select
End Sub
End Class
なお、タイトルクラスはTitleClass、出題クラスはPlayClassとしています。
4択クイズを作ってみる その7
- ジャンル : コンピュータ
- スレッドテーマ : Visual Basic
4択クイズを作ってみる その6の続きです。
まず、ゲームの基盤となるForm1クラスから作成していきます。
このクラスはいつもどおりの定番処理ばかりになります。
このクラスが行う処理は、ゲームクラスの作成・管理と各種イベントの管理になります。
プログラムは以下のような感じです。
イベント処理としてMouseMoveとMouseClickを作成しています。
MouseMoveイベントはタイトル画面でのコース選択、問題の選択肢の選択時にマウスオーバー処理をさせるために利用します。
MouseEnterイベントを利用するのは一般的なコントロール上であって、今回のようなFormしかないプログラムではすべてEnter状態になってしまいますから利用しません。
また、MouseClickイベントはどのコース、どの選択肢を選択したかを判断するために利用します。
もちろん、重要となるのはクリックしたときの座標ですね。
この座標を利用することにより選択肢等の判断をすることができます。
次回はゲームに関するクラスの生成・管理を行うゲームクラスを作成していきます。
まず、ゲームの基盤となるForm1クラスから作成していきます。
このクラスはいつもどおりの定番処理ばかりになります。
このクラスが行う処理は、ゲームクラスの作成・管理と各種イベントの管理になります。
プログラムは以下のような感じです。
Public Class Form1
Dim _Game As GameClass 'ゲームクラス
Dim swatch As Stopwatch = New Stopwatch( ) 'ストップウォッチ
Dim lastTime As Double '前回実行時の時間
Sub New( )
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent( )
' InitializeComponent( ) 呼び出しの後で初期化を追加します。
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer, True)
Me.ClientSize = New Size(640, 480)
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
lastTime = 0.0
swatch.Reset( )
swatch.Start( )
_Game = New GameClass
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
Dim nowTime As Double = swatch.ElapsedMilliseconds / 1000.0 'Paintメソッドを実行した瞬間の時間
Dim elapsedTime As Double = nowTime - lastTime
lastTime = nowTime
'更新
_Game.Update(nowTime, elapsedTime)
'描画
_Game.Draw(g)
'フォーム再描画
Me.Invalidate( )
End Sub
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
_Game.MouseMove(e.Location)
End Sub
Private Sub Form1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
_Game.MouseClick(e.Location, e.Button)
End Sub
End Class
Dim _Game As GameClass 'ゲームクラス
Dim swatch As Stopwatch = New Stopwatch( ) 'ストップウォッチ
Dim lastTime As Double '前回実行時の時間
Sub New( )
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent( )
' InitializeComponent( ) 呼び出しの後で初期化を追加します。
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer, True)
Me.ClientSize = New Size(640, 480)
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
lastTime = 0.0
swatch.Reset( )
swatch.Start( )
_Game = New GameClass
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
Dim nowTime As Double = swatch.ElapsedMilliseconds / 1000.0 'Paintメソッドを実行した瞬間の時間
Dim elapsedTime As Double = nowTime - lastTime
lastTime = nowTime
'更新
_Game.Update(nowTime, elapsedTime)
'描画
_Game.Draw(g)
'フォーム再描画
Me.Invalidate( )
End Sub
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
_Game.MouseMove(e.Location)
End Sub
Private Sub Form1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
_Game.MouseClick(e.Location, e.Button)
End Sub
End Class
イベント処理としてMouseMoveとMouseClickを作成しています。
MouseMoveイベントはタイトル画面でのコース選択、問題の選択肢の選択時にマウスオーバー処理をさせるために利用します。
MouseEnterイベントを利用するのは一般的なコントロール上であって、今回のようなFormしかないプログラムではすべてEnter状態になってしまいますから利用しません。
また、MouseClickイベントはどのコース、どの選択肢を選択したかを判断するために利用します。
もちろん、重要となるのはクリックしたときの座標ですね。
この座標を利用することにより選択肢等の判断をすることができます。
次回はゲームに関するクラスの生成・管理を行うゲームクラスを作成していきます。






