# Visual Basic > Games and Graphics Programming > Game Demos >  [VB.Net] Bouncy Ball

## dday9

*UPDATE*
See post #10 for the update.

This is the source code and the .exe

Features:
Multiple LevelsResembles the 'Brick Breaker' game

Drawbacks:
Not enough levels imo

Notes:
Eventually I will add some more levels. As of right now, the 5 levels are hard enough :PIf you want to increase the speed of the ball, I would suggest lowering the Timer's interval. You could change the Static X,Y if you want to though.


Full Project:
Bouncy Ball.zip

----------


## PowerProg

Very Good Game

Do you have a lesson for make games 2D or 3D ..
Can you tutorial me ..  :Confused:

----------


## dday9

Unfortunately I do not have any lessons for making 2/3d games. However, Jacob Romaine has a few lessons here in the forums. I would do a search for his name and take a look at his DirectX tutorials.

----------


## PowerProg

> Unfortunately I do not have any lessons for making 2/3d games. However, Jacob Romaine has a few lessons here in the forums. I would do a search for his name and take a look at his DirectX tutorials.


OK my friend , Thanks

----------


## dday9

*Update*

I created a new class named 'Brick'. It's basically a panel with one added property. That property sets if it is breakable or not. I also created 1 new level with the bricks that cannot be broken. It's nearly identical to level 4, only the bottom tier is unbreakable. I also changed Panel1 and Panel2 to pnl_Ball and pnl_Brick. The unbreakable bricks are gray whereas the breakable bricks are orange.

Game
Attachment 96631

----------


## dday9

Update! I got rid of the timer and use JMcIlhinney's method, here. The whole code won't fit in a post, so here is the project minus the .exe's:

bouncy ball.zip

----------


## sebastiantho

It's a simple, yet decent game. Although the controls of the player paddle is poor. I recommend using a Timer for movement, gives it a much more fluid and smoother movement. I think it would highly improve the experience.

----------


## Jacob Roman

> It's a simple, yet decent game. Although the controls of the player paddle is poor. I recommend using a Timer for movement, gives it a much more fluid and smoother movement. I think it would highly improve the experience.


Timers are hidious to use for games to be honest. I prefer a loop locked at 60 frames per second unless you use DirectX, then it locks it for you.

----------


## dday9

> It's a simple, yet decent game. Although the controls of the player paddle is poor. I recommend using a Timer for movement, gives it a much more fluid and smoother movement. I think it would highly improve the experience.


I can't remember off hand what I did for the paddle, but I believe that all I did was adjust the Left property of the control that represents the paddle. If I didn't do that, then I'm sure that the paddle's position would be in relation to the cursor's X coordinate. Either way both of those methods are tremendously better than using a timer to move the paddle.

If you'd like to read an article on why managed game loops are far superior to timers and to busy waits(like Jacob pointed out), I suggest that you read an article I posted a while back: http://ddaysnippet.netai.net/gameloop.html

----------


## dday9

*UPDATE!*

Here is the source(broken up into next several post):


```
Public Class Form1

#Region "Globals"
    Private bricks As List(Of Brick)
    Private brokenBricks As List(Of Brick)
    Private FramesPerMillisecond As Single
    Private lag As Integer
    Private level As Integer
    Private levels As List(Of String)
    Private lives As Integer
    Private gameThread As Threading.Thread
    Private paddle As Panel
    Private player As Ball
    Private quit As Boolean
    Private r As Random
    Private sw As Stopwatch

    Private Sub ResetGlobals()
        bricks = New List(Of Brick)

        brokenBricks = New List(Of Brick)

        FramesPerMillisecond = 16.6 '60 FPS

        level = -1

        levels = New List(Of String)

        'This is where you load all the levels
        levels.AddRange({My.Computer.FileSystem.ReadAllText(IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "level1.txt")), _
                         My.Computer.FileSystem.ReadAllText(IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "level2.txt")), _
                         My.Computer.FileSystem.ReadAllText(IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "level3.txt"))})

        lives = 3 '3 Lives

        quit = True

        r = New Random 'New random object

        Me.MoveNextLevel()
    End Sub
#End Region

#Region "Game Loops"

    Private Sub MainLoop()

        Dim s As Stopwatch = New Stopwatch
        s.Start()

        'Update
        If quit = False Then
            Me.ThreadMoveBall()
        End If
        If quit = False Then
            Me.HitBrick()
        End If
        If quit = False Then
            Me.HitWall()
        End If
        If quit = False Then
            Me.HitPaddle()
        End If
        If quit = False Then
            Me.CheckNextLevel()
        End If

        'Draw
        'Render

        'Loop
        While s.Elapsed <= TimeSpan.FromMilliseconds(8.3) '120 FPS
            Application.DoEvents()
        End While

        'Get FPS
        Console.WriteLine("Frames Per Second: " & (1000 / s.ElapsedMilliseconds))

        s.Stop()
        If quit = False Then
            gameThread = New Threading.Thread(AddressOf MainLoop)
            gameThread.Start()
        End If
    End Sub

#End Region

#Region "Physics"

    Private Sub HitBrick()
        Dim hitBrick As Brick = Nothing
        For Each item As Brick In bricks
            If player.Bounds.IntersectsWith(item.Bounds) AndAlso item.Visible = True Then
                Dim topSide As Rectangle = New Rectangle(item.Location, New Size(item.Width, 2))
                Dim bottomSide As Rectangle = New Rectangle(New Point(item.Location.X, item.Height), New Size(item.Width, 2))
                Dim leftSide As Rectangle = New Rectangle(item.Location, New Size(2, item.Height))
                Dim rightSide As Rectangle = New Rectangle(New Point(item.Right, item.Location.Y), New Size(2, item.Height))

                'Moving right/down, test top/left
                'Moving right/up, test bottom/left
                'Moving left/down, test top/right
                'Moving left/up, test bottom/right

                If player.HorizontalMove > 0 Then
                    If player.VerticalMove > 0 Then
                        'Moving right/down
                        If player.Bounds.IntersectsWith(topSide) Then
                            player.VerticalMove = -player.VerticalMove
                        ElseIf player.Bounds.IntersectsWith(leftSide) Then
                            player.HorizontalMove = -player.HorizontalMove
                        End If
                    Else
                        'Moving right/up
                        If player.Bounds.IntersectsWith(bottomSide) Then
                            player.VerticalMove = -player.VerticalMove
                        ElseIf player.Bounds.IntersectsWith(leftSide) Then
                            player.HorizontalMove = -player.HorizontalMove
                        End If
                    End If
                Else
                    If player.VerticalMove > 0 Then
                        'Moving left/down
                        If player.Bounds.IntersectsWith(topSide) Then
                            player.VerticalMove = -player.VerticalMove
                        ElseIf player.Bounds.IntersectsWith(rightSide) Then
                            player.HorizontalMove = -player.HorizontalMove
                        End If
                    Else
                        'Moving left/up
                        If player.Bounds.IntersectsWith(bottomSide) Then
                            player.VerticalMove = -player.VerticalMove
                        ElseIf player.Bounds.IntersectsWith(rightSide) Then
                            player.HorizontalMove = -player.HorizontalMove
                        End If
                    End If
                End If


                hitBrick = item
                Exit For
            End If
        Next

        If hitBrick IsNot Nothing AndAlso hitBrick.CanBreak Then
            hitBrick.Broken = True
            brokenBricks.Add(hitBrick)
        End If

    End Sub

    Private Sub HitWall()
        If player.Left <= 0 Then
            Me.ThreadZeroLeftBall()
        ElseIf player.Right >= Me.ClientSize.Width Then
            Me.ThreadZeroRightBall()
        ElseIf player.Top <= 0 Then
            Me.ThreadZeroTopBall()
        ElseIf player.Bottom >= Me.ClientSize.Height Then
            lives -= 1
            If lives > 0 Then
                Me.Reset()
            Else
                MessageBox.Show("Game Over.", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information)
                Me.ResetGlobals()
            End If
        End If
    End Sub

    Private Sub HitPaddle()
        If player.Bounds.IntersectsWith(paddle.Bounds) Then
            player.VerticalMove = -player.VerticalMove
        End If
    End Sub

#End Region

#Region "Game"

    Private Sub CheckNextLevel()
        Dim breakableBricks As Integer = bricks.Select(Function(b) b.CanBreak = True).Count
        If brokenBricks.Count = breakableBricks Then
            Me.ThreadRemoveBricks()
        End If
    End Sub

    Private Sub MoveBall()
        player.Left += player.HorizontalMove
        player.Top += player.VerticalMove
    End Sub

    Private Sub MoveNextLevel()
        level += 1
        If level < levels.Count Then
            Me.ParseMap(levels.Item(level), "'"c)
            Me.ThreadAddBricks()
        Else
            MessageBox.Show("You beat the game!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If
        Me.Reset()
    End Sub

    Private Sub RemoveBrick()
        Me.Controls.Remove(bricks.Item(0))
        bricks.RemoveAt(0)
    End Sub

    Private Sub Reset()
        quit = True

        Me.ThreadInvokePaddleAndPlayer()
        Me.ThreadMoveBall()

        gameThread.Abort()

    End Sub

    Private Sub ResetPaddle()
        With paddle
            .Left = (Me.ClientSize.Width \ 2) - (.Width \ 2)
            .Top = Me.ClientSize.Height - (.Height * 3)
        End With
    End Sub

    Private Sub ResetPlayer()
        With player
            .HorizontalMove = -2
            .Left = (Me.ClientSize.Width \ 2) - (.Width \ 2)
            .Top = paddle.Top - CInt(.Height * 1.3)
            .VerticalMove = -2
        End With
    End Sub

    Private Sub ZeroLeft()
        player.Left = 0 + lag
        player.HorizontalMove = -player.HorizontalMove
        lag = 0
    End Sub

    Private Sub ZeroRight()
        player.Left = Me.ClientSize.Width - player.ClientSize.Width - lag
        player.HorizontalMove = -player.HorizontalMove
        lag = 0
    End Sub

    Private Sub ZeroTop()
        player.Top = 0 + lag
        player.VerticalMove = -player.VerticalMove
        lag = 0
    End Sub

#End Region

#Region "Threading"

    Private Sub ThreadAddBricks()
        Me.BeginInvoke(Sub() AddBricks())
    End Sub

    Private Sub ThreadInvokePaddleAndPlayer()
        If player.InvokeRequired Then
            player.Invoke(New MethodInvoker(AddressOf ThreadInvokePaddleAndPlayer))
        Else
            Me.ResetPaddle()
        End If
        If paddle.InvokeRequired Then
            player.Invoke(New MethodInvoker(AddressOf ThreadInvokePaddleAndPlayer))
        Else
            Me.ResetPlayer()
        End If
    End Sub

    Private Sub ThreadMoveBall()
        If quit = False Then
            Me.BeginInvoke(Sub() Me.MoveBall())
        End If
    End Sub

    Private Sub ThreadRemoveBrick()
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf Me.ThreadRemoveBrick))
        Else
            Me.RemoveBrick()
        End If
    End Sub

    Private Sub ThreadRemoveBricks()
        Do
            Me.ThreadRemoveBrick()
        Loop Until bricks.Count = 0

        Me.MoveNextLevel()
    End Sub

    Private Sub ThreadZeroLeftBall()
        If player.InvokeRequired AndAlso quit = False Then
            lag += 1
            player.Invoke(New MethodInvoker(AddressOf ThreadZeroLeftBall))
        Else
            Me.ZeroLeft()
        End If
    End Sub

    Private Sub ThreadZeroRightBall()
        If player.InvokeRequired AndAlso quit = False Then
            lag += 1
            player.Invoke(New MethodInvoker(AddressOf ThreadZeroRightBall))
        Else
            Me.ZeroRight()
        End If
    End Sub

    Private Sub ThreadZeroTopBall()
        If player.InvokeRequired AndAlso quit = False Then
            lag += 1
            player.Invoke(New MethodInvoker(AddressOf ThreadZeroTopBall))
        Else
            Me.ZeroTop()
        End If
    End Sub

#End Region

#Region "Map"

    Private Sub ParseMap(ByVal contents As String, ByVal delimiter As Char)
        Dim brickSize As Size = New Size(50, 15)
        Dim lines() As String = contents.Split({Environment.NewLine}, StringSplitOptions.None)
        Dim y As Integer = 0

        For Each line As String In lines
            If String.IsNullOrWhiteSpace(line) Then
                y += brickSize.Height
            ElseIf line.StartsWith("//") Then
                'Do nothing, it's a comment
            Else
                Dim x As Integer = 0
                For Each item As String In line.Split({delimiter}, StringSplitOptions.None)
                    If item = "+" Then
                        Dim newBrick As Brick = New Brick
                        With newBrick
                            .CanBreak = True
                            .GradientBegin = Color.Chocolate
                            .GradientEnd = Color.SaddleBrown
                            .Location = New Point(x, y)
                            .Size = brickSize
                        End With

                        bricks.Add(newBrick)
                    ElseIf item = "=" Then
                        Dim newBrick As Brick = New Brick
                        With newBrick
                            .CanBreak = False
                            .GradientBegin = Color.Silver
                            .GradientEnd = Color.DarkGray
                            .Location = New Point(x, y)
                            .Size = brickSize
                        End With

                        bricks.Add(newBrick)
                    End If
                    x += brickSize.Width
                Next
                y += brickSize.Height
            End If
        Next

    End Sub

    Private Sub AddBricks()
        Me.Controls.AddRange(bricks.ToArray)
    End Sub

#End Region

#Region "Form Events"

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        quit = True
        If gameThread IsNot Nothing Then
            gameThread.Abort()
        End If
    End Sub

    Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        If quit = True AndAlso e.KeyCode = Keys.Space Then
            quit = False
            gameThread = New Threading.Thread(AddressOf MainLoop)
            gameThread.Start()
        ElseIf quit = False AndAlso e.KeyCode = Keys.A Then
            paddle.Left -= 5
        ElseIf quit = False AndAlso e.KeyCode = Keys.D Then
            paddle.Left += 5
        End If
    End Sub

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

        With Me
            .DoubleBuffered = True
            .FormBorderStyle = Windows.Forms.FormBorderStyle.FixedToolWindow
            .KeyPreview = True
            .Size = New Size(750, 750)
            .MaximumSize = .Size
            .StartPosition = FormStartPosition.CenterScreen
            .Text = "Bouncy Ball"
        End With

        paddle = New Panel
        With paddle
            .BackColor = Color.Black
            .Size = New Size(Me.ClientSize.Width \ 5, 6)
            .Left = (Me.ClientSize.Width \ 2) - (.Width \ 2)
            .Top = Me.ClientSize.Height - (.Height * 3)
        End With

        player = New Ball
        With player
            .BackColor = Color.Green
            .HorizontalMove = -1
            .Size = New Size(25, 25)
            .Left = (Me.ClientSize.Width \ 2) - (.Width \ 2)
            .Top = paddle.Top - CInt(.Height * 1.3)
            .VerticalMove = -1
        End With

        Me.Controls.AddRange({paddle, player})

        Me.ResetGlobals()
    End Sub

#End Region

End Class
```

----------


## dday9

```
Public Class Brick
    Inherits System.Windows.Forms.Control

    Private bWidth As Integer
    <System.ComponentModel.Description("Gets/Sets if the width of the border. If no border is desired set the BorderWidth to less than or equal to 0.")> _
    Public Property BorderWidth() As Integer
        Get
            Return bWidth
        End Get
        Set(ByVal value As Integer)
            bWidth = value
        End Set
    End Property

    Private broke As Boolean
    <System.ComponentModel.Description("Gets/Sets if the brick has been broken.")> _
    Public Property Broken() As Boolean
        Get
            Return broke
        End Get
        Set(ByVal value As Boolean)
            broke = value
            RaiseEvent BrokenChanged()
        End Set
    End Property

    Private break As Boolean
    <System.ComponentModel.Description("Gets/Sets if the brick can be broken whenever the ball hits it.")> _
    Public Property CanBreak() As Boolean
        Get
            Return break
        End Get
        Set(ByVal value As Boolean)
            break = value
        End Set
    End Property

    Private gBegin As Color
    <System.ComponentModel.Description("Gets/Sets if the starting color for the gradient background.")> _
    Public Property GradientBegin() As Color
        Get
            Return gBegin
        End Get
        Set(ByVal value As Color)
            gBegin = value
        End Set
    End Property

    Private gEnd As Color
    <System.ComponentModel.Description("Gets/Sets if the starting color for the gradient background.")> _
    Public Property GradientEnd() As Color
        Get
            Return gEnd
        End Get
        Set(ByVal value As Color)
            gEnd = value
        End Set
    End Property

    Public Event BrokenChanged As Action

    Private Sub Brick_BrokenChanged() Handles Me.BrokenChanged
        Me.ThreadBrokenChanged()
    End Sub

    Private Sub ThreadBrokenChanged()
        Me.BeginInvoke(Sub() ChangeVisible())
    End Sub

    Private Sub ChangeVisible()
        Me.Visible = Not broke
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        Dim drawRect As Rectangle = New Rectangle(New Point(0, 0), New Size(Me.ClientSize.Width, Me.ClientSize.Height))

        Using b As Drawing2D.LinearGradientBrush = New Drawing2D.LinearGradientBrush(New Point(0, 0), New Point(Me.Width, Me.Height), gBegin, gEnd)
            e.Graphics.FillRectangle(b, drawRect)
        End Using

        drawRect.Size = New Size(Me.ClientSize.Width - bWidth, Me.ClientSize.Height - bWidth)

        If bWidth > 1 Then
            drawRect.Location = New Point(bWidth \ 2, bWidth \ 2)
        End If

        If bWidth > 0 Then
            Using p As Pen = New Pen(Brushes.Black, bWidth)
                e.Graphics.DrawRectangle(p, drawRect)
            End Using
        End If

    End Sub

    Sub New()
        bWidth = 1
        break = True
        broke = False
        gBegin = Me.BackColor
        gEnd = Me.BackColor
    End Sub

End Class
```

----------


## dday9

```
Public Class Ball
    Inherits System.Windows.Forms.Control

    Private hMove As Integer
    <System.ComponentModel.Description("Gets/Sets the horizontal movement of the ball.")> _
    Public Property HorizontalMove() As Integer
        Get
            Return hMove
        End Get
        Set(ByVal value As Integer)
            hMove = value
        End Set
    End Property

    Private bMove As Integer
    <System.ComponentModel.Description("Gets/Sets the vertical movement of the ball.")> _
    Public Property VerticalMove() As Integer
        Get
            Return bMove
        End Get
        Set(ByVal value As Integer)
            bMove = value
        End Set
    End Property

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        Using b As SolidBrush = New SolidBrush(Me.BackColor)
            Using gp As Drawing2D.GraphicsPath = New Drawing2D.GraphicsPath
                e.Graphics.FillEllipse(b, Me.DisplayRectangle)
                gp.AddEllipse(Me.DisplayRectangle)
                Me.Region = New Region(gp)
            End Using
        End Using

    End Sub

End Class
```

----------


## dday9

To make the levels, the text content is coded like this:

//: Comment
+: Breakable Brick
=: Unbreakable Brick

And you can chose any delimiter that you want. In my examples I use the hyphen ' Each character represents the size of the brick in the ParseMap sub. Here are my first 3 levels that I made, just save them to MyDocuments and name them level1, level2, and level3:

level1:


```
// diamond shape with blockers



'''''''+

'''''+''+''+

'''+''+''+''+''+

'''''+''+''+

'''''''+
```

Level 2


```
// diamond shape with blockers



'''''''+
'''''''=

'''''+''+''+
'''''=''=''=

'''+''+''+''+''+
'''=''=''=''=''=

'''''+''+''+
'''''=''=''=

'''''''+
'''''''=
```

Level 3


```
// blocked square



'''='''''''=
'''='''''''=
'''='''''''=
'''=''+'+'+'+''=
'''='''''''=
'''=''+'+'+'+''=
'''='''''''=
'''=''+'+'+'+''=
'''='''''''=
'''=''+'+'+'+''=
'''='''''''=
'''=''+'+'+'+''=
'''='''''''=
'''='='='='='='='=
```

----------


## port12345

Thank you all for your valuable information.

----------


## dday9

Hey no problem! I'm glad that you enjoyed the info that I had. I also encourage you to check out my GDI+ 2d tile based map engine.

----------

