Results 1 to 7 of 7

Thread: [VB.Net] Asteroid Avoid

  1. #1

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    [VB.Net] Asteroid Avoid

    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:
    Code:
    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.
    Last edited by dday9; May 19th, 2014 at 10:42 AM. Reason: Added comments and changed up the code
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  2. #2
    PowerPoster Nightwalker83's Avatar
    Join Date
    Dec 2001
    Location
    Adelaide, Australia
    Posts
    13,344

    Re: [VB.Net] Asteroid Avoid

    Which version of VB.Net? I just tried creating the game in 2008 but received 31 errors.
    when you quote a post could you please do it via the "Reply With Quote" button or if it multiple post click the "''+" button then "Reply With Quote" button.
    If this thread is finished with please mark it "Resolved" by selecting "Mark thread resolved" from the "Thread tools" drop-down menu.
    https://get.cryptobrowser.site/30/4111672

  3. #3
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: [VB.Net] Asteroid Avoid

    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:
    Code:
      Public Property Character As Char
    ...becomes:
    Code:
      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)

  4. #4

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Re: [VB.Net] Asteroid Avoid

    @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.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  5. #5
    PowerPoster Nightwalker83's Avatar
    Join Date
    Dec 2001
    Location
    Adelaide, Australia
    Posts
    13,344

    Re: [VB.Net] Asteroid Avoid

    Quote Originally Posted by dday9 View Post
    @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?
    when you quote a post could you please do it via the "Reply With Quote" button or if it multiple post click the "''+" button then "Reply With Quote" button.
    If this thread is finished with please mark it "Resolved" by selecting "Mark thread resolved" from the "Thread tools" drop-down menu.
    https://get.cryptobrowser.site/30/4111672

  6. #6

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Re: [VB.Net] Asteroid Avoid

    Nah, what I'll do is adjust the code to work for 2008 on up
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  7. #7
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: [VB.Net] Asteroid Avoid

    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.
    Code:
    '...
          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.
    Code:
    '...
          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.
    Code:
    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
    Last edited by passel; May 19th, 2014 at 07:23 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width