VB.NET - Movement Blocked due to Faulty Collision Detection

£可爱£侵袭症+ 提交于 2020-01-24 16:27:06

问题


So, I am making a game for my programming class as part of my final project. I'm just in the planning and experimenting stage at the moment and I decided to get a headstart on graphics and collisions. I first made my program just by experimenting with the Graphics class VB has to offer, instead of using PictureBoxes. Alongside that, I added keyboard input to move an Image around. When I decided to add collision detection through the intersectsWith() method of the Image class, things became weird.

Basically, in my code, the "Player" entity has three different images - which change depending on which way they are facing, which is in turn determined by what key the user presses. Without any collision detection code, the movement and image changing works fine and the image moves about. However, as soon as I add collision detection the player does not move at all, only the way they face changes. This happens even if the player's Image is nowhere near the image I want to test for intersection (a dollar sign). Here's my entire code:

Public Class Form1

    Enum DirectionFacing
        FORWARDS
        BACKWARD
        LEFT
        RIGHT
    End Enum

    ' Player X position.
    Dim pX As Integer = 100
    ' Player Y position.
    Dim pY As Integer = 100
    ' The direction the player is facing - by default, backward.
    Dim dir As DirectionFacing = DirectionFacing.BACKWARD
    ' The image of the player.
    Dim pI As Image = My.Resources.MainCharacter_Forward
    ' Another image designed to test for collision detection.
    Dim dI As Image = My.Resources.DollarSign

    Private Sub Form1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
        If (e.KeyCode = Keys.W) Then
            ' If they press W, move forward.
            dir = DirectionFacing.FORWARDS
            pI = My.Resources.MainCharacter_Forward
            movePlayer(DirectionFacing.FORWARDS, 10)
        ElseIf (e.KeyCode = Keys.S) Then
            ' If they press S, move backward.
            dir = DirectionFacing.BACKWARD
            pI = My.Resources.MainCharacter_Behind
            movePlayer(DirectionFacing.BACKWARD, 10)
        ElseIf (e.KeyCode = Keys.A) Then
            ' If they press A, move to the left.
            pI = My.Resources.MainCharacter_Side
            dir = DirectionFacing.LEFT
            movePlayer(DirectionFacing.LEFT, 10)
        ElseIf (e.KeyCode = Keys.D) Then
            ' If they press D, move to the right. To make the player face rightward,
            ' the image can be flipped.
            Dim flipped As Image = My.Resources.MainCharacter_Side
            flipped.RotateFlip(RotateFlipType.RotateNoneFlipX)
            pI = flipped
            dir = DirectionFacing.LEFT
            movePlayer(DirectionFacing.RIGHT, 10)
        End If
    End Sub

    ' Moves the player by a certain amount AND checks for collisions.
    Private Sub movePlayer(dir As DirectionFacing, amount As Integer)
        If (dI.GetBounds(GraphicsUnit.Pixel).IntersectsWith(pI.GetBounds(GraphicsUnit.Pixel))) Then
            Return
        End If

        If (dir = DirectionFacing.FORWARDS) Then
            pY -= 10
        ElseIf (dir = DirectionFacing.BACKWARD) Then
            pY += 10
        ElseIf (dir = DirectionFacing.LEFT) Then
            pX -= 10
        ElseIf (dir = DirectionFacing.RIGHT) Then
            pX += 10
        End If

    End Sub

    Private Sub draw(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        Dim g As Graphics = e.Graphics()
        g.DrawImage(dI, 400, 350)
        g.DrawImage(pI, pX, pY)
        Me.Invalidate()
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.DoubleBuffered = True
    End Sub
End Class

Basically, every time I press a key and want the image to move, the image doesn't move at all (even when the Player is nowhere close to the dollar sign), but the direction they are facing still changes. How can I keep the player moving and still stop the player from colliding with another image?


回答1:


Well, the

If (dI.GetBounds(GraphicsUnit.Pixel).IntersectsWith(pI.GetBounds(GraphicsUnit.Pixel)))

will always return False since the GetBounds method does not return the current location of each rectangle. So they will never intersect, and your drawing scene remains the same.

So let's try to solve this problem.

Enum DirectionFacing
    FORWARDS
    BACKWARD
    LEFT
    RIGHT
End Enum

' The image of the player.
Dim pI As New Bitmap(My.Resources.MainCharacter_Forward)
' Another image designed to test for collision detection.
Dim dI As New Bitmap(My.Resources.DollarSign)
'The rectangle of the player's image.
Dim pIrect As New Rectangle(100, 100, pI.Width, pI.Height)
'The static rectangle of the collision's image.
Dim dIrect As New Rectangle(400, 350, dI.Width, dI.Height)

Now the IntersectWith function should work in the movePlayer method:

Private Sub movePlayer(dir As DirectionFacing, amount As Integer)
    Dim px = pIrect.X
    Dim py = pIrect.Y

    Select Case dir
        Case DirectionFacing.FORWARDS
            py -= amount
        Case DirectionFacing.BACKWARD
            py += amount
        Case DirectionFacing.LEFT
            px -= amount
        Case DirectionFacing.RIGHT
            px += amount
    End Select

    If Not New Rectangle(px, py, pI.Width, pI.Height).IntersectsWith(dIrect) Then
        pIrect = New Rectangle(px, py, pI.Width, pI.Height)
        Invalidate()
    End If
End Sub

Note that, both px and py variables are now locals because we already have pIrect which includes the currect x and y. We replaced the If statement with Select Case as a better approach I believe. We created a new rectangle to check any possible collision, if not, then we update our pIrect and refresh the drawing.

Besides moving the image through the W S A D keys, you also can make use of the ← ↑ → ↓ keys. To intercept them in the KeyDown event, just override the IsInputKey function as follow:

Protected Overrides Function IsInputKey(keyData As Keys) As Boolean
    Select Case keyData And Keys.KeyCode
        Case Keys.Left, Keys.Up, Keys.Right, Keys.Down
            Return True
        Case Else
            Return MyBase.IsInputKey(keyData)
    End Select
End Function

Thus, the KeyDown event:

Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    Select Case e.KeyCode
        Case Keys.W, Keys.Up
            pI?.Dispose()
            pI = New Bitmap(My.Resources.MainCharacter_Forward)
            movePlayer(DirectionFacing.FORWARDS, 10)
        Case Keys.S, Keys.Down
            pI?.Dispose()
            pI = New Bitmap(My.Resources.MainCharacter_Behind)
            movePlayer(DirectionFacing.BACKWARD, 10)
        Case Keys.A, Keys.Left
            pI?.Dispose()
            pI = New Bitmap(My.Resources.MainCharacter_Side)
            movePlayer(DirectionFacing.LEFT, 10)
        Case Keys.D, Keys.Right
            pI?.Dispose()
            pI = New Bitmap(My.Resources.MainCharacter_Side)
            pI.RotateFlip(RotateFlipType.RotateNoneFlipX)
            movePlayer(DirectionFacing.RIGHT, 10)
    End Select
End Sub

Again, we replaced the If Then Else statement with Select Case. If you are not supposed to do that, I believe it will be easy for you to revert and use If e.KeyCode = Keys.W OrElse e.KeyCode = Keys.Up Then ....

The Paint routine:

Private Sub draw(sender As Object, e As PaintEventArgs) Handles Me.Paint
    Dim g As Graphics = e.Graphics()

    g.DrawImage(dI, dIrect)
    g.DrawImage(pI, pIrect)
End Sub

Finally, don't forget to clean up:

Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
    pI?.Dispose()
    dI?.Dispose()
End Sub

Good luck



来源:https://stackoverflow.com/questions/59482551/vb-net-movement-blocked-due-to-faulty-collision-detection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!