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

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

Related

How to make a picture box appear randomly on the screen

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

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

Visual Basic: Image moves up and and then back down after a button click

I am attempting to make a character appear to jump straight up in the air and then come back down and return to the same level he started at. (y=100) The code below seems to make the program fight itself and move him up and down at the same time.
I have tried countless methods and all of them resulted in the guy either going up and not coming back down or flying off the page.
Private Sub btnJump_Click(sender As Object, e As EventArgs) Handles btnJump.Click
tmrJump.Start()
End Sub
Private Sub tmrJump_Tick(sender As Object, e As EventArgs) Handles tmrJump.Tick
For intCounterUp As Integer = 100 To 15
picSpaceRunner.Location = New Point(intCounterX, intCounterY)
intCounterY = intCounterUp
Next intCounterUp
For intCounterDown As Integer = 15 To 100
picSpaceRunner.Location = New Point(intCounterX, intCounterY)
intCounterY = intCounterDown
Next intCounterDown
End Sub
End Class
The code is running with no delay, so you're at the mercy of the machine.
I'm not a professional game coder, so I couldn't explain the intricacies of modern game engines. However, one of the basic ideas I learned a long time ago is to control your game/animation loop. Consider the frames per second.
In your code, it could be as simple as adding a delay within each loop iteration. If you want the character to complete his jump in 2 seconds (1 second up, 1 second down), then divide 1000 (1 sec = 1000 ms) by the number of iterations in each loop and delay by that amount. For example, you have 85 iterations, so each iteration would take approximately 12 ms.
If you don't mind blocking a thread, you can do this very easily with Threading.Thread.Sleep(12). If blocking is an issue, you'll likely want to use an external timer.
I found this link during a Google search. He explains how to set up a managed game loop in VB.Net.
http://www.vbforums.com/showthread.php?737805-Vb-Net-Managed-Game-Loop
UPDATE: Per OP's comment...
To do this using timers, you'll want to manipulate the character object directly within the Timer event handler (Tick). You wouldn't use loops at all.
Set the Timer's Interval to the value discussed earlier - the number of ms corresponding to how long it takes to move 1 pixel. Then, in the Timer's Tick handler, set the character object's Location equal to a new Point with the new value. Also in the Tick handler, check your upper bound (15), then reverse the process until it hits the lower bound (100).
For example,
Private Sub tmrJump_Tick(sender As Object, e As EventArgs) Handles tmrJump.Tick
If (intCounterY > 15 And blnGoingUp == True) Then
picSpaceRunner.Location = new Point(intCounterX, intCounterY - 1);
End If
... Remaining Code Goes Here ...
End Sub
Do not put the loop in the timer_tick. Increase or decrease the height by set interval instead and then check if the image had reached the maximum or minimum height.

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

This BackgroundWorker is currently busy and cannot run multiple tasks concurrently

I am confused. Yes i understand I can't use the same backgroundworker to do two tasks at the same time. What I do not understand is this. Here is my code (all this thing does is set the marqueeanimationspeed of a progress bar...
'THE FOLLOWING SUB TOGGLES THE PROGRESS BAR
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'CHECK THE STATE OF THE PROGRESS BAR AND TOGGLE IT
If ToolStripProgressBar1.MarqueeAnimationSpeed = 0 Then
ToolStripProgressBar1.MarqueeAnimationSpeed = 22
End If
ToolStripProgressBar1.MarqueeAnimationSpeed = 0
End Sub
OK, so how long can this possibly take? Doesn't the worker do the task and exit? So I put in a pause (system.threading.thread.sleep(2000)... same problem, made it 20 seconds... same problem.
So I am assuming this is a simple thing I'm missing, but I've spent more than an hour searching and I don't get it.
All I am trying to accomplish here is to start the marquee progress bar while the UI is running something else, and then stop it. I assume I can create another backgroundworker and just use it, but I want to understand why the first one is not done with the task.
Thanks, and again, yes I spent an hour searching and I find all kinds of "solutions" but no explanation as to why this thing is not finished.
OK SO HERE IS THE SUB CALLING THE BGW
'THE FOLLOWING SUB FIRES THE SETTING CONNECTION STRINGS SUB
Private Sub SetCSButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SetCSButton.Click
'START THE PROGRESS BAR & CHANGE THE LABEL
BackgroundWorker1.RunWorkerAsync()
Threading.Thread.Sleep(1000)
ToolStripStatusLabel1.Text = "Preparing the connection strings..."
Me.Refresh()
thread3 = New System.Threading.Thread(AddressOf SetConnectionStrings)
thread3.Start()
'STOP THE PROGRESS BAR & CHANGE THE LABEL
BackgroundWorker2.RunWorkerAsync()
Threading.Thread.Sleep(1000)
ToolStripStatusLabel1.Text = "Standing by..."
Me.Refresh()
End Sub**strong text**
I had a 20second delay but still the first BGW does not finish. I know this is something simple but I dont understand, that's all I am after here.
I DID change the code and do not use the same methodology as I was trying at the time I wrote this question... What I do not understand is why a simple operation is never, apparently, finishing... having said that, it DOES finish as I was able to show a msgbox using the runworkercompleted event. So, as I tried and failed to convey, thbis is not about the right or wrong way to code, I know it wa wrong and was just trying to be quick and dirty, regardless of that, I am not doing that now, but I do not understand why the BGW is "still working". There must be some simple thing I am ignorant about.
Thanks
The error is not in the posted code but where you start the Bgw.
But it is all irrelevant because you should not touch the GUI from DoWork:
Private Sub BackgroundWorker1_DoWork(...) Handles BackgroundWorker1.DoWork
'CHECK THE STATE OF THE PROGRESS BAR AND TOGGLE IT
If ToolStripProgressBar1.MarqueeAnimationSpeed = 0 Then ' Boom, cross-threading violation
ToolStripProgressBar1.MarqueeAnimationSpeed = 22
End If
I don't think you need a Bgw, thread or timer here. Just change the speed before/after the slow action.