Search code examples
vb.netwinforms.net-corepicturebox

Picturebox movement out of sync


Background: I'm trying to make a Flappy Bird-esque game in Visual Basic to familiarise myself with the language (I'm going to need to code with it for the next 2 years, I don't really get a choice).

The issue is with continuous movement of the pipes. The pipes are generated every 1200ms. The game checks for each control tagged "pipe" and "score" and moves it based on another timer with interval 60ms. Code for gameloop timer:

    Private Sub gameloop_Tick(sender As Object, e As EventArgs) Handles gameloop.Tick
        ' continuous movements
        player.Top += 10
        For Each c As Control In Controls
            If c.Tag = "pipe" Or c.Tag = "score" Then
                If c.Left < -40 Then ' checking if it's off screen, then removing it if so
                    Controls.Remove(c)
                Else ' if it's on screen, move it towards the player
                    c.Left -= 10
                End If
            End If
            If c.Tag = "collider" Or c.Tag = "pipe" Then ' checking for collision
                If player.Bounds.IntersectsWith(c.Bounds) Then
                    resetgame()
                End If
            End If
            If c.Tag = "score" Then ' scoring if player goes between
                If player.Bounds.IntersectsWith(c.Bounds) Then
                    Controls.Remove(c)
                    score += 1
                    scorelabel.Text = "Score: " & score
                End If
            End If
        Next
    End Sub

The bottom pipe and the scoring zone both move in sync with one another, but the top one seemingly lags behind a little bit (all 3 should move together), and gets progressively worse as the game goes, see here. Scoring zone is made pink for clarity.

I'm not sure what's causing this issue. I checked the elapsed time for both the gameloop and pipegen timer, the code for both of which executes well within the timer interval. I also tried replacing the logic with a case statement but it still didn't change the desynchronisation. I personally couldn't find anything related to this online. Is it an issue with pictureboxes, VB or have I made a silly mistake (which is entirely possible, I'm very new to the language).

Here's the full code on pastebin.

Thanks!


Solution

  • The major error I made in the code was removing from an array while looping through it. Instead I made a list of controls to delete and looped through that afterwards:

        ' game logic
        Private Sub gameloop_Tick(sender As Object, e As EventArgs) Handles gameloop.Tick
            ' list of things to remove
            Dim removethese As New List(Of Control)
            ' continuous movements
            player.Top += 10
            For Each c As Control In Controls
                If c.Tag = "pipe" Or c.Tag = "score" Then
                    If c.Left < -40 Then ' checking if it's off screen, then removing it if so
                        removethese.Add(c)
                    Else ' if it's on screen, just move it towards the player
                        c.Left -= 10
                    End If
                End If
                If c.Tag = "collider" Or c.Tag = "pipe" Then ' checking for collision
                    If player.Bounds.IntersectsWith(c.Bounds) Then
                        resetgame()
                    End If
                End If
                If c.Tag = "score" Then ' scoring if player goes between
                    If player.Bounds.IntersectsWith(c.Bounds) Then
                        removethese.Add(c)
                        score += 1
                        scorelabel.Text = "Score: " & score
                    End If
                End If
            Next
            For Each c As Control In removethese
                Controls.Remove(c)
            Next
        End Sub
    

    All the pipes now move in sync so I did the same for the destroy() function, I no longer need to call it 3 times for it to work.

        Public Sub destroy()
            ' list of things to remove
            Dim temp As New List(Of Control)
            ' collecting controls to remove
            For Each c As Control In Controls
                If c.Tag = "pipe" Or c.Tag = "score" Then
                    temp.Add(c)
                End If
            Next
            ' removing controls
            For Each c As Control In temp
                Controls.Remove(c)
            Next
        End Sub