# Visual Basic > Games and Graphics Programming > Game Demos >  [VB.Net] Asteroid Avoid

## dday9

Here is a neat little console application that Funky Dexter inspired me to create called Asteroid Avoid.

*Purpose:*
Avoid hitting the asteroids
*Objects:*
The space ship: ^
The asteroids: *
*Controls:*
Left Arrow: Moves the space ship to the left
Right Arrow: Moves the space ship to the right
*Source:*


```
Option Strict On
Option Explicit On

Imports System.ComponentModel
Module Module1

    Private Structure Obj
        Private p_Character As Char
        <Description("Gets/Sets the displaying character of the object.")> _
        Public Property Character() As Char
            Get
                Return p_Character
            End Get
            Set(ByVal value As Char)
                p_Character = value
            End Set
        End Property

        Private p_X As Integer
        <Description("Gets/Sets the X position of the object.")> _
        Public Property X() As Integer
            Get
                Return p_X
            End Get
            Set(ByVal value As Integer)
                p_X = value
            End Set
        End Property

        Private p_Y As Integer
        <Description("Gets/Sets the Y position of the object.")> _
        Public Property Y() As Integer
            Get
                Return p_Y
            End Get
            Set(ByVal value As Integer)
                p_Y = value
            End Set
        End Property
    End Structure

    Private Structure Size

        Private hei As Integer
        <Description("Gets/Sets the vertical size mesured in pixels.")> _
        Public Property Height() As Integer
            Get
                Return hei
            End Get
            Set(ByVal value As Integer)
                hei = value
            End Set
        End Property

        Private wid As Integer
        <Description("Gets/Sets the horizontal size mesured in pixels.")> _
        Public Property Width() As Integer
            Get
                Return wid
            End Get
            Set(ByVal value As Integer)
                wid = value
            End Set
        End Property

    End Structure

    'Globals
    Private asteroids As List(Of Obj) 'The list that will store our asteroids
    Private keyThread As Threading.Thread 'The thread that will check which key was hit
    Private gameThread As Threading.Thread 'The game loop thread
    Private playing As Boolean 'The boolean that will keep track of if we're playing the game
    Private r As Random 'The random variable that will ultimately deterime the X coordinate of the asteroids
    Private screenSize As Size 'The playing area
    Private ship As Obj 'The ship that the user will move around

    Sub Main()
        'Setup the title of our window
        Console.Title = "Asteroid Avoid"
        'Set the random to a new random object
        r = New Random

        'Loop until the user closes out
        Do
            'Tell the user to start a new game
            Console.WriteLine("Press any key to start a new game.")
            Console.ReadKey()

            'Start the new game and loop until the user loses
            Call NewGame()
            Do
            Loop Until Not playing
        Loop
    End Sub

    Private Sub NewGame()
        'Create new instances of the objects
        asteroids = New List(Of Obj)
        keyThread = New Threading.Thread(AddressOf KeyInput)
        gameThread = New Threading.Thread(AddressOf GameLoop)
        playing = True
        screenSize = New Size
        ship = New Obj

        'Set the playing area
        With screenSize
            .Height = 10
            .Width = 20
        End With

        'Set the ship's properties
        With ship
            .Character = "^"c
            .X = screenSize.Width \ 2
            .Y = screenSize.Height
        End With

        'Draw the user on the screen
        Call Draw()

        'Start listening for the keys that were hit and also the game loop
        keyThread.Start()
        gameThread.Start()
    End Sub

    Private Sub MoveAsteroids()
        'Move each asteroid's Y position down 1
        For i As Integer = asteroids.Count - 1 To 0 Step -1
            Dim asteroid As Obj = asteroids.Item(i)
            asteroid.Y += 1

            asteroids.Item(i) = asteroid
        Next
    End Sub

    Private Sub NewAsteroid()
        'Create a new asteroid at a random X position
        Dim newbie As Obj = New Obj

        With newbie
            .Character = "*"c
            .X = r.Next(0, screenSize.Width + 1)
            .Y = 0
        End With

        asteroids.Add(newbie)
    End Sub

    Private Sub CheckCollision()
        'Check if the asteroid has hit the bottom of the screen or hit the user
        'If it hits the bottom of the screen remove it
        'If it hist the user then stop the game
        For i As Integer = asteroids.Count - 1 To 0 Step -1
            Dim asteroid As Obj = asteroids.Item(i)

            If asteroid.Y = screenSize.Height + 1 Then
                asteroids.RemoveAt(i)
            ElseIf asteroid.X = ship.X AndAlso asteroid.Y = ship.Y Then
                playing = False
            End If
        Next
    End Sub

    Private Sub Draw()
        'Draw the user and all the asteroids
        Console.Clear()

        Console.CursorLeft = ship.X
        Console.CursorTop = ship.Y
        Console.Write(ship.Character)

        For Each asteroid As Obj In asteroids
            Console.CursorLeft = asteroid.X
            Console.CursorTop = asteroid.Y
            Console.Write(asteroid.Character)
        Next
    End Sub

    Private Sub GameLoop()
        Dim s As New Stopwatch
        s.Start()

        Call MoveAsteroids()

        If CBool(DateTime.Now.Millisecond Mod 2) Then
            Call NewAsteroid()
        End If

        Call Draw()

        Call CheckCollision()

        'Loop
        While s.Elapsed <= TimeSpan.FromMilliseconds(51)
        End While

        s.Stop()

        Debug.WriteLine(s.ElapsedMilliseconds)

        If playing = True Then
            gameThread = New Threading.Thread(AddressOf GameLoop)
            gameThread.Start()
        Else
            Console.Clear()
            Console.WriteLine("Press any key to start a new game.")
            Console.ReadKey()
        End If
    End Sub

    Private Sub KeyInput()
        Do
            Dim info As ConsoleKeyInfo = Console.ReadKey
            If info.Key = ConsoleKey.LeftArrow AndAlso ship.X - 1 >= 0 Then
                ship.X -= 1
            ElseIf info.Key = ConsoleKey.RightArrow AndAlso ship.X + 1 <= screenSize.Width Then
                ship.X += 1
            End If
        Loop Until Not playing
    End Sub

End Module
```

To set it up, simply start a new console application and copy/paste the source code into the module.

*Update*
As Si_The_Geek pointed out, my prior code was designed for VS2010+. So I've changed the code to where it will compile on any version of VS that targets the 3.0 .Net framework and up.

----------


## Nightwalker83

Which version of VB.Net? I just tried creating the game in 2008 but received 31 errors.

----------


## si_the_geek

It works fine in VB 2010, but 2008 will have problems due to the single-line properties.

I think it will work for 2008 if you expand all of the properties, eg:


```
  Public Property Character As Char
```

...becomes:


```
  Private _Character as Char
  Public Property Character As Char
    Get
      Return _Character
    End Get
    Set(ByVal value As Char)
      _Character = value
    End Set
  End Property
```


In terms of the game itself, it is good... but I think the playing area is a bit too small (it seems to be about 10 characters high, when the window allows about 20), and the speed is a bit quick (half the speed would probably be about right)

----------


## dday9

@nightwalker,
As si pointed out, in 2008 you're not able to use single line properties which is what is probably causing those errors.

@si
I figured the area would be to small, the size can always be adjusted. Same with the speed, but I think with a larger are the speed should probably be fine.

----------


## Nightwalker83

> @nightwalker,
> As si pointed out, in 2008 you're not able to use single line properties which is what is probably causing those errors.


Maybe it would be a good idea to append the version info  VB2010, 2012, etc to the thread title?

----------


## dday9

Nah, what I'll do is adjust the code to work for 2008 on up

----------


## passel

I noticed that the update was a bit ragged, and there were long periods where I had no asteroids, and then a sudden flurry.
The reason for the long gaps, sometimes six seconds or more, was because the machine I'm on could have long periods of time where the millisecond number was even.
I added code to check not only the elapsed time within the thread, as you had, but also the elapsed time between threads. While you had the thread use 51 milliseconds of time, most of the time, there could be 10 to 200 milliseconds between when one thread ended and the new thread was created and running, thus not very regulated frame times.
Should not really need to end one thread and start another for every frame of the game loop. That is just a lot of extra work for the OS to do and clean up after.
The primary issue was the empty loop in the main launching loop.


```
'...
      Call NewGame()
      Do
      Loop Until Not playing
```

This ensures that one CPU core is spending 100% of its time executing that loop, unless interrupted by the OS. Your other two threads (along with the rest of the system) have to share the remaining cores.  Since that thread is doing absolutely nothing until the game is over, put a Threading.Thread.Sleep(100) in there so it checks to see if the game is over 10 times a second, but makes the core available the rest of the time, really helps the situation tremendously. Checking for game completion at 10hz is plenty fast for that situation.

To make the asteroids more deterministic, rather than launching them based on time of day clock, which can give really long runs of the bit being set or clear, just use your random object. I put this in so the asteroids would be added 2 times out of 3. You could up the percentage if you want more a denser field.


```
'...
      If r.Next(3) < 2 Then
        Call NewAsteroid()
      End If
```

I added a trigger value that increments at a constant rate, so the frame rate should be a constant interval automatically adjusting for variations in drawing times and other random delays. I compared the times frame to frame, and rather than the ragged values before, it was now right on the millisecond every time.
Of course, now that every frame was exactly 51 milliseconds, instead of sometimes (because of random sized large times between the destruction of one thread and the creation of the next), the speed was consistent and faster than the average you had before.  I went ahead and changed it to run at 10hz, rather than 20hz, which suited my reaction time better. You can adjust that by changing the trigger increment. (perhaps even start slower and ramp up over time).

So, the gameloop is launched and loops within the thread (I assume you had it like that originally) and is very steady.

Getting it to start the next game with one Key Input was a little bit of a challenge. When the game is over, the main loop will be released and get to the console input. The KeyInput thread will also be waiting at a console input (and you also had the game loop waiting at a console input as well). So it could take several key presses to get the next game going.
Since the KeyInput is most likely going to get stuck at the ReadKey, I added a flag to know when the KeyInput had actually left, and could test for that in the main loop, and either do the KeyInput there, or not if the KeyInput thread was already waiting for a key input. Where the flag was set to false took more than one shot to figure out. Where you would think it should go, up in the If conditions where it is checked, wouldn't work, because the parallel threads, allowed it be true from the last exit because of the way the thread executions overlap. I could try to explain it in detail, but I think that it is a little hard without some kind of timing diagram to lay it out.

Left most of the old lines in the source, commented out so you can see the changes in one place.  You can clean it up and update the first post, if you want.


```
Option Strict On
Option Explicit On

Imports System.ComponentModel
Module Module1

  Private Structure Obj
    Private p_Character As Char
    <Description("Gets/Sets the displaying character of the object.")> _
    Public Property Character() As Char
      Get
        Return p_Character
      End Get
      Set(ByVal value As Char)
        p_Character = value
      End Set
    End Property

    Private p_X As Integer
    <Description("Gets/Sets the X position of the object.")> _
    Public Property X() As Integer
      Get
        Return p_X
      End Get
      Set(ByVal value As Integer)
        p_X = value
      End Set
    End Property

    Private p_Y As Integer
    <Description("Gets/Sets the Y position of the object.")> _
    Public Property Y() As Integer
      Get
        Return p_Y
      End Get
      Set(ByVal value As Integer)
        p_Y = value
      End Set
    End Property
  End Structure

  Private Structure Size

    Private hei As Integer
    <Description("Gets/Sets the vertical size mesured in pixels.")> _
    Public Property Height() As Integer
      Get
        Return hei
      End Get
      Set(ByVal value As Integer)
        hei = value
      End Set
    End Property

    Private wid As Integer
    <Description("Gets/Sets the horizontal size mesured in pixels.")> _
    Public Property Width() As Integer
      Get
        Return wid
      End Get
      Set(ByVal value As Integer)
        wid = value
      End Set
    End Property

  End Structure

  'Globals
  Private asteroids As List(Of Obj) 'The list that will store our asteroids
  Private keyThread As Threading.Thread 'The thread that will check which key was hit
  Private gameThread As Threading.Thread 'The game loop thread
  Private playing As Boolean 'The boolean that will keep track of if we're playing the game
  Private r As Random 'The random variable that will ultimately deterime the X coordinate of the asteroids
  Private screenSize As Size 'The playing area
  Private ship As Obj 'The ship that the user will move around
  Private KeyInputExited As Boolean = True

  Sub Main()
    'Setup the title of our window
    Console.Title = "Asteroid Avoid"
    'Set the random to a new random object
    r = New Random

    'Loop until the user closes out
    Do
      'Tell the user to start a new game
      Console.Clear()
      Console.WriteLine("Press any key to start a new game.")
      If KeyInputExited Then
        Console.ReadKey()
      Else
        Do Until KeyInputExited
          Threading.Thread.Sleep(100)
        Loop
      End If

      'Start the new game and loop until the user loses
      Call NewGame()
      Do
        Threading.Thread.Sleep(100)
      Loop Until Not playing
      KeyInputExited = False
    Loop
  End Sub

  Private Sub NewGame()
    'Create new instances of the objects
    asteroids = New List(Of Obj)
    keyThread = New Threading.Thread(AddressOf KeyInput)
    gameThread = New Threading.Thread(AddressOf GameLoop)
    playing = True
    screenSize = New Size
    ship = New Obj

    'Set the playing area
    With screenSize
      .Height = 10
      .Width = 20
    End With

    'Set the ship's properties
    With ship
      .Character = "^"c
      .X = screenSize.Width \ 2
      .Y = screenSize.Height
    End With

    'Draw the user on the screen
    Call Draw()

    'Start listening for the keys that were hit and also the game loop
    keyThread.Start()
    gameThread.Start()
  End Sub

  Private Sub MoveAsteroids()
    'Move each asteroid's Y position down 1
    For i As Integer = asteroids.Count - 1 To 0 Step -1
      Dim asteroid As Obj = asteroids.Item(i)
      asteroid.Y += 1

      asteroids.Item(i) = asteroid
    Next
  End Sub

  Private Sub NewAsteroid()
    'Create a new asteroid at a random X position
    Dim newbie As Obj = New Obj

    With newbie
      .Character = "*"c
      .X = r.Next(0, screenSize.Width + 1)
      .Y = 0
    End With

    asteroids.Add(newbie)
  End Sub

  Private Sub CheckCollision()
    'Check if the asteroid has hit the bottom of the screen or hit the user
    'If it hits the bottom of the screen remove it
    'If it hist the user then stop the game
    For i As Integer = asteroids.Count - 1 To 0 Step -1
      Dim asteroid As Obj = asteroids.Item(i)

      If asteroid.Y = screenSize.Height + 1 Then
        asteroids.RemoveAt(i)
      ElseIf asteroid.X = ship.X AndAlso asteroid.Y = ship.Y Then
        playing = False
      End If
    Next
  End Sub

  Private Sub Draw()
    'Draw the user and all the asteroids
    Console.Clear()

    Console.CursorLeft = ship.X
    Console.CursorTop = ship.Y
    Console.Write(ship.Character)

    For Each asteroid As Obj In asteroids
      Console.CursorLeft = asteroid.X
      Console.CursorTop = asteroid.Y
      Console.Write(asteroid.Character)
    Next
  End Sub

  Private Sub GameLoop()
    Dim s As New Stopwatch
    Dim trigger As Long

    s.Start()
    Do
      trigger += 100
      Call MoveAsteroids()
      '      Dim ndate As Date = DateTime.Now
      '      Dim nmil As Integer = ndate.Millisecond
      '      Dim nsec As Integer = ndate.Second
      '     If CBool(nmil Mod 2) Then
      If r.Next(3) < 2 Then
        Call NewAsteroid()
      End If

      Call Draw()
      Call CheckCollision()

      '      While s.Elapsed <= TimeSpan.FromMilliseconds(51)
      '      End While
      Do While s.ElapsedMilliseconds < trigger
      Loop
      's.Stop()

      '  Debug.WriteLine(s.ElapsedMilliseconds)
    Loop While playing = True

    'If playing = True Then
    '  gameThread = New Threading.Thread(AddressOf GameLoop)
    '  gameThread.Start()
    'Else
    Console.Clear()
    Console.WriteLine("Press any key to start a new game.")
    'Console.ReadKey()
    '   End If
  End Sub

  Private Sub KeyInput()
    Do
      Dim info As ConsoleKeyInfo = Console.ReadKey
      If info.Key = ConsoleKey.LeftArrow AndAlso ship.X - 1 >= 0 Then
        ship.X -= 1
      ElseIf info.Key = ConsoleKey.RightArrow AndAlso ship.X + 1 <= screenSize.Width Then
        ship.X += 1
      End If
    Loop Until Not playing
    KeyInputExited = True
  End Sub

End Module
```

----------

