Reputation: 3
For my Major Work project for my last year of school, I am creating a Space Invaders game, and I am making it on VB.NET because it is what I am most comfortable with. This is going to be a very long-winded question because there are a lot of factors that may or may not be contributing to this issue I'm having. I have made the shooting system like so:
Private Sub SpaceInvaders_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
If fired = False Then
If e.KeyChar = "W" Or e.KeyChar = "w" Then
shotFired = True
fired = True
End If
End If
End Sub
I use this code block to register if the player is attempting to shoot. I would have put this in a KeyDown command, but that allows the player to shoot continuously which is very broken and makes it much harder for me to make a balanced game. shotFired shows that the player has shot this round, and fired makes sure that you can't shoot again until you have taken your finger off of the W key.
Private Sub SpaceInvaders_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
Select Case e.KeyCode
Case Keys.W
fired = False
End Select
End Sub
All this is intended to do is make sure that the player can fire a shot again once they have released the W key.
Private Sub tmrMovement_Tick(sender As Object, e As EventArgs) Handles tmrMovement.Tick
If shotFired = True Then
My.Computer.Audio.Play(My.Resources.Laser_Shoot,
AudioPlayMode.Background)
shotFired = False
bullet(bulletCount).Left = pbPlayer.Left + 25
bullet(bulletCount).Top = pbPlayer.Top - 25
bullet(bulletCount).Visible = True
bulletCount += 1
If bulletCount = bullet.Length - 1 Then
bulletCount = 0
End If
End If
For i As Integer = 0 To bullet.Length - 1
bullet(i).Top -= 5
If bullet(i).Top < -50 Then
bullet(i).Visible = False
bullet(i).Top = Me.Height
End If
Next
For my shooting, which is the area in my program that is having the trouble currently, I am making it so that there is an audio queue that plays first, and whilst that continues to play in the background, the shotFired value will immediately reset to false, making it so it doesn't shoot multiple bullets in a short amount of ticks. The way that my bullets work is that they are in an array, currently set at 20. I shoot and my array will move one number further across the line, making sure that the last bullet fired doesn't keep moving back to the same position when I shoot, and I can multiple bullets on the screen at a time. I keep track of this with the bulletCount digit, and once it equals the max amount of bullets in my array, it resets, making it go back to bullet(0), resetting and making sure I don't have to make a new bullet every single time I want to shoot.
The way I make them move is that every tick that they are active, it goes up the form by 5. If it goes above -50, it disappears and goes back to its starting position. It will not affect collision, however, as collision first takes into account if both variables are visible or not.
Regardless, once I created a menu for my game, an issue arose. It's a simple menu, with only a label with the title of the game, and a button to start the first level. Once I start the first level, though, I cannot shoot. This is the code for when the start button is clicked:
Private Sub btnBegin_Click(sender As Object, e As EventArgs) Handles btnBegin.Click
Me.KeyPreview = True
lblTitle.Visible = False
btnBegin.Visible = False
pbPlayer.Visible = True
pbGuard1.Visible = True
pbGuard2.Visible = True
pbGuard3.Visible = True
btnBegin.Enabled = False
If level = 1 Then
enemy(0).Visible = True
enemy(0).Left = 234
enemy(1).Visible = True
enemy(1).Left = 193
tmrLevel1.Enabled = True
End If
End Sub
I need to make the button's enabled value false, as if I keep it as true, it will still technically be highlighted by my game, making it so if the player hits the spacebar, the game will respawn all of the enemies and go back to the start of level 1. This becomes a massive issue, however, because once I disable the button, my player character cannot shoot. I am extremely unsure of what the issue is here, and I can see no correlation between the two values. I have tried to find ways to un-highlight the button, so I don't have to disable it and can therefore still shoot, but I am not sure how I could do it at this moment.
How could I un-highlight the button once the level starts? Is there a certain value I need to disable? And if possible, does anyone know what the correlation here between the button and the ability to shoot is, and how I could fix that? Any help would be greatly appreciated.
Upvotes: 0
Views: 111
Reputation: 54457
When you click a Button
, it receives the input focus. If you want to remove that focus then you can either focus a different control by calling its Select
method or not focus any control at all by setting the form's ActiveControl
property to Nothing
.
If you're felling adventurous then you can actually create a custom Button
control that won't receive focus in the first place. Here is a class I wrote almost 14 years ago to do just that:
''' <summary>
''' A button control that cannot receive focus.
''' </summary>
Public Class UnselectableButton
Inherits System.Windows.Forms.Button
#Region " Constructors "
''' <summary>
''' Initialises a new instance of the <see cref="UnselectableButton"/> class.
''' </summary>
Public Sub New()
MyBase.New()
'The button cannot receive focus.
Me.SetStyle(ControlStyles.Selectable, False)
End Sub
#End Region 'Constructors
#Region " Variables "
''' <summary>
''' Corresponds to the MA_NOACTIVATE window process reply.
''' </summary>
Private Shared ReadOnly noActivate As New IntPtr(NativeConstants.MA_NOACTIVATE)
#End Region 'Variables
#Region " Methods "
''' <summary>
''' Processes Windows messages.
''' </summary>
''' <remarks>
''' Informs Windows not to activate the window when it is clicked with the mouse. Note that this behaviour applies to the client area of the window only.
''' </remarks>
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
MyBase.WndProc(m)
If m.Msg = NativeConstants.WM_MOUSEACTIVATE Then
'Tell Windows not to activate the window on a mouse click.
m.Result = noActivate
End If
End Sub
Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
MyBase.OnClick(e)
End Sub
#End Region 'Methods
End Class
If you add that class to your project and build, the custom control will appear at the top of the Toolbox and you can use it instead of a regular Button
.
Here's a class that includes the constants used above:
''' <summary>
''' Contains Win32 constant declarations.
''' </summary>
Friend NotInheritable Class NativeConstants
''' <summary>
''' Specifies that a window cannot be activated.
''' </summary>
''' <remarks>
''' If a child window has this style, clicking it does not cause its top-level parent to activate. Although a window that has this style will still receive mouse events, neither it nor its child windows can get the focus.
''' </remarks>
Public Const WS_EX_NOACTIVATE As Integer = &H8000000
''' <summary>
''' This message is sent when the cursor is in an inactive window and the user presses a mouse button.
''' </summary>
Public Const WM_MOUSEACTIVATE As Integer = &H21
''' <summary>
''' Does not activate the window, and does not discard the mouse message.
''' </summary>
''' <remarks>
''' This is one of the possible return values when the <see cref="WM_MOUSEACTIVATE"/> notification is received.
''' </remarks>
Public Const MA_NOACTIVATE As Integer = 3
''' <summary>
''' Displays a window in its most recent size and position. This value is similar to <b>SW_SHOWNORMAL</b>, except the window is not actived.
''' </summary>
''' <remarks>
''' This is one of the possible values for the <b>nCmdShow</b> argument of the <see cref="NativeMethods.ShowWindow">ShowWindow</see> method.
''' </remarks>
Public Const SW_SHOWNOACTIVATE As Integer = 4
End Class
You can use that class as is, remove the constants you don't need or just copy the constants you do need to the UnselectableButton
class. My original code included a number of other classes that made use of those constants.
Upvotes: 1