VB.NET - 由于错误的碰撞检测导致运动受阻
VB.NET - Movement Blocked due to Faulty Collision Detection
所以,我正在为我的编程制作一个游戏 class 作为我最终项目的一部分。我目前正处于计划和试验阶段,我决定先在图形和碰撞方面取得领先。我首先只是通过试验 Graphics
class VB 必须提供的而不是使用 PictureBox
es 来制作我的程序。除此之外,我还添加了键盘输入来移动 Image
。当我决定通过 Image
class 的 intersectsWith()
方法添加碰撞检测时,事情变得很奇怪。
基本上,在我的代码中,"Player" 实体具有三个不同的图像 - 它们会根据它们面向的方向而变化,而这又取决于用户按下的键。没有任何碰撞检测代码,运动和图像变化工作正常,图像移动。但是,一旦我添加碰撞检测,玩家就根本不会移动,只是他们面对的方式会发生变化。即使玩家的 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
基本上,每次我按下一个键并希望图像移动时,图像根本不会移动(即使玩家离美元符号不远),但他们面对的方向仍然在变化.我怎样才能让玩家保持移动并仍然阻止玩家与另一个图像发生碰撞?
嗯,
If (dI.GetBounds(GraphicsUnit.Pixel).IntersectsWith(pI.GetBounds(GraphicsUnit.Pixel)))
将始终 return False
因为 GetBounds
方法不会 return 每个矩形的当前位置。所以它们永远不会相交,你的绘图场景保持不变。
所以让我们尝试解决这个问题。
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
请注意,px
和 py
变量现在都是本地变量,因为我们已经有了 pIrect
,其中包括当前的 x
和 y
。我认为我们用 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
祝你好运
所以,我正在为我的编程制作一个游戏 class 作为我最终项目的一部分。我目前正处于计划和试验阶段,我决定先在图形和碰撞方面取得领先。我首先只是通过试验 Graphics
class VB 必须提供的而不是使用 PictureBox
es 来制作我的程序。除此之外,我还添加了键盘输入来移动 Image
。当我决定通过 Image
class 的 intersectsWith()
方法添加碰撞检测时,事情变得很奇怪。
基本上,在我的代码中,"Player" 实体具有三个不同的图像 - 它们会根据它们面向的方向而变化,而这又取决于用户按下的键。没有任何碰撞检测代码,运动和图像变化工作正常,图像移动。但是,一旦我添加碰撞检测,玩家就根本不会移动,只是他们面对的方式会发生变化。即使玩家的 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
基本上,每次我按下一个键并希望图像移动时,图像根本不会移动(即使玩家离美元符号不远),但他们面对的方向仍然在变化.我怎样才能让玩家保持移动并仍然阻止玩家与另一个图像发生碰撞?
嗯,
If (dI.GetBounds(GraphicsUnit.Pixel).IntersectsWith(pI.GetBounds(GraphicsUnit.Pixel)))
将始终 return False
因为 GetBounds
方法不会 return 每个矩形的当前位置。所以它们永远不会相交,你的绘图场景保持不变。
所以让我们尝试解决这个问题。
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
请注意,px
和 py
变量现在都是本地变量,因为我们已经有了 pIrect
,其中包括当前的 x
和 y
。我认为我们用 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
祝你好运