Trouble with Collision Between Objects Defined in Different Arrays
Hello.
I've been having a major concern that I haven't been able to figure, likely because I'm just not used to debugging/looking at code critically. Perhaps I'm just being too complicated, but anyway.
I have a single-screen top-down shooter/dungeon-kind-of game in which there's two main objects - blocks and skeletons. There are eight blocks which the player and the skeletons can't pass through, and two skeletons that the player can't touch without restarting the level.
I've used two different arrays to define the objects that are to be skeletons or blocks, so that I don't have to name a whole bunch of objects and refer to them individually. I've been able to get the skeletons to move in the opposite direction (they currently only go right-left) when they hit a block, however the game crashes if the player restarts with an IndexOutOfRange error on the "If Skeleton(I).Bounds.IntersectsWith(Block(J).Bounds) Then" line.
Code:
Dim Skeleton(1) As PictureBox
Dim Block(7) As PictureBox
Dim obj As Object, I As Integer, J As Integer
Dim WhichWay As Direction = Direction.Right
Enum Direction
Left
Right
End Enum
Private Sub Form4_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' define which objects are "blocks" and which are "skeletons"
For Each obj In Me.Controls
If TypeOf obj Is PictureBox Then
If obj.tag = "skeleton" Then
Skeleton(I) = obj
I += 1
End If
If obj.tag = "block" Then
Block(J) = obj
J += 1
End If
End If
Next
End Sub
Private Sub tmrMovement_Tick(sender As Object, e As EventArgs) Handles tmrMovement.Tick
' determine skeletons' movement
For I = 0 To 1
Select Case WhichWay
Case Direction.Left
Skeleton(I).Left -= 1
Case Direction.Right
Skeleton(I).Left += 1
End Select
If Skeleton(I).Bounds.IntersectsWith(picPlayer.Bounds) AndAlso Skeleton(I).Visible = True AndAlso picPlayer.Visible = True Then
picPlayer.Visible = False
MsgBox("You died!" & Environment.NewLine & "Try again!")
Reset()
End If
' determines how the skeleton changes direction when hitting a "block" object
For J = 0 To 7
If Skeleton(I).Bounds.IntersectsWith(Block(J).Bounds) Then
Select Case WhichWay
Case Direction.Left
WhichWay = Direction.Right
Skeleton(I).Left += 1
Case Direction.Right
WhichWay = Direction.Left
Skeleton(I).Left -= 1
End Select
End If
Next
Next
End Sub
Private Sub Form4_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
Select Case e.KeyCode
Case Keys.Left
tmrLeft.Start()
Case Keys.Right
tmrRight.Start()
Case Keys.Down
tmrDown.Start()
Case Keys.Up
tmrUp.Start()
End Select
End Sub
' the rest is just the player's movement and a function to reset the level when the player dies - so, probably not integral to the problem
Private Sub Form2_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
Select Case e.KeyCode
Case Keys.Left
tmrLeft.Stop()
Case Keys.Right
tmrRight.Stop()
Case Keys.Down
tmrDown.Stop()
Case Keys.Up
tmrUp.Stop()
End Select
End Sub
Private Sub tmrLeft_Tick(sender As Object, e As EventArgs) Handles tmrLeft.Tick
picPlayer.Left -= 2
End Sub
Private Sub tmrRight_Tick(sender As Object, e As EventArgs) Handles tmrRight.Tick
picPlayer.Left += 2
End Sub
Private Sub tmrDown_Tick(sender As Object, e As EventArgs) Handles tmrDown.Tick
picPlayer.Top += 2
End Sub
Private Sub tmrUp_Tick(sender As Object, e As EventArgs) Handles tmrUp.Tick
picPlayer.Top -= 2
End Sub
Private Function Reset()
Dim frm As New Form1
frm.Show()
Me.Hide()
End Function
This code should simulate a minimalised version of the game I'm making (the actual game has shooting and a goal and other stuff)
I can't figure anything out. I'm concerned about the fact that the game runs fine when it's first loaded, however if it's reloaded with the Reset() function, it crashes. I don't understand why the IndexOutOfRange error wouldn't occur when it's first loaded. Apparently, when the game crashes, it says that I = 2, which is good, but J = 0, which I'm pretty sure is bad. However, I don't know why it wouldn't equal 0.
Side note - the two skeletons change direction at the same time, even if only one hits a block. Their direction is determined by the "WhichWay" variable. If I want the skeletons to not change direction in unison, do I need a different "WhichWay" integer for each skeleton to determine their direction at any given time, or is there a better way to determine the change in movement of different objects in the same array?
Thanks in advance for any help.
Re: Trouble with Collision Between Objects Defined in Different Arrays
I'm pretty sure I am not going to be of much assistance with your current code, however, I may be able to provide you with an alternative idea. If this is for a school project though, just move past this thought because I don't want to confuse or overly complicate your assignment.
Currently in one application I have a dice roller app. I can have an unlimited amount of dice, but it's usually pretty buggy when I go past 10k instances on the screen. The dice is an actual class that is setup, and in that class each die handles it's own collision, with other dice that are stored in a list(of Dice), or List(of T) to google, as well as collision with the walls (or window bounds).
For each die in that list each rendering loop I am checking to see if the index is that die, if it is then continue on to check collision with the other dice in that list.
The point is to not have everything be in sync the skeletons would need to be an actual class, and each instance would handle it's own collision during each tick of your timer...however, an actual game loop would be much better for your purpose.
Re: Trouble with Collision Between Objects Defined in Different Arrays
You've defined your rules, the game will restart should the user or skeletons either:
- Touch the blocks
- Two specific skeletons touch the user
What I would do to check for these two conditions is to use LINQ to get the count of blocks or skeletons that've touched the user:
Code:
If Blocks.Count(Function(block) block.Bounds.IntersectsWith(picPlayer.Bounds)) > 0 Then
'Reset
ElseIf Skeleton.Count(Function(skelly) skelly.Bounds.IntersectsWith(picPlayer.Bounds)) > 0 Then
'Reset
Else
'Continue on
End If
Also, for what its worth this block can be condensed:
Code:
For Each obj In Me.Controls
If TypeOf obj Is PictureBox Then
If obj.tag = "skeleton" Then
Skeleton(I) = obj
I += 1
End If
If obj.tag = "block" Then
Block(J) = obj
J += 1
End If
End If
Next
Into these two lines:
Code:
Skeleton = (From pb As PictureBox In Me.Controls().OfType(Of PictureBox)() Where pb.Tag.ToString = "skeleton").ToArray()
Blocks = (From pb As PictureBox In Me.Controls().OfType(Of PictureBox)() Where pb.Tag.ToString = "block").ToArray()
Re: Trouble with Collision Between Objects Defined in Different Arrays
Be aware that LINQ is concise, but it isn't fast. You won't see a difference in speed for something like this, since either way will appear to happen instantly, but if you find LINQ painful to read then don't worry that you're missing out.
One thing I would suggest is that you try to make the reset work with the existing form rather than creating a new form and hiding the current. There's an issue with doing it as you are, since every object takes up a bit more memory and it never gets freed. If you hit reset enough times, the computer would crash...though it would likely take a few thousand presses before that happened, so you could ignore it. Still, truly resetting the current form is a better solution.
In any case, you want to study this error a bit more by putting a breakpoint on the For statement in the Load event. When execution stops at the breakpoint, use F11 to step through the code. What you are specifically looking for is what gets put into each array and what the values of I and J are. I suspect that you will find that you get different results between the first time you run this and after the reload.
Re: Trouble with Collision Between Objects Defined in Different Arrays
The problem with your skeletons all changing direction together is because, as you have identified, they share a variable (WhichWay), and yes, the solution is to give each it's own variable.
My suggestion would be to create a Skeleton Class with two properties: 1 A PictureBox and 2 A WhichWay variable. Your Skeletons collection would then be a collection of Skeletons, rather than a collection of PictureBoxes. (In fact, I'd probably have Skeleton, Block and Player classes because it will make each of those entities easier to manage - although the Block one might be overkill).
I'd also second Shaggy's suggestion on the reset. Rather than create a new form it would be better to reset the state of the current one. Those dead instances are going to cause you grief.
Re: Trouble with Collision Between Objects Defined in Different Arrays
Quote:
Originally Posted by
FunkyDexter
I'd also second Shaggy's suggestion on the reset. Rather than create a new form it would be better to reset the state of the current one. Those dead instances are going to cause you grief.
I guess I decided to go the route I did because I didn't know how to place the skeletons back in their original positions, what with them not being individually named and all.
Re: Trouble with Collision Between Objects Defined in Different Arrays
How do you place them now? Is it just where you're positioned the picture boxes on the form?
I don't think it matters that they're not individually named (although my Class suggestion would allow that;)). You don't care which skeleton starts at e.g. 50,12. You just care that a skeleton starts at 50,12. Just iterate through your Skeletons collection and set one skeleton to each starting position.
Re: Trouble with Collision Between Objects Defined in Different Arrays
If you went with a skeleton class then you could give them starting positions as member variables. Resetting them would just be a matter of changing their current positions to those stored starting positions.
A class is pretty much the way to go in a case like this, but it depends on where you're at. After all, you have two obvious objects: Skeletons and Blocks. Those objects have several obvsious members: Position, name, starting position, and quite possibly more. So, classes make sense.