クラスの実装の仕方

タグの編集
投稿者 H_K  (社会人) 投稿日時 2009/9/13 04:38:24
長文です。ごめんなさい。(2008 Express Editionを使用しています。)

VB中学校初級講座 2章第14回の実技「ちんちろりん」をちょっとだけ改造して、画像を読み込むのではなく、1章で習った円や四角を描く方法でやってみました。PictureBox1を張り付けて、第2回で紹介されているAutoGraphicsを使っています。

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        '最初はピンゾロ表示
        For i As Integer = 1 To 3
            SaikoroDraw(1, i)           '引数:目,場所(=左から1,2,3)
        Next

End Sub

    Private Sub SaikoroDraw(ByVal deme As Integer, ByVal x As Integer)
        x -= 1                'Pictureboxの座標は0からなので-1
        Select Case deme
            Case 1
                SaikoroDraw1(x)
            Case 2
                SaikoroDraw2(x)
            Case 3
                SaikoroDraw3(x)
            Case 4
                SaikoroDraw4(x)
            Case 5
                SaikoroDraw5(x)
            Case 6
                SaikoroDraw6(x)
        End Select

    End Sub

    Private Sub SaikoroDraw1(ByVal x As Integer)
        Dim g As Graphics = AutoGraphics(PictureBox1)
        g.DrawRectangle(Pens.Black, 0 + x * 80, 0, 60, 60)
        g.FillRectangle(Brushes.White, 1 + x * 80, 1, 58, 58)
        g.DrawEllipse(Pens.Black, 25 + x * 80, 25, 10, 10)
        g.FillEllipse(Brushes.Red, 25 + x * 80, 25, 10, 10)
    End Sub

    Private Sub SaikoroDraw2(ByVal x As Integer)
        Dim g As Graphics = AutoGraphics(PictureBox1)
        g.FillRectangle(Brushes.White, 1 + x * 80, 1, 58, 58)
        g.FillEllipse(Brushes.Black, 12 + x * 80, 12, 10, 10)
        g.FillEllipse(Brushes.Black, 38 + x * 80, 38, 10, 10)

    End Sub

    Private Sub SaikoroDraw3(ByVal x As Integer)
        Dim g As Graphics = AutoGraphics(PictureBox1)
        SaikoroDraw2(x)
        g.FillEllipse(Brushes.Black, 25 + x * 80, 25, 10, 10)
    End Sub

    Private Sub SaikoroDraw4(ByVal x As Integer)
        Dim g As Graphics = AutoGraphics(PictureBox1)
        SaikoroDraw2(x)
        g.FillEllipse(Brushes.Black, 38 + x * 80, 12, 10, 10)
        g.FillEllipse(Brushes.Black, 12 + x * 80, 38, 10, 10)

    End Sub

    Private Sub SaikoroDraw5(ByVal x As Integer)
        Dim g As Graphics = AutoGraphics(PictureBox1)
        SaikoroDraw4(x)
        g.FillEllipse(Brushes.Red, 25 + x * 80, 25, 10, 10)
    End Sub

    Private Sub SaikoroDraw6(ByVal x As Integer)
        Dim g As Graphics = AutoGraphics(PictureBox1)
        SaikoroDraw4(x)
        g.FillEllipse(Brushes.Black, 12 + x * 80, 25, 10, 10)
        g.FillEllipse(Brushes.Black, 38 + x * 80, 25, 10, 10)

    End Sub
    '自動再描画
    Private Function AutoGraphics(ByVal picSource As PictureBox) As Graphics
        If picSource.Image Is Nothing Then
            picSource.Image = New Bitmap(picSource.ClientRectangle.Width, picSource.ClientRectangle.Height)
        End If

        Return Graphics.FromImage(picSource.Image)

    End Function

それでまぁ、考えていた事はできて満足していたのですが、第6章52回 実技4「オセロ」を見ると全然違う方法みたいです。
で、まねしてサイコロをクラスにして、
    Dim MyDice As New dice
    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint

        Mydice.Draw(e.Graphics)

    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        '最初はピンゾロ表示
        For i As Integer = 1 To 3
            Mydice.SpotSet(1, i)     '目を1に設定する?
        Next
       PictureBox1.Invalidate()

End Sub

Public Class dice
    Public Sub Draw(ByVal g As Graphics)

    End Sub
End Class

ここまでやってはたと手が止まってしまいました。オセロをよく読み返したり、4章の内容を読み返したりしてみたのですが、頭がこんがらがるばかりでどうしていいかわかりません。
どのように書いたらいいかご教授いただければ幸いです。
投稿者 mitchin  (社会人) 投稿日時 2009/9/13 08:07:03
その前に SaikoroDraw1~SaikoroDraw6 メソッド内部の g は 引数で受けるようにしたほうがいいですね。(多重生成される場合があるので)

サイコロをクラスにするなら
 1. 出目の値
 2. 描画処理
があればいいかなぁ。

例えば、自分なら拡張性を考慮してこんな感じで実装します。
''' <summary>サイコロを表すクラスです。</summary> 
''' <remarks></remarks> 
Public Class Dice
#Region " プライベートフィールド "
  Dim _Deme, _MaxDeme As Integer    '現在の出目, 出目の最大値 
#End Region

#Region " パブリックコンストラクタ "
  ''' <summary>Dice クラスの新しいインスタンスを初期化します。</summary> 
  ''' <remarks></remarks> 
  Public Sub New()
    MyClass.New(6)
  End Sub
#End Region

#Region " プロテクトコンストラクタ "
  ''' <summary>出目の最大値を指定して Dice クラスの新しいインスタンスを初期化します。</summary> 
  ''' <param name="MaxDeme">出目の最大値。</param> 
  ''' <remarks></remarks> 
  Protected Sub New(ByVal MaxDeme As Integer)
    _MaxDeme = MaxDeme
  End Sub
#End Region

#Region " パブリックプロパティ "
  ''' <summary>現在の出目を取得します。</summary> 
  ''' <value></value> 
  ''' <returns>現在の出目。</returns> 
  ''' <remarks></remarks> 
  Public ReadOnly Property Deme() As Integer
    Get
      Return _Deme
    End Get
  End Property
#End Region

#Region " プロテクトプロパティ "
  ''' <summary>出目の最大値を取得します。既定値は 6 です。</summary> 
  ''' <value></value> 
  ''' <returns>出目の最大値。</returns> 
  ''' <remarks></remarks> 
  Protected ReadOnly Property MaxDeme() As Integer
    Get
      Return _MaxDeme
    End Get
  End Property
#End Region

#Region " パブリックメソッド "
  ''' <summary>指定した Graphics オブジェクトで指定した出目を描画します。</summary> 
  ''' <param name="g">描画対象の Graphics オブジェクト。</param> 
  ''' <param name="deme">出目。</param> 
  ''' <remarks></remarks> 
  Public Sub Draw(ByVal g As Graphics, ByVal deme As Integer)
    If g Is Nothing Then
      Throw New ArgumentNullException("g")
    End If
    If (deme < 1) OrElse (deme > _MaxDeme) Then
      Throw New ArgumentOutOfRangeException("deme")
    End If

    If deme = _Deme Then    '現在の出目を指定した場合は処理しない 
      Return
    End If
    _Deme = deme    '出目をセット 
    Call DrawDeme(g)    '現在の出目を描画 
  End Sub
#End Region

#Region " プロテクトメソッド "
  ''' <summary>指定した Graphics オブジェクトで現在の出目を描画します。 
  ''' 派生クラスで使用する場合は必ず基本クラスのメソッドを呼び出してください。</summary> 
  ''' <param name="g">描画対象の Graphics オブジェクト。</param> 
  ''' <remarks></remarks> 
  Protected Sub DrawDeme(ByVal g As Graphics)
    'ToDo _Deme が 1 から 6 までの場合の出目を描画する 
  End Sub
#End Region
End Class


Form 側はこんな感じ(AutoGraphics メソッドは省略しています)
  ReadOnly LeftDice, CenterDice, RightDice, Dices() As Dice

  Public Sub New()
    ' この呼び出しは、Windows フォーム デザイナで必要です。 
    InitializeComponent()
    ' InitializeComponent() 呼び出しの後で初期化を追加します。 

    LeftDice = New Dice()
    CenterDice = New Dice()
    RightDice = New Dice()
    Dices = New Dice() {LeftDice, CenterDice, RightDice}
  End Sub

  Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load
    Using g As Graphics = AutoGraphics(Me.PictureBox1)
      For Each MyDice As Dice In Dices
        MyDice.Draw(g, 1)
      Next
    End Using
  End Sub
投稿者 mitchin  (社会人) 投稿日時 2009/9/13 08:43:00
1 箇所間違っていました。


Protected Sub DrawDeme(ByVal g As Graphics)


Protected Overridable Sub DrawDeme(ByVal g As Graphics)

オーバーライドできないと拡張できませんよね。
投稿者 八王子5517  (小学生) 投稿日時 2009/9/13 13:12:00
そのサイコロとやらさぁ、ペイントの中に全部書けば?
Basicは処理速度が遅いのに、余計の遅くなる感じ。
投稿者 H_K  (社会人) 投稿日時 2009/9/14 04:46:32
mitchinさん、貴重な時間をさいてサンプルプログラムを提供していただき、ありがとうごさいます。
初級講座の内容をかなり超えている部分があるみたいで、まだ理解できているとは言い難いですが、これから少しずつ調べてみます。人の書いたコードを見るのはものすごく勉強になります。

ただその、質問の仕方が悪かったのですが、AutoGraphicsは使用せず、PictureBox1_PaintメソッドでMydice.Draw(e.Graphics)とだけ記述して他のイベントプロシージャからはDiceクラスの目を設定して PictureBox1.Invalidate()とやろうと思ったんです。
Drawメソッドにe.Graphicsを渡しているので、目を描画する処理はDrawメソッドに書かなきゃいけないのはわかるのですが、目を設定するメソッドを作ったらeは渡せないし、あれぇ?というそんなレベルだったのです。

八王子5517さん、この場合は純粋に学習のためのプログラムで、処理速度はあまり重要ではないのです。
それに単純な丸と四角を描くだけなので、先にやった方法で、3つのサイコロをランダムに目を変えながら順番に止まっていく、みたいな処理くらいでは遅くて困ると言う感じではありませんでした。
しかしながら、そういう観点からの発想は全くなかったので、貴重な意見として伺っておきます。ありがとうございました。
そもそも、 PictureBox1.Invalidate()を、オセロを見るまで知らなかったのでペイントの中に書くという発想自体がありませんでした。
もしそうやるとすると、他のプロシージャからはグローバル変数に出目とかをセットしてペイントの中で分岐していけばいいわけですか。

と、書いていて気づきましたが、クラス側のグローバル変数を使えば同じ事ができるのかも。mitchinさんのサンプルを参考(マネとも言う)にしながらいろいろといじくってみます。
お二人ともありがとうございました。


投稿者 八王子5517  (中学生) 投稿日時 2009/9/14 17:52:15
ちょっと作りました。
サイコロは3個ですけど、100個でも増やせます。
出目は1~3。完成させてください。(宿題^^)
'Vb2008EE 
Public Class Form1
    Const sikoro_suu As Integer = 3
    Dim sikoro(sikoro_suu - 1) As Integer
    Dim sikoroRandom As System.Random

    Private Sub Form1_Load(ByVal sender As ObjectByVal e As System.EventArgs) Handles Me.Load
        Me.BackColor = Color.Green
        Me.Size = New Size(300, 300)
        Button1.Location = New Point(190, 20)
        Button1.Text = "Start"
        Button2.Location = New Point(190, 50)
        Button2.Text = "Stop"
        PictureBox1.Location = New Point(10, 10)
        PictureBox1.BackColor = Color.Gray
        PictureBox1.Size = New Size(265, 245)
        sikoroRandom = New System.Random()
        Timer1.Interval = 70
        sikoro(0) = 1 : sikoro(1) = 1 : sikoro(2) = 1
    End Sub

    Private Sub PictureBox1_Paint(ByVal sender As ObjectByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        Dim g As Graphics = e.Graphics
        g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
        For i As Integer = 0 To 2
            g.FillRectangle(Brushes.White, 20 + i * 100, 20 + i * 90, 30, 30)
            Select Case sikoro(i)
                Case 1 'ダイス目__1 
                    g.FillEllipse(Brushes.Red, 26 + i * 100, 26 + i * 90, 18, 18)
                Case 2 'ダイス目__2 
                    g.FillEllipse(Brushes.Black, 23 + i * 100, 23 + i * 90, 10, 10)
                    g.FillEllipse(Brushes.Black, 37 + i * 100, 37 + i * 90, 10, 10)
                Case 3 'ダイス目__3 
                    For z As Integer = 0 To 2
                        g.FillEllipse(Brushes.Blue, (40 - (9 * z)) + i * 100, 23 + (8 * z) + i * 90, 8, 8)
                    Next
            End Select
        Next
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Timer1.Tick
        For i As Integer = 0 To 2
            sikoro(i) = sikoroRandom.Next(1, 4)
        Next
        PictureBox1.Invalidate()
    End Sub

    Private Sub Button1_Click(ByVal sender As ObjectByVal e As System.EventArgs) Handles Button1.Click
        Timer1.Start()
    End Sub

    Private Sub Button2_Click(ByVal sender As ObjectByVal e As System.EventArgs) Handles Button2.Click
        Timer1.Stop()
    End Sub
End Class

投稿者 H_K  (社会人) 投稿日時 2009/9/15 20:21:46
八王子5517さん、この前の書き込みの後、とりあえずクラスを作らずペイントで描いたらどうなるかやってみました。講座のサンプル以外自分でクラスを作ったことがないので、クラスの方はこれからです。
AutoGraphics メソッドだと前描いたのが残っているのに、paint イベントだと毎回クリアされちゃうんですね。細かい修正が必要でした。
これだと元々の講座のサンプルのようにピクチャーボックスを3つ使った方がよかったかもしれません。

サイコロ描画の部分だけ抜き出してみました。長くてすみません。

[CODE]
Public Class Form1
    Dim Rnd As New Random
    Dim watch As New Stopwatch
    Dim Mydice(2) As Integer

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Panel1の中にPictureBox1を配置。位置は適当に
        '他にButton1とlabel1, タイマーが3つ

        Panel1.Size = New Size(259, 119)
        Panel1.BorderStyle = BorderStyle.Fixed3D
        Panel1.BackColor = Color.FromArgb(0, 192, 0)
        PictureBox1.Size = New Size(221, 61)

        '最初はピンゾロ表示
        For i As Integer = 0 To 2
            Mydice(i) = 1
        Next
        PictureBox1.Invalidate()

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        watch.Reset()
        watch.Start()
        Timer1.Interval = 20
        Timer2.Interval = 20
        Timer3.Interval = 20
        Timer1.Start()
        Timer2.Start()
        Timer3.Start()

    End Sub
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        '1秒たったら表示をスローに
        Static tcount As Integer = 50
        If watch.Elapsed.Seconds >= 1 Then
            tcount += 50
            Timer1.Interval = tcount
        End If
        'インターバルが500になったら表示決定
        If tcount >= 500 Then
            Timer1.Stop()
            tcount = 50
            Label1.Text = "チン"
        End If
        '目をランダムに表示
        Mydice(0) = Rnd.Next(1, 7)
        PictureBox1.Invalidate()
    End Sub

    Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
        '2秒たったら表示をスローに
        Static tcount As Integer = 50
        If watch.Elapsed.Seconds >= 2 Then
            tcount += 50
            Timer2.Interval = tcount
        End If
        If tcount >= 500 Then
            Timer2.Stop()
            tcount = 50
            Label1.Text = "チンチロ"
        End If

        Mydice(1) = Rnd.Next(1, 7)
        PictureBox1.Invalidate()
    End Sub

    Private Sub Timer3_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer3.Tick
        Static tcount As Integer = 50

        '3秒たったら表示をスローに
        If watch.Elapsed.Seconds >= 3 Then
            tcount += 50
            Timer3.Interval = tcount
        End If

        Mydice(2) = Rnd.Next(1, 7)
        PictureBox1.Invalidate()

        If tcount >= 500 Then
            Timer3.Stop()
            tcount = 50
            Label1.Text = "チンチロリ~ン"
        End If
    End Sub

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
        For i As Integer = 0 To 2
            SaikoroDraw(Mydice(i), i, e.Graphics)
        Next
    End Sub
[/code]
続く
投稿者 H_K  (社会人) 投稿日時 2009/9/15 20:23:42
続き。
    Public Sub SaikoroDraw(ByVal deme As IntegerByVal x As IntegerByVal g As Graphics)

        Select Case deme
            Case 1
                SaikoroDraw1(x, g)
            Case 2
                SaikoroDraw2(x, g)
            Case 3
                SaikoroDraw3(x, g)
            Case 4
                SaikoroDraw4(x, g)
            Case 5
                SaikoroDraw5(x, g)
            Case 6
                SaikoroDraw6(x, g)
        End Select

    End Sub

    Private Sub SaikoroDraw1(ByVal x As IntegerByVal g As Graphics)

        g.DrawRectangle(Pens.Black, 0 + x * 80, 0, 60, 60)
        g.FillRectangle(Brushes.White, 1 + x * 80, 1, 58, 58)
        g.DrawEllipse(Pens.Black, 25 + x * 80, 25, 10, 10)
        g.FillEllipse(Brushes.Red, 25 + x * 80, 25, 10, 10)
    End Sub

    Private Sub SaikoroDraw2(ByVal x As IntegerByVal g As Graphics)

        g.DrawRectangle(Pens.Black, 0 + x * 80, 0, 60, 60)
        g.FillRectangle(Brushes.White, 1 + x * 80, 1, 58, 58)
        g.FillEllipse(Brushes.Black, 12 + x * 80, 12, 10, 10)
        g.FillEllipse(Brushes.Black, 38 + x * 80, 38, 10, 10)

    End Sub

    Private Sub SaikoroDraw3(ByVal x As IntegerByVal g As Graphics)

        SaikoroDraw2(x, g)
        g.FillEllipse(Brushes.Black, 25 + x * 80, 25, 10, 10)
    End Sub

    Private Sub SaikoroDraw4(ByVal x As IntegerByVal g As Graphics)

        SaikoroDraw2(x, g)
        g.FillEllipse(Brushes.Black, 38 + x * 80, 12, 10, 10)
        g.FillEllipse(Brushes.Black, 12 + x * 80, 38, 10, 10)

    End Sub
    Private Sub SaikoroDraw5(ByVal x As IntegerByVal g As Graphics)

        SaikoroDraw4(x, g)
        g.FillEllipse(Brushes.Red, 25 + x * 80, 25, 10, 10)
    End Sub

    Private Sub SaikoroDraw6(ByVal x As IntegerByVal g As Graphics)

        SaikoroDraw4(x, g)
        g.FillEllipse(Brushes.Black, 12 + x * 80, 25, 10, 10)
        g.FillEllipse(Brushes.Black, 38 + x * 80, 25, 10, 10)

    End Sub
End Class
投稿者 (削除されました)  () 投稿日時 2009/9/16 22:26:21
(削除されました)
投稿者 (削除されました)  () 投稿日時 2009/9/17 09:26:31
(削除されました)