如何检测同一数组中PictureBox的碰撞并实现碰撞反弹
问题描述
我正在开发一款游戏,需要实现敌人(太空垃圾)相互碰撞时向相反方向反弹的功能。我尝试使用两层For循环,结合IntersectsWith方法检测碰撞,但未能成功。当前已通过Timer实现飞船、基地、太空垃圾等对象的移动及部分碰撞逻辑,现需解决数组中太空垃圾之间的碰撞检测及反弹问题。
以下是我的Form1类完整代码:
Public Class Form1 Private pic_Nave As Nave Private pic_Base As Base Private vDesecho(10) As Desecho Private nivel As Integer Private vAsteroide(10) As Asteroide Private desechoEliminated As Integer 'Private cont(10) As Double Private spawntime As Integer Private Sub tmr_Mover_Tick(sender As Object, e As EventArgs) Handles tmr_Mover.Tick pic_Nave.Mover(Me) pic_Base.Mover(Me) spawntime -= 1 lblitems.Text = spawntime For i = 0 To 9 vDesecho(i).Mover(Me) If (pic_Nave.imagen.Bounds.IntersectsWith(vDesecho(i).imagen.Bounds)) Then Controls.Remove(vDesecho(i).imagen) End If If (pic_Base.imagen.Bounds.IntersectsWith(vDesecho(i).imagen.Bounds)) Then vDesecho(i).diry = -vDesecho(i).diry End If Next 'For i = 0 To 9 'For j = 9 To 0 'If (vDesecho(i).imagen.Bounds.IntersectsWith(vDesecho(j).imagen.Bounds)) Then 'spawntime = 5 'vDesecho(i).dirx = -vDesecho(i).dirx 'vDesecho(j).diry = -vDesecho(j).diry 'vDesecho(j).Mover(Me) 'End If 'Next 'Next For i = 0 To nivel - 1 vAsteroide(i).Mover(Me) If (pic_Base.imagen.Bounds.IntersectsWith(vAsteroide(i).imagen.Bounds)) Then vAsteroide(i).diry = -vAsteroide(i).diry End If Next End Sub Private Sub makedesecho() For i = 1 To 1 vDesecho(i) = New Desecho() Controls.Add(vDesecho(i).imagen) vDesecho(i).Mover(Me) If (pic_Nave.imagen.Bounds.IntersectsWith(vDesecho(i).imagen.Bounds)) Then Controls.Remove(vDesecho(i).imagen) desechoEliminated = desechoEliminated + 1 End If Next End Sub Public Sub New() ' Esta llamada es exigida por el diseñador. InitializeComponent() ' Agregue cualquier inicialización después de la llamada a InitializeComponent(). nivel = 1 desechoEliminated = 0 spawntime = 5 For i = 0 To 9 vDesecho(i) = New Desecho() Controls.Add(vDesecho(i).imagen) Next For i = 0 To nivel - 1 vAsteroide(i) = New Asteroide() Controls.Add(vAsteroide(i).imagen) Next pic_Nave = New Nave() Controls.Add(pic_Nave.imagen) pic_Base = New Base() Controls.Add(pic_Base.imagen) tmr_Mover.Start() End Sub Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean Select Case keyData Case Keys.Up pic_Nave.diry -= 1 Case Keys.Down pic_Nave.diry += 1 Case Keys.Left pic_Nave.dirx -= 1 Case Keys.Right pic_Nave.dirx += 1 End Select Return MyBase.ProcessCmdKey(msg, keyData) End Function End Class
解决方案
核心问题分析
你之前的碰撞检测代码存在两个关键问题:
- 重复检测:循环中
i和j遍历全范围,会重复检测同一对太空垃圾(如i=0,j=1和i=1,j=0),还会触发自身碰撞(i=j)。 - 错误的反弹逻辑:只反转其中一个对象的x方向和另一个的y方向,不符合物理反弹的预期。
修正后的实现
1. 优化碰撞检测循环
让j从i+1开始遍历,确保每对垃圾只检测一次,且跳过自身:
For i = 0 To 9 ' 跳过已被移除的太空垃圾 If Not Me.Controls.Contains(vDesecho(i).imagen) Then Continue For End If For j = i + 1 To 9 ' 跳过已被移除的太空垃圾 If Not Me.Controls.Contains(vDesecho(j).imagen) Then Continue For End If ' 检测碰撞 If vDesecho(i).imagen.Bounds.IntersectsWith(vDesecho(j).imagen.Bounds) Then ' 方案1:简单反转方向(模拟反弹) vDesecho(i).dirx = -vDesecho(i).dirx vDesecho(i).diry = -vDesecho(i).diry vDesecho(j).dirx = -vDesecho(j).dirx vDesecho(j).diry = -vDesecho(j).diry ' 方案2:交换速度(更真实的弹性碰撞,假设质量相同) ' Dim tempDirX As Integer = vDesecho(i).dirx ' Dim tempDirY As Integer = vDesecho(i).diry ' vDesecho(i).dirx = vDesecho(j).dirx ' vDesecho(i).diry = vDesecho(j).diry ' vDesecho(j).dirx = tempDirX ' vDesecho(j).diry = tempDirY ' 移动一次避免对象卡在一起 vDesecho(i).Mover(Me) vDesecho(j).Mover(Me) End If Next Next
2. 替换到Timer事件中
把这段代码替换掉你原来注释的碰撞循环,放在太空垃圾移动循环之后即可:
Private Sub tmr_Mover_Tick(sender As Object, e As EventArgs) Handles tmr_Mover.Tick pic_Nave.Mover(Me) pic_Base.Mover(Me) spawntime -= 1 lblitems.Text = spawntime For i = 0 To 9 vDesecho(i).Mover(Me) If (pic_Nave.imagen.Bounds.IntersectsWith(vDesecho(i).imagen.Bounds)) Then Controls.Remove(vDesecho(i).imagen) End If If (pic_Base.imagen.Bounds.IntersectsWith(vDesecho(i).imagen.Bounds)) Then vDesecho(i).diry = -vDesecho(i).diry End If Next ' 新增/修正的太空垃圾碰撞检测代码 For i = 0 To 9 If Not Me.Controls.Contains(vDesecho(i).imagen) Then Continue For End If For j = i + 1 To 9 If Not Me.Controls.Contains(vDesecho(j).imagen) Then Continue For End If If vDesecho(i).imagen.Bounds.IntersectsWith(vDesecho(j).imagen.Bounds) Then vDesecho(i).dirx = -vDesecho(i).dirx vDesecho(i).diry = -vDesecho(i).diry vDesecho(j).dirx = -vDesecho(j).dirx vDesecho(j).diry = -vDesecho(j).diry vDesecho(i).Mover(Me) vDesecho(j).Mover(Me) End If Next Next For i = 0 To nivel - 1 vAsteroide(i).Mover(Me) If (pic_Base.imagen.Bounds.IntersectsWith(vAsteroide(i).imagen.Bounds)) Then vAsteroide(i).diry = -vAsteroide(i).diry End If Next End Sub
3. 额外注意事项
- 空引用防护:检测前先判断控件是否存在,避免已被移除的垃圾对象引发错误。
- 碰撞后位移:碰撞后立即调用一次
Mover,避免两个对象重叠卡住。 - 进阶优化:如果需要更精确的碰撞,可以使用矩形的交集区域判断碰撞方向,只反转对应轴的方向(比如左右碰撞反转x,上下碰撞反转y)。
内容的提问来源于stack exchange,提问作者Lizmarie Feriz
相关产品推荐
相关产品推荐

