locked
I need an efficient and effective method of moving the canon in Space Invaders.

    Dotaz

  • I have just finished a program I call ET Invasion which is obviously based ont the iconic Space Invaders game of the late 70's (See image below). I have tried to emulate all the actions of the original game and am satisfied with the outcome of all aspects except the movement of the cannon left and right across the bottomof the screen.

    I have tried all combinations of Key_Down, Key_Up with and without timer events. I have not tried Key_Press because I don't know how to capture the left and right arrow keys using Key_Press. The following function comes close to what I want:

    Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
            If Lives = 0 Then Exit Function
            Dim Key As String = keyData.ToString
            Select Case Key
                Case "Left"
                    picbxGun.Left -= 5
                Case "Right"
                    picbxGun.Left += 5
                Case "Space"
                    If Bullet.Top > 490 Then
                        If SoundFlag = True Then
                            Dim snd As New SoundPlayer(My.Resources.smash)
                            snd.Play()
                        End If
                        tmrBullet.Start()
                    End If
            End Select
            If Bullet.Top > 490 Then
                'Bullet.Top = picbxGun.Top - 5
                Bullet.Left = picbxGun.Left + 24
            End If
            Application.DoEvents()
        End Function

    But it too falls short of the function I'm trying to achieve and that is smooth consistent movement when either of the left or right arrow keys are pressed without intefering with the other functions of the game which are primarily controled bu Timer events. (7 in all).

    So my question is; are there other means of capturing the left and right arrow keys and acting upon them without affecting program flow.

    24. února 2012 0:20

Všechny reakce

  • What about just detecting the keydown and keyup and using them to start and stop another timer which controls the movement.

    Are all the pictures actually graphics or (heaven forbid) pictureboxes?

    24. února 2012 0:49
  • Hi Dave,

    Firstly I've tried Using KeyDown to start a timer and keyup to stop, but when I hold the key down for continual movement then everything else stops.

    And yes, heaven forbid, the aliens are picture boxes but the 'castles' are 4 arrays of a 15 X 15 grid of labels.

    I only wish I could create the same thing using graphics but the only claim to fame I have regarding graphics is drawing a graph of a Sine and Cosine wave.

    24. února 2012 1:23
  • This link is how to make hotkeys:

    http://social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/c1a24688-d844-4adc-9d85-416a7158c6ba/

    And this is what I would do:

    Option Strict On
    
    Imports System.Runtime.InteropServices
    
    Public Class Form1
    
    #Region "HotKeys"
    
        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            If m.Msg = Hotkeys.WM_HOTKEY Then
                Dim id As IntPtr = m.WParam
                Select Case (id.ToString)
                    Case "100"
                        PictureBox1.Left += 50
                    Case "200"
                        PictureBox1.Left -= 50
                End Select
            End If
            MyBase.WndProc(m)
        End Sub
    
    #End Region
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            Hotkeys.RegisterHotKey(Me.Handle, 100, 0, Keys.Right)
            Hotkeys.RegisterHotKey(Me.Handle, 200, 0, Keys.Left)
        End Sub
    
      Private Sub Form1_FormClosing(sender As System.Object, e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
            Hotkeys.UnregisterHotKey(Me.Handle, 100)
            Hotkeys.UnregisterHotKey(Me.Handle, 200)
        End Sub
    
    End Class
    
    Public Class Hotkeys
    
        <DllImport("User32.dll")> _
        Public Shared Function RegisterHotKey(ByVal hwnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer
        End Function
    
        <DllImport("User32.dll")> _
        Public Shared Function UnregisterHotKey(ByVal hwnd As IntPtr, ByVal id As Integer) As Integer
        End Function
    
        Public Const MOD_ALT As Integer = &H1 'Alt key
        Public Const MOD_CTRL As Integer = &H2 ' Control Key
        Public Const WM_HOTKEY As Integer = &H312
        Public Const MOD_SHIFT As Integer = &H10
    
    End Class

    EDIT: Works fairly well when you hold the key down.

    Hope this helps,

    - Jordan


    Jordan St. Godard| Microsoft® Community Contributor 2011



    24. února 2012 2:44
  • Hi Gordon,

    Thanks for that. I don't pretend to understand exactly what it all means but I tried your solution verbatum.

    As a stand alone example the code worked well without errors and did what I expected it to do. I also included another HotKey for the space bar, but when I incorporated it into my program I got the following error:

    "Win32Exception was unhandled"

    "Error creating window handle."

    I copied the code exactly as you presented it, including the Public Const for the Alt key and others which I don't think I really need.

    I also made sure the code was placed in the right locations within my program, although from the link you gave me to "How do I register a hotkey in VB.Net" there doesn't seem to be a need for a separate class. Is this correct?

    Would you have any idea as to what created the error and how I may correct it?

    regards Ron


    • Upravený Ron.Sul 24. února 2012 4:48
    24. února 2012 4:43
  • Ron

    Visual Basic and Windows.Forms applications really aren't well suited to this sort of application - but ignoring that I've thrown together the following to show you how easy it is to make the app with graphics.

    Alien.png is just a simple 30x30 image I copied from your post above so all of the aliens are the same but obviously making each row different wouldn't be that difficult.

    Hold down the left or right arrow keys to move the cannon and press space to fire the bullets (as many as you like).

    You can do simple hit testing using Rectangle.Intersects to detect collisions.

    I was surprised how smoothly it runs.

    Try it in a new project:

    Public Class Form1
       Private AlienImage As Image = Image.FromFile("d:\temp\alien.png")
       Private Alien As New Rectangle(20, 20, 30, 30)
       Private Cannon As New Rectangle(20, 400, 60, 20)
       Private WithEvents CannonTimer As New Timer
       Private WithEvents AlienTimer As New Timer
       Private WithEvents BulletTimer As New Timer
       Private Fired As Boolean
       Private Bullets As New List(Of Rectangle)
       Private Aliens(9, 5) As Alien
       Private AlienMove As Integer = 20
       Private CannonMovement As Integer = 2
       Private CannonIncrement As Integer
       Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
          Size = New Size(800, 500)
          BackColor = Color.Black
          For Column As Integer = 0 To 9
             For row As Integer = 0 To 5
                Aliens(Column, row) = New Alien(New Rectangle(20 + 50 * Column, 50 + 40 * row, 30, 30), AlienImage)
             Next
          Next
          DoubleBuffered = True
          CannonTimer.Interval = 10
          AlienTimer.Interval = 1000
          BulletTimer.Interval = 20
          AlienTimer.Start()
          BulletTimer.Start()
          CannonTimer.Start()
       End Sub
       Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
          If e.KeyCode = Keys.Right Then
             CannonIncrement = CannonMovement
          ElseIf e.KeyCode = Keys.Left Then
             CannonIncrement = -CannonMovement
          ElseIf e.KeyCode = Keys.Space Then
             Bullets.Add(New Rectangle(Cannon.Left + Cannon.Width \ 2, Cannon.Top, 6, 10))
          End If
       End Sub
       Private Sub Form1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
          Select Case e.KeyCode
             Case Keys.Left, Keys.Right
                CannonIncrement = 0
          End Select
       End Sub
       Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
          For Each Alien As Alien In Aliens
             e.Graphics.DrawImage(Alien.Image, Alien.Bounds)
          Next
          e.Graphics.FillRectangle(Brushes.Blue, Cannon)
          For Each Bullet As Rectangle In Bullets
             e.Graphics.FillRectangle(Brushes.Red, Bullet)
          Next
       End Sub
       Private Sub CannonTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles CannonTimer.Tick
          Cannon.X += CannonIncrement
          Invalidate(Rectangle.Inflate(Cannon, 2, 2))
       End Sub
       Private Sub AlienTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles AlienTimer.Tick
          For Each Alien As Alien In Aliens
             Alien.MoveRight(20)
             Invalidate()
          Next
       End Sub
       Private Sub BulletTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles BulletTimer.Tick
          Dim Bullet As Rectangle
          For count As Integer = Bullets.Count - 1 To 0 Step -1
             Bullet = Bullets(count)
             Bullets(count) = New Rectangle(Bullet.X, Bullet.Y - 5, 5, 10)
             If Bullet.Y - 5 < 0 Then
                Bullets.RemoveAt(count)
             End If
             Invalidate(Rectangle.Inflate(Bullet, 2, 10))
          Next
       End Sub
    End Class
    
    
    Public Class Alien
       Private _Bounds As Rectangle
       Public ReadOnly Property Bounds As Rectangle
          Get
             Return _Bounds
          End Get
       End Property
       Public Property Image As Image
       Public Sub New(ByVal Bounds As Rectangle, ByVal Image As Image)
          _Bounds = Bounds
          _Image = Image
       End Sub
       Public Sub MoveRight(ByVal Distance As Integer)
          _Bounds = New Rectangle(_Bounds.X + Distance, _Bounds.Y, Bounds.Width, Bounds.Height)
       End Sub
    End Class


    • Upravený Dave299 24. února 2012 12:46
    24. února 2012 12:42
  • Show your code if you can where it goes wrong. And no, I just use a separate class because it makes it a lot easier when programming applications with multiple forms. Most of my applications use the hotkeys class and it works extremely well.

    Explanation in short: basically you just call two functions from the user32.dll to register and unregister the hotkey. You call the register function in Form.Load and the Unregister function in Form.Closing. When a user presses the hotkey, the WndProc case is checked. So the same ID handle for the hotkey (i.e. 100) will be the one checked in WndProc. Within that case, you can place your own code. It's pretty good.

    Normally these hotkeys are for two presses simultaneously such as Ctrl+C. That specific hotkey would look like this when called:

     Hotkeys.RegisterHotKey(Me.Handle, 100, Hotkeys.MOD_CTRL, Keys.C)

    For spacebar and single key hotkeys, you need to do a little hack that I found:

    Hotkeys.RegisterHotKey(Me.Handle, 100, Keys.Space, 0)

    We have to use 0 because the cuts the second part of the hotkey. Also keep in mind, if you plan on using a pause feature within your game, you may need to write a condition within WndProc such as:

    If paused = True
    ' do nothing
    Else ' paused is false
    PictureBox.Left += 50
    End If

    Of course declaring paused as a boolean naming it however you would like.

    So back to the error... What OS are you using? Are you running the program as a standard user or administrator? (Although standard works fine here...) It should work for XP + systems. 

    Try using my full code in a new project if you have not done so yet as sometimes copy and pasting into a full project will give those annoying errors. If that works, then we know it may be something with your project which I can look into over Skype or Teamviewer if you wish.

    - Jordan


    Jordan St. Godard | Microsoft® Community Contributor 2011

    24. února 2012 12:48
  • Hi Dave,

    I was so anxious to run your code with the hope of eventually learning something about graphics but it wouldn't run as it came up with errors in the following lines:

    Public Property Image() As Image
       Public Sub New(ByVal Bounds As Rectangle, ByVal Image As Image)
            _Bounds = Bounds
            _Image = Image
    
        End Sub

    with the following errors:

    Error 1 Property without a 'ReadOnly' or 'WriteOnly' specifier must provide both a 'Get' and a 'Set'. C:\Users\Ron\AppData\Local\Temporary Projects\Daves Space Invaders\Form1.vb 86 21 Daves Space Invaders
    Error 2 Statement cannot appear within a property body. End of property assumed. C:\Users\Ron\AppData\Local\Temporary Projects\Daves Space Invaders\Form1.vb 87 4 Daves Space Invaders
    Error 3 Name '_Image' is not declared. C:\Users\Ron\AppData\Local\Temporary Projects\Daves Space Invaders\Form1.vb 89 9 Daves Space Invaders

    If you could provide some fixes so I may get the full benefits of your code.

    Regards Ron

    24. února 2012 22:52
  • Hi Jordan,

    Thanksfor taking the time and effort to help me out.

    To answer your questions:

    I cannot show the code where it goes wrong because the error message appears wether I'm in design mode or code mode. It gives no indication as to which are the offending lines. I also included the referenced version in another game program which uses the four arrow keys but the same error occurred. No there is no pause feature in either program.

    My vital stats are: I'm using VB 2008 Express on a computer with an Intel(R) Quad CPU @2.4GHz with 4GB RAM and a 32 bit OS running Windows Vista Home Premium with SP2, and I do have adminstrator access.

    I've used your complete code in a new project in incoporated three keys, left, right and space and other than the movement being a bit slow all works well.

    I can definitely say that it's something in my prgrams. Both programs I've tried it in use timer events???? (Just guessing)

    I would be happy for you to look into my prgram over Skype or Teamviewer, (youls have to talk me through it), but we may have a problem being in different time zones. I'm GMT +10 on the East coast of Australia. The current time is 0903 25Feb12.

    Regards Ron


    • Upravený Ron.Sul 25. února 2012 3:28
    24. února 2012 23:02
  • The error message is self explanatory - you need to provide a Get and a Set.  A bit like this:

        Private _Image As Image
        Public Property Image() As Image
            Get
                Return _Image
            End Get
            Set(ByVal value As Image)
                _Image = Image
            End Set
        End Property
    

    All three errors arise from the same cause so changing the above fixes them all.

    Having to do that for all the properties in a class is probably one reason why people used to use fields instead of properties but in VB2010 you can just declare the property (as I did) and it puts the rest in for you behind the scenes.

    I've just tried the code in VB2008 and it works fine with the above change.

    24. února 2012 23:03
  • Hi Dave,

    I'm sorry that I don't have your expertise regarding OOP but I'm trying. The Shift Alt F10 provided a fix but when I applied it it was still wrong and I couldn't see  what I should do to fix it. I just don't know enough!

    I replaced the offending lines with your fix but then got another error in this line:

     Aliens(Column, row) = New Alien(New Rectangle(20 + 50 * Column, 50 + 40 * row, 30, 30), AlienImage)

    The error I got was:
    Error 1 Too many arguments to 'Public Sub New()'. C:\Users\Ron\AppData\Local\Temporary Projects\Daves Space Invaders\Form1.vb 20 49 Daves Space Invaders

    Please have patience with me, I'm trying.

    regards Ron

    24. února 2012 23:46
  • I don't understand that one but then I'm not sure what fix you have applied.

    The Alien class should look like this:

    Public Class Alien
        Private _Bounds As Rectangle
        Public ReadOnly Property Bounds() As Rectangle
            Get
                Return _Bounds
            End Get
        End Property
        Private _Image As Image
        Public Property Image() As Image
            Get
                Return _Image
            End Get
            Set(ByVal value As Image)
                _Image = Image
            End Set
        End Property
        Public Sub New(ByVal Bounds As Rectangle, ByVal Image As Image)
            _Bounds = Bounds
            _Image = Image
        End Sub
        Public Sub MoveRight(ByVal Distance As Integer)
            _Bounds = New Rectangle(_Bounds.X + Distance, _Bounds.Y, Bounds.Width, Bounds.Height)
        End Sub
    End Class
    

    24. února 2012 23:57
  • Thanks Dave,

    The program runs now. All I have to do is try to figure out how the code does what it does so that I may have a better understanding of using graphics.

    The first thing I want to do is to stop the aliens going off the screen!

    Although I realize that I cannot use any of your code in my program it is certainly valuable in my quest to learn more about graphics and OOP.

    Thanks again Dave.

    regards Ron

    25. února 2012 0:10
  • Hi Ron.Sul,

    Thanks for you post.

    I’m glad to hear that you found a good idea to resolve your question. Would you like to finish this thread by marking the helpful replies as answer?

    Sorry for any inconvenience and have a nice day.


    Mark Liu-lxf [MSFT]
    MSDN Community Support | Feedback to us

    27. února 2012 8:36
    Moderátor
  • Hi Mark,

    None of the advice has been helpful. Everything that has been advised I have tried.

    I think I've found the cause of my problem but not the solution.

    I replied to Jordan St Godard's suggestion in the affirmative and I am still waiting for a reply from Jordan.

    Regards Ron

    28. února 2012 5:26
  • Add me up on Skype: search "Jordan St. Godard". I was out of office for a week so sorry for the late reply.

    - Jordan


    Jordan St. Godard | Microsoft® Community Contributor 2011

    4. března 2012 14:23
  • I have just finished a long conversation with Jordan. We were unable to determine why the 'HotKey' function was returning an error.

    But since starting this thread I have re-written my Space Invaders program and called it Alien Invasion using Graphic methods and OOP (to the best of my ability and limited knowledge.)

    The second version works well enough with the Key_Down event and I discovered what causes the poor response of the Key_Down event. I don't know why or how it affects it but the cause, I believe, is too many controls in the program. Each of the 'Castles' in the original version were made up of an array of 225 Labels giving a total of 900 labels plus of course the 55 Alien picture boxes. When I wrote the second version using Graphics methods I thought I would transfer the code for creating the castles from the first version to the second version. As soon as I did I experienced the same problem of a poor response to the Key_Down event. I then changed the castle from Labels to Graphic Rectangles and now the Key_Down event to move the Cannon is acceptable, It's not perfect but I can live with that.

    So to Jordan, thank you once again for taking the time and trouble to contact me personally to try to fix the problem and thanks to Dave299 for encouraging me to explore Graphics methods and OOP further. (I still don't fully understand the function of 'Get' and 'Set' but given time........) As I said to Jordan, in my case it's not the destination that matters, it's the journey.

    The problem of 'HotKeys' still exist so if anyone wishes to seek a solution I can upload the offending program to MediaFire.Com where it can be downloaded and examined.

    regards Ron

    Regards Ron

    5. března 2012 1:50