【问题标题】:VB.NET - Movement Blocked due to Faulty Collision DetectionVB.NET - 由于错误的碰撞检测而阻止运动
【发布时间】:2019-12-26 23:35:02
【问题描述】:

所以,我正在为我的编程课制作一款游戏,作为我期末项目的一部分。我目前正处于计划和试验阶段,我决定在图形和碰撞方面抢占先机。我首先通过尝试 VB 提供的Graphics 类而不是使用PictureBoxes 来制作我的程序。除此之外,我添加了键盘输入来移动Image。当我决定通过Image 类的intersectsWith() 方法添加碰撞检测时,事情变得很奇怪。

基本上,在我的代码中,“玩家”实体具有三个不同的图像——它们的变化取决于他们面对的方向,而这又取决于用户按下的键。没有任何碰撞检测代码,移动和图像更改工作正常,图像移动。但是,一旦我添加了碰撞检测,玩家就根本不动,只有他们面对的方式发生了变化。即使玩家的Image 与我要测试的交叉点图像(美元符号)相去甚远,也会发生这种情况。这是我的完整代码:

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

基本上,每次我按下一个键并希望图像移动时,图像都不会移动(即使玩家离美元符号不远),但他们所面对的方向仍然会发生变化。我怎样才能让播放器保持移动并阻止播放器与另一张图像发生碰撞?

【问题讨论】:

  • 删除Me.Invalidate() 方法。您应该从其他地方调用此方法来刷新绘图。在Paint event = dead/infinite 循环中使用它。在movePlayer 方法中使用它作为最后一行。
  • 我试过了,但它只是完全冻结了播放器——Image 甚至没有改变方向。我尝试将它放在 KeyDown 事件中的每个 if 语句的末尾,但它也不起作用。

标签: vb.net graphics logic collision-detection game-development


【解决方案1】:

嗯,

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

将始终返回False,因为GetBounds 方法不会返回每个矩形的当前位置。所以它们永远不会相交,你的绘图场景保持不变。

那么让我们尝试解决这个问题。

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)

现在IntersectWith 函数应该在movePlayer 方法中工作:

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

请注意,pxpy 变量现在都是本地变量,因为我们已经有了 pIrect,其中包括当前的 xy。我们用Select Case 替换了If 语句,我相信这是一种更好的方法。我们创建了一个新的矩形来检查任何可能的碰撞,如果没有,那么我们更新我们的pIrect 并刷新绘图。

除了通过 W S A D 键移动图像外,您还可以使用← ↑ → ↓ 键。要在KeyDown 事件中拦截它们,只需重写IsInputKey 函数,如下所示:

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

因此,KeyDown 事件:

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

同样,我们将 If Then Else 语句替换为 Select Case。如果您不应该这样做,我相信您可以轻松恢复并使用If e.KeyCode = Keys.W OrElse e.KeyCode = Keys.Up Then ...

Paint 例程:

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

最后别忘了清理:

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

祝你好运

【讨论】:

    猜你喜欢
    • 2012-04-03
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多