ブロック崩しについての質問 への返答

投稿で使用できる特殊コードの説明。(別タブで開きます。)
本名は入力しないようにしましょう。
投稿した後で削除するときに使うパスワードです。返答があった後は削除できません。
返答する人が目安にします。相手が小学生か社会人かで返答の仕方も変わります。
最初の投稿が質問の場合、質問者が解決時にチェックしてください。(以降も追加書き込み・返信は可能です。)
※「過去ログ」について書くときはその過去ログのURLも書いてください。

以下の返答は逆順(新しい順)に並んでいます。

投稿者 YAS  (社会人) 投稿日時 2010/8/26 23:32:07
うっかり返信してしまった。
失敗。
投稿者 YAS  (社会人) 投稿日時 2010/8/26 23:28:26
>Timerコントロールの作成文が変だな。
>だから、不具合がでるんだよ。 

私のコードに不具合がありましか?
一応動作確認はしているのですが。
投稿者 中島省吾  (社会人) 投稿日時 2010/8/26 21:59:03
Timerコントロールの作成文が変だな。
だから、不具合がでるんだよ。
投稿者 YAS  (社会人) 投稿日時 2010/8/25 08:00:18
今まで話の流れだと下のような感じでしょうか。(コピー&ペーストで動きます。)
もしかすると「Me.KeyPreview = True」が必要なのかもしれません。

Public Class Form1

    Dim Paddle As New PictureBox
    Dim Ball As New PictureBox
    Dim Balldx As Integer = 5
    Dim Balldy As Integer = 5
    Dim Paddledx As Integer
    Dim Key As Keys
    Dim WithEvents Timer As New Timer

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.DoubleBuffered = True
        Me.BackColor = Color.Black
        Me.Ball.Size = New Size(14, 14)
        Me.Ball.Image = New Bitmap(Me.Ball.Size.Width, Me.Ball.Size.Height)
        Using g As Graphics = Graphics.FromImage(Me.Ball.Image)
            g.FillEllipse(Brushes.White, g.VisibleClipBounds)
        End Using
        Me.Paddle.Size = New Size(50, 14)
        Me.Paddle.Image = New Bitmap(Me.Paddle.Size.Width, Me.Paddle.Size.Height)
        Using g As Graphics = Graphics.FromImage(Me.Paddle.Image)
            g.FillRectangle(Brushes.White, g.VisibleClipBounds)
        End Using
        Me.Paddle.Location = New Point((Me.ClientSize.Width - Me.Paddle.Width) / 2, Me.ClientSize.Height - Me.Paddle.Height)
        Me.KeyPreview = True
        Me.Timer.Interval = 10
        Me.Timer.Start()
    End Sub

    Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        Key = e.KeyCode
    End Sub

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        If Me.Paddle.Image IsNot Nothing AndAlso Me.Ball.Image IsNot Nothing Then
            e.Graphics.DrawImage(Me.Paddle.Image, Me.Paddle.Location)
            e.Graphics.DrawImage(Me.Ball.Image, Me.Ball.Location)
        End If
    End Sub

    Private Sub Timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer.Tick
        If Me.Ball.Location.X + Me.Ball.Size.Width > Me.ClientSize.Width OrElse Me.Ball.Location.X < 0 Then
            Me.Balldx *= -1
        End If
        If Me.Ball.Location.Y + Me.Ball.Size.Height > Me.ClientSize.Height OrElse Me.Ball.Location.Y < 0 Then
            Me.Balldy *= -1
        End If
        Me.Ball.Location += New Point(Me.Balldx, Me.Balldy)
        If Key = Keys.Right Then
            Me.Paddledx = DirectCast(IIf(Me.Paddle.Location.X + Me.Paddle.Size.Width >= Me.ClientSize.Width, 0, 3), Integer)
        ElseIf Key = Keys.Left Then
            Me.Paddledx = DirectCast(IIf(Me.Paddle.Location.X <= 0, 0, -3), Integer)
        End If
        Me.Paddle.Location += New Point(Me.Paddledx, 0)
        Me.Invalidate()
    End Sub

End Class
投稿者 TAKASI  (学生) 投稿日時 2010/8/23 11:56:04
お二方返答ありがとうございます。
るきおさんの方法を試させて貰ったのですが、変わらずkeydownイベントは動いていないようです。
そこでボタンを2つ作りそのボタンを押すと自機のx、y座標に3ずつ足す方法にしたところ、動いたのでそれで行こうと思います。
ただその方法だと自機がボールのスピードに追いつかないのでボタンを長押しして動くようにしたいのですがどんな方法がありますか?
投稿者 よねKEN  (社会人) 投稿日時 2010/8/22 23:34:22
PictureBoxを使うならImageプロパティを使うと便利ですよ。

■FormのLoadイベントで以下の処理を記述
Pic1.Image = New Bitmap(Pic1.ClientSize.Width, Pic1.ClientSize.Height) 

■Graphicsオブジェクトが必要な場面では以下の取得
Dim g As Graphics = Graphics.FromImage(Pic1.Image)

■何か描画処理を行った後は以下のような記述をしてPictureBoxの描画を更新しておく
Pic1.Invalidate()

投稿者 るきお  (社会人) 投稿日時 2010/8/22 10:21:27
推測で申し訳ないですが、複数の場所でCreateGraphicsしているので結果が合成されていないのだと思います。

ゲームのような描画処理が必要な場合は、Pic1のPaintイベントで描画してください。
KeyDownイベントやballmoveメソッドから描画を行いたい場合はPic1.Invalidateを呼び出します。
Invalidateを呼び出すとPaintイベントが呼び出されます。

Paintイベント内ではe.Graphicsに対して描画を命令します。
このようにすることで描画が一か所に集約されうまくつじつまがあいます。

なお、さらに一歩進めると描画処理はKeyDownやballmoveからは行わずTimer1_Timerイベント内でのみPic1.Invalidateを呼び出すようにすることをお勧めします。
KeyDownは押されたキーを記憶しておくだけ、ballmoveは新しい座標を計算しておくだけで、
それを描画するのはPic1_Paintイベント、描画を呼び出すのPic.Invalidate)はTimer1_Timerイベントだけという仕組みです。

このような基本的な枠組み(=フレームワーク)はゲーム作りでは最重要です。この枠組み次第では簡単なゲームもやけに難しいプログラムになったりどうしても作れくなったりしますが、枠組みがしっかりしていればある程度複雑なゲームでも驚くほど簡単に作成できることもあります。

具体例はサンプルで公開しているシューティングゲームを参考にしてください。
http://homepage1.nifty.com/rucio/main/DownLoad/Index_dl.htm

追加で質問などあればいつでもどうぞ。
ゲーム作りがんばってください!

…完成したらコンテストに応募してもらえるんですよね…??
投稿者 TAKASI  (学生) 投稿日時 2010/8/22 00:14:46
VB2008でブロック崩しを作っているのですが、玉は動くようになり次に自機を動かそうと思いやってみたのですが動きません。
もう一つプロジェクトを作って自機を動かす文だけでやってみるときちんと動きます。
あと、keydownイベント内にブレークポイントを作ってみたのですが反応はありませんでした。
長くなってしまうのでballmoveとkeydownイベントだけにしました。簡単に言うとボタン1が押されるとタイマーが動き出し、タイマーが刻むたびにballmove()関数がうごきます。
何が問題だと思われますか?何でもいいのでアドバイスお願いします

Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        Dim g As Graphics = Pic1.CreateGraphics()
        g.DrawImage(picback, zikiX, zikiY, rect, GraphicsUnit.Pixel)
        Select Case e.KeyCode
            Case Keys.Left : zikiX -= 3
            Case Keys.Right : zikiX += 3
        End Select
        g.DrawImage(ziki, zikiX, zikiY)
        g.Dispose()
    End Sub

Private Sub ballmove()
        Dim g As Graphics = Pic1.CreateGraphics()
                rec.X = x : rec.Y = y : rec.Width = 28 : rec.Height = 28
        g.DrawImage(picback, x, y, rec, GraphicsUnit.Pixel)
        g.DrawImage(ziki, zikiX, zikiY)
        If x + 28 >= Pic1.Width Then
            flag_x = False
        ElseIf x < 1 And flag_x = False Then
            flag_x = True
        End If
        If y + 28 >= Pic1.Height Then
            flag_y = False
        ElseIf y < 1 And flag_y = False Then
            flag_y = True
        End If
        If flag_x = True The
            x = x + 3
        Else
            x = x - 3
        End If
        If flag_y = True Then
            y = y + 3
        Else
            y = y - 3
        End If
        g.DrawImage(tama, x, y)
        g.Dispose()
    End Sub