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.Friday, February 24, 2012 12:20 AM
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.Friday, February 24, 2012 1:23 AM
This link is how to make hotkeys:
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 St. Godard| Microsoft® Community Contributor 2011
Friday, February 24, 2012 2:44 AM
- Edited by Jordan St. Godard Friday, February 24, 2012 2:49 AM
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?
Friday, February 24, 2012 4:43 AM
- Edited by Ron.Sul Friday, February 24, 2012 4:48 AM
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
Friday, February 24, 2012 12:42 PM
- Edited by Dave299 Friday, February 24, 2012 12:46 PM
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 St. Godard | Microsoft® Community Contributor 2011Friday, February 24, 2012 12:48 PM
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 RonFriday, February 24, 2012 10:52 PM
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.
Friday, February 24, 2012 11:02 PM
- Edited by Ron.Sul Saturday, February 25, 2012 3:28 AM
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.Friday, February 24, 2012 11:03 PM
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 RonFriday, February 24, 2012 11:46 PM
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 ClassFriday, February 24, 2012 11:57 PM
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 RonSaturday, February 25, 2012 12:10 AM
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
Monday, February 27, 2012 8:36 AMModerator
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 RonTuesday, February 28, 2012 5:26 AM
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 RonMonday, March 05, 2012 1:50 AM