How to make a picture box appear randomly on the screen - vb.net

I am coding a zombie game in vb.net and need to make the zombies(which I have put in picture boxes and an array, there are 13) appear randomly, maybe two each time 1 zombie is killed. How can I make this in code ? I am new to coding and cannot figure it out even after numerous searching.

I think I understand what you're trying to do.
You'll want to construct a picture box in code then define where you want it to "spawn" on your form.
You can start with something like this, if you're going to have more than one zombie at a time you'll want to either make a list of them or name them uniquely so you can reference them later on (moving them/despawning/etc)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim zombie As New PictureBox
With zombie
zombie.Width = 100 'or the size you need
zombie.Height = 100 ' same thing
zombie.Top = 20 'or where you need it could be a random
zombie.Left = 20 'same as top
zombie.ImageLocation = "C:\mydocuments\zombie.png" 'change this to the location of your zombie image. if you're storing it in a resource you can call it here
End With
Me.Controls.Add(zombie)
End Sub
This should get you started at least
edit: I missed the part where you said you have an array for your zombies, but you can do an array of your picture boxes as well

Related

VB.NET: How to program a simple collision detection system?

I am programming Atari Breakout in VB.NET, and I need to program my ball to detect collisions with the paddle, blocks, and the edges of the gameboard. I have written out a small piece of code, but I am unsure of what to do next. Essentially, when the ball touches either the blocks, the paddle, or the edges of the game board, it needs to bounce off of it and move in a different direction. For the moment I am only focusing on the vertical movement of the ball, so the ball will move down, but then must move upwards when it hits the paddle.
The way I am moving the picturebox I am using as the ball is by setting up a timer, and every time the timer ticks, the ball will move at a certain pace. To achieve this, I use the following piece of code:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Ball.Top += 10
End Sub
This piece of code can be adjusted to get the ball to move different directions. So for example, I can change the plus to a minus to get the ball to move upwards, and I can also use Ball.Left to control sideways movement. I can even combine the two together to get diagonal movement. I can also edit the speed at which the ball moves. Right now it is on 10, which is more than fast enough for the game I am making.
What I want to do is program the ball so that, when it is touching another object (in this case, a picturebox, which I am using as my paddle), the direction of travel is reversed, so it will move upwards instead of downwards. I tried to do this in the code written below, however this does not work as the ball simply stops when it gets close to the paddle, and won't move until I move the paddle, only for it to keep moving downwards.
Here is the code I have written:
Private Sub Ball_Move(sender As Object, e As EventArgs) Handles Ball.Move
If Ball.Bounds.IntersectsWith(Platform.Bounds) Then
Ball.Top -= 10
End If
End Sub
UPDATE:
I thought I'd quickly edit this just so I can show what I'm trying to do more clearly, as my code goes over the character limit for comments.
First of all, I'd like to thank #jmcilhinney for sharing the code. When I tried to use it in my program however, it didn't work properly. I tried to place it within the subroutine I am using for the movement of the ball (posted in my initial question, uses a timer to allow the ball to move). The main issue is that, when the program is run, the ball goes straight past my paddle, which is a green PictureBox at the location of 318, 397. Another issue is that, when I added the code to my program, it showed a lot of errors. Some of which I was able to amend using the quick actions menu, but some I was not able to amend. I've tried to move the code to different parts of the same subroutine to no avail. Here is the subroutine with the collision code in it that is giving me the errors:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'Direction of movement. +1 is down and -1 is up.
Private direction As Integer = &B1
Private Sub MoveControl(ctrl As Control)
Dim direction As Integer = Nothing
'Move the control is the current direction.
ctrl.Top += direction * 10
'If the control has reached the top or the bottom of the form...
If ctrl.Top <= 0 OrElse ctrl.Bottom >= ClientSize.Height Then
'...reverse the direction.
direction *= -1
End If
End Sub
Ball.Top += 10
End Sub
End Class
Some sections of the code look different to the code that was posted in #jmcilhinney's answer, but that is because I had to use the quick actions and refactorings menu to change some parts of the code so as to not give me any errors. I may have done something wrong here, because the code just doesn't seem to be working for me.
These are the errors I have with this code: Line 1 has error code BC30026, Line 4 has error code BC30247, Line 5 has error code BC30289, Line 15 has error code BC30188, Line 16 has error code BC30429.
UPDATE 2: I've managed to figure out something that has reduced the number of errors present in my program. What I've done is moved the piece of code calling the Timer subroutine to be after the 'Private direction As Integer' line. My code has gone from having five errors down to just two. Here is the code with the change I have made:
'This part of the code uses a timer to allow to the ball to move. It moves in a certain direction at a certain speed with every tick.'
Private increment As Integer = 10
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Private Sub MoveControl(ctrl As Control)
ctrl.Top += increment
If ctrl.Top <= 0 OrElse ctrl.Bottom >= ClientSize.Height Then
increment *= -1
End If
End Sub
End Class
My first error is present on line 3 (Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick). Here, I am getting error BC30026 'End Sub expected', and the other error I am getting is on line 4 (Private Sub MoveControl (ctrl as Control)). Here, I am getting error BC30289 'Statement cannot appear within a method body. End of method assumed.' However, when I tried to move that particular piece of code outside of the method, I got error BC30026 'End Sub expected.' I am so confused on what to do next. I am still relatively new to VB and I haven't tried something like this before, so this is probably why I am doing everything so wrong. Thanks to jmcilhinney for all the help so far, including providing me with code. I appreciate it a lot!
I should also mention that the code I used here is the second piece of code jmcilhinney posted, rather than the first one. That's why it is slightly different.
If you want to move the control by 10 pixels each time but you want the direction to change then the easiest option is to simply have a multiplier of +/- 1 that you flip the sign of each time the appropriate condition is True. Here's a simple example that will move the specified control up and down the form between the top and the bottom:
'Direction of movement. +1 is down and -1 is up.
Private direction As Integer = 1
Private Sub MoveControl(ctrl As Control)
'Move the control is the current direction.
ctrl.Top += direction * 10
'If the control has reached the top or the bottom of the form...
If ctrl.Top <= 0 OrElse ctrl.Bottom >= ClientSize.Height Then
'...reverse the direction.
direction *= -1
End If
End Sub
EDIT:
I guess you could just store the increment in the field and change the sign of that. I was thinking to keep the direction and the increment separate but there's probably no need for that:
Private increment As Integer = 10
Private Sub MoveControl(ctrl As Control)
ctrl.Top += increment
If ctrl.Top <= 0 OrElse ctrl.Bottom >= ClientSize.Height Then
increment *= -1
End If
End Sub

How to update a group of combo boxes using Loops

I have a form with combo boxes (cmbPort#) to select up to 8 serial ports. I want to first clear the item list for each, populate them with currently available system ports, and then add the option "Off" to each list. Finally, to set each combo box according to defaults saved in string spName(). I created a GroupBox (gBox1) and dragged each cmbPort onto it but I'm not sure how to reference the controls on it. I'm using VB 2015.
Can you help with VB.NET code to use loops ("For Each" or similar) to do this more efficiently?
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cmbPort1.Items.Clear()
...
cmbPort8.Items.Clear()
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort1.Items.Add(sp)
...
cmbPort8.Items.Add(sp)
Next
cmbPort1.Items.Add("Off")
...
cmbPort8.Items.Add("Off")
cmbPort1.Text = spName(1)
...
cmbPort8.Text = spName(8)
End Sub
Loops are an incredibly useful tool to master. I pitched here some code so you can get the idea, but I was working out of IDE so you might have to apply some fixes to this code to make it work as you want it to.
The main idea is that you shouldn't have to write a line more than once. If you multiply the same operation on several lines of code, you create potential problems for the future. Loops and subs are really helpful to prevent this kind of issues.
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'This is for later
Dim cmbIndex As Integer = 1
'You basically do the same operations for every Combobox in your list, son only one loop needed
For Each cmbPort As ComboBox In {cmbPort1, cmbPort2, cmbPort3 [...] cmbPort8} 'this is a way to declare an array
'Clear
cmbPort.Items.Clear()
'Add SerialPortNames
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort.Items.Add(sp)
Next
'Add "Off"
cmbPort.Items.Add("Off")
'This last one is a little bit trickier because you want to match numbers
'This is the place where you get errors when something doesn't go as planned
'If you need to keep it inside the loop here's a way to achieve that, but honestly I would't do that
'Instead I would suggest that you take this part out of the loop and do it manually
If spName(cmbIndex) IsNot Nothing Then cmbPort.Text = spName(cmbIndex)
cmbIndex += 1
Next
End Sub
You shouldn't consider efficiency into this equation, as this operation will not be called all the time, only on load. I mean: you should always do things in the best way you can, but optimization is sometimes the enemy of good, readable code.

How can I cause a button click to change the image in a picture box?

I'm using VS2010 for VisualBasic, and I'm working with several similar forms. What I need to have happen is for the buttonclick on each page to cycle through the My.Resource images in order: adj_01, adj_02, adj_03,... and each form will have a different three-letter prefix.
This is what I have so far:
It might not be clear, but I'm trying to have the images cycle trough one after the other with each button click. Apparently there is an issue with either my referencing, or that the images are .png format. Simultaneously, I'm trying to have 2 separate label update information with each image change. This is what I have so far with that:
EDIT I just noticed an error that might confuse everyone on the photos: The first lines starting the If statements are checking to see if the PictureBox is empty. Needless to say, I don't know how to do that.
Here you go...
Private Sub NextAdjButton_Click(sender As Object, e As EventArgs) Handles NextAdjButton.Click
If AdjectivesPictureBox.Tag Is Nothing Then
AdjectivesPictureBox.Tag = 0
End If
Dim number As Integer = CInt(AdjectivesPictureBox.Tag)
If number < 5 Then
number = number + 1
AdjectivesPictureBox.Image = My.Resources.ResourceManager.GetObject("adj_" & number.ToString("00"))
AdjectivesPictureBox.Tag = number
End If
End Sub

Using For Loop to Check for duplicates - Error The error is: Object reference not set to an instance of an object

I have a problem that I and my limited experience cannot figure out.
I have a multi-display (form) program that is for a game show system which uses a (5) display system. As I am wrapping this project up, I have been doing some house keeping and handling some potential user errors up front.
In this instance: There is a form called GameSetup which has drop down menus that are filled via a database selection of "Teams". When a Team is selected, (10) labels are populated with registered team members. The user drags and drops a team member (ie. player) to a group of (6) labels, which will make up the active team for the game.
The problem: I wanted to have a check when a name is added to the active team roster to make sure there are no duplicates. The cleanest way to do this, I thought, would be on the .TextChanged action of the active player label and use a For loop to check the other active team player names.
Private Sub lblT1_P1_TextChanged(sender As Object, e As EventArgs) Handles lblT1_P1.TextChanged
cmdReady.BackColor = Color.Yellow
For i = 1 To 6
If i = 1 Then
' Do nothing
ElseIf Me.Controls("lblT1_P" & i.ToString).Text = lblT1_P1.Text Then 'This is the line triggering the NullException
MsgBox("This Player is already selected. Please choose another Player", MsgBoxStyle.OkOnly)
Me.Controls("lblT1_P" & i.ToString).Text = "DRAG PLAYER HERE" 'This is the other line triggering the NullException
End If
Next i
End Sub
i = 1 Do Nothing is so that it wont compare against itself (in this instance)
When I run the program (de-bug), before the form GameSetup loads, I am getting a Null Exception, which makes sense as it is looking at a form or an object in a form that is not yet initialized. Clearly, the Me.Controls is the problem, but I do not know how to handle using the integer (i) in the For Loop otherwise.
The label names are lblT1_P1 - lblT1_P6 (3 instance of each group (T1/T2/T3). I'm only dealing with T1_P1 at the moment.
Any suggestion would be greatly appreciated.
Use string.Format() to generate your control names, because it is more readable.
Instead of Me.controls("lblName").Text
use DirectCast(Me.FindControl("lblName"), Label).Text

What would be a better way to make a calendar than using labels?

I've created my own winforms month-view calendar in VB.net.
To do it I've used a table layout panel, with 42 separate cells. In each of the cells is a label called lblDay1, lblDay2, etc.
When I load the page, the labels are all written to with the correct numbers for that month.
Dim daysInMonthCnt As Integer =31 'Assume 31 days for now
Dim firstDay As Integer = Weekday("1/" & Now.month & "/" & Now.year) 'Get weekday for 1st of month
For dayCount As Integer = firstDay To daysInMonthCnt
Dim lbl As Label
lbl = CType(pnlMonthBody.Controls("lblDay" & dayCount), Label)
lbl.Text = dayCount 'Write to label
Next dayCount
Unfortunately, that turns out to be incredibly slow to load. Can anyone please suggest a faster method.
Just writing values to a so small number of labels is a really fast process. The problems you are experiencing have to do most likely with the VB.NET "problems" while refreshing the contents of GUI controls; the best way to fix this is looking into multithreading, as suggested by FraserOfSmeg.
As far as I think that this is a pretty simplistic GUI with a low number of controls and a not too demanding algorithm (big amount of/long loops is the prime cause of GUI-refreshing problems), you might get an acceptable performance even without relying on multithreading. In your situation, I would do the following:
A container (the TableLayoutPanel you are using or something
simpler, like a Panel) including all the labels at the start. In
case of not getting too messy (what does not seem to be the case,
with just 42 labels) I would include them in the "design view"
(rather than at run time).
A function populating all the labels depending upon the given month.
A "transition effect" for
the container called every time the user selects a different month.
You can accomplish this quite easily with a Timer relocating the
container (e.g., when the button is clicked the container's position
is set outside the form and then comes back gradually (20 points per
10ms -> made-up numbers) until being back to its original position).
Synchronising the two points above: the values of the labels
will start changing when the transition starts; in this way the user
will not notice anything (just a nice-appealing transition
month to month).
This GUI should deliver the kind of performance you are after. If not, you should improve its performance by relying on additional means (e.g., the proposed multi-threading).
SAMPLE CODE TO ILLUSTRATE POINT 3
Add a panel (Panel1), a button (Button1) and a timer (Timer1) to a new form and the code below.
Public Class Form1
Dim curX, origX, timerInterval, XIncrease As Integer
Dim moving As Boolean
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
If (curX >= origX) Then
If (moving) Then
curX = Panel1.Location.X
moving = False
Timer1.Stop()
Else
curX = 0 'getting it out of the screen
moving = True
End If
Else
curX = curX + XIncrease
End If
Panel1.Location = New Point(curX, Panel1.Location.Y)
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Timer1.Start()
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
XIncrease = 100
timerInterval = 100
Panel1.BackColor = Color.Maroon
origX = Panel1.Location.X
curX = origX
With Timer1
.Enabled = False
.Interval = timerInterval
End With
End Sub
End Class
This is very simplistic, but shows the idea clearly: when you click the button the panel moves in X; by affecting the timerInterval and XIncrease values you can get a nice-looking transition (there are lots of options, bear in mind that if you set curX to minus the width of the panel, rather than to zero, it goes completely outside the form).
If you're purely interested in speeding up your code I'd suggest running the loading code on multiple threads simultaneously. This may be overkill depending on your application needs but it's a good way to code. As a side note so the program looks a little more slick for the end user I'd suggest always running time consuming processes such as this on a separate thread(s).
For info on multithreading have a look at this page:Mutlithreading tutorial