locked
HELP PLZZZ RRS feed

  • Question

  • When I click on 2 photos a message appears saying "a calling thread cannot access this object because a different thread owns it" I have no clue how to fix this PLZZZ HELP I just started this class so I don't know many key words here is my code

    ' Match Game
    InitializeProgram()
    
    Sub InitializeProgram
      'graphics window
      GraphicsWindow.Width = 825
      GraphicsWindow.Height = 530
      GraphicsWindow.BackgroundColor = "LightSkyBlue"
      'initialize box locations with covers
      Cover = "C:\New folder\gold.png"
      BoxW = 150
      BoxH = 100
      x = 5
      y = 5
      For I = 1 To 20
        BoxX[I] = x
        BoxY[I] = y
        GraphicsWindow.DrawResizedImage(Cover, BoxX[I], BoxY[I], BoxW, BoxH)
        x = x + BoxW + 5
        If (x > 4 * BoxW) Then
          x = 5
          y = y + BoxH + 5
        EndIf
      EndFor
    EndSub
    'load images
    Photo[1] = "C:\Users\John\Desktop\New folder (2)\gg.jpg"
    Photo[2] = "C:\Users\John\Desktop\New folder (2)\ggg.jpg"
    Photo[3] = "C:\Users\John\Desktop\New folder (2)\gggg.jpg"
    Photo[4] = "C:\Users\John\Desktop\New folder (2)\ggggg.jpg"
    Photo[5] = "C:\Users\John\Desktop\New folder (2)\gggggg.jpg"
    Photo[6] = "C:\Users\John\Desktop\New folder (2)\ggggggg.jpg"
    Photo[7] = "C:\Users\John\Desktop\New folder (2)\gggggggg.jpg"
    Photo[8] = "C:\Users\John\Desktop\New folder (2)\ggggggggg.jpg"
    Photo[9] = "C:\Users\John\Desktop\New folder (2)\gggggggggg.jpg"
    Photo[10] ="C:\Users\John\Desktop\New folder (2)\ggggggggggg.jpg"
    Timer.Interval = 1000
    Timer.Pause()
    Timer.Tick = TickSub
    
    GraphicsWindow.MouseDown = MouseDownSub
    Sub MouseDownSub
      If (GameStatus = "Playing" And CanClick) Then
        'find which box was clicked
        x = GraphicsWindow.MouseX
        y = GraphicsWindow.MouseY
        BoxNumber = 0
        For I = 1 To 20
          If (x > BoxX[I] And x < BoxX[I] + BoxW) Then
            If (y > BoxY[I] And y < BoxY[I] + BoxH) Then
              BoxNumber = I
              Goto GotIt
            EndIf
          EndIf
        EndFor
        GotIt:
        If (BoxNumber <> 0) Then
          'if already clicked then exit
          If (PhotoFound[BoxNumber]) Then
            Goto LeaveMouseDownSub
          EndIf
          ShowPhotoAndCheck()
        EndIf
      EndIf
    LeaveMouseDownSub:
    EndSub
    
    'headings
    GraphicsWindow.BrushColor = "Black"
    GraphicsWindow.FontBold = "false"
    GraphicsWindow.FontSize = 20
    Heading[1] = Shapes.AddText("Player 1")
    Heading[2] = Shapes.AddText("Player 2")
    Shapes.Move(Heading[1], 670, 5)
    Shapes.Move(Heading[2], 670, 70)
    'scores
    GraphicsWindow.BrushColor = "White"
    GraphicsWindow.FillRectangle(670, 35, 100, 25)
    GraphicsWindow.FillRectangle(670, 100, 100, 25)
    GraphicsWindow.BrushColor = "Black"
    Score[1] = 0
    Score[2] = 0
    ScoreDisplay[1] = Shapes.AddText(Score[1])
    ScoreDisplay[2] = Shapes.AddText(Score[2])
    Shapes.Move(ScoreDisplay[1], 715, 35)
    Shapes.Move(ScoreDisplay[2], 715, 100)
    'Draw message area
    GraphicsWindow.BrushColor = "Yellow"
    GraphicsWindow.FillRectangle(630, 250, 185, 45)
    GraphicsWindow.PenColor = "Black"
    GraphicsWindow.PenWidth = 2
    GraphicsWindow.DrawRectangle(630, 250, 185, 45)
    GraphicsWindow.BrushColor = "Blue"
    GraphicsWindow.FontBold = "false"
    GraphicsWindow.FontSize = 18
    Message = "Game Stopped"
    MessageX = 30
    MessageArea = Shapes.AddText(Message)
    DisplayMessage()
    Sub DisplayMessage
      Shapes.Move(MessageArea, 630 + MessageX, 260)
      Shapes.SetText(MessageArea, Message)
    EndSub
    'define buttons
    GraphicsWindow.BrushColor = "Black"
    GraphicsWindow.FontSize = 16
    StartStopButton = Controls.AddButton("Start Game", 630, 410)
    OptionsButton = Controls.AddButton("Change Options", 630, 450)
    ExitButton = Controls.AddButton("Exit", 630, 490)
    'Default Options
    NumberPlayers = 2
    PlayAlone = "true"
    Difficulty = 1
    SetTitle()
    GameStatus = "Stopped"
    Sub SetTitle
      If (NumberPlayers = 1) Then
        GraphicsWindow.Title = "Match Game - 1 Player, "
        If (PlayAlone) Then
          GraphicsWindow.Title = GraphicsWindow.Title + "Play Alone"
        Else
          GraphicsWindow.Title = GraphicsWindow.Title + "Play Computer"
        EndIf
      Else
        GraphicsWindow.Title = "Match Game - 2 Players"
      EndIf
    EndSub
    Controls.ButtonClicked = ButtonClickedSub
    Sub ButtonClickedSub
      B = Controls.LastClickedButton
      If (GameStatus = "Stopped") Then
        If (B = ExitButton) Then
          Program.End()
        ElseIf (B = OptionsButton) Then
          SetOptions()
        ElseIf (B = StartStopButton) Then
          StartGame()
        EndIf
      ElseIf (GameStatus = "Playing") Then
        If (B = StartStopButton) Then
          'stop program
          Message = "Game Stopped"
          MessageX = 30
          StopGame()
        EndIf
      EndIf
    EndSub
    Sub SetOptions
      GraphicsWindow.Hide()
      TextWindow.Show()
      TextWindow.Title = "Match Game"
      TextWindow.CursorLeft = 3
      TextWindow.CursorTop = 3
      TextWindow.WriteLine("MATCH GAME OPTIONS")
      TextWindow.WriteLine("")
      GetPlayers:
      TextWindow.CursorLeft = 3
      TextWindow.WriteLine("With one player, you play alone or against the computer.")
      TextWindow.CursorLeft = 3
      TextWindow.WriteLine("With two players, you play against a friend.")
      TextWindow.CursorLeft = 3
      TextWindow.Write("How many players do you want (1 or 2)? ")
      NumberPlayers = TextWindow.ReadNumber()
      If (NumberPlayers < 1 Or NumberPlayers > 2) Then
        Goto GetPlayers
      EndIf
      If (NumberPlayers = 1) Then
        Heading[1] = "You"
        GetPlayAlone:
        TextWindow.WriteLine("")
        TextWindow.CursorLeft = 3
        TextWindow.WriteLine("You can play alone or play the computer.")
        TextWindow.CursorLeft = 3
        TextWindow.Write("Make your choice (1-Play Alone, 2-Play Computer)? ")
        T = TextWindow.ReadNumber()
        If (T < 1 Or T > 2) Then
          Goto GetPlayAlone
        EndIf
        If (T = 1) Then
          PlayAlone = "true"
          Heading[2] = "Guesses"
        Else
          PlayAlone = "false"
          Heading[2] = "Computer"
          GetDifficulty:
          TextWindow.WriteLine("")
          TextWindow.CursorLeft = 3
          TextWindow.WriteLine("You can set the game difficulty.")
          TextWindow.CursorLeft = 3
          TextWindow.Write("Make your choice (1-Easiest, 2-Easy, 3-Hard, 4-Hardest)? ")
          Difficulty = TextWindow.ReadNumber()
          If (Difficulty < 1 Or Difficulty > 4) Then
            Goto GetDifficulty
          EndIf
        EndIf
      Else
        Heading[1] = "Player 1"
        Heading[2] = "Player 2"
      EndIf
      SetTitle()
      TextWindow.Hide()
      GraphicsWindow.Show()
    EndSub
    GameStatus = "Stopped"
    
    For I = 1 To 10
      GraphicsWindow.DrawResizedImage(Photo[I], BoxX[I], BoxY[I], BoxW, BoxH)
    EndFor
    Sub NRandomIntegers
      'Order all elements initially
      For I = 1 To N
        NIntegers[I] = I
      EndFor
      'J is the number of integers remaining
      For J = N To 2 Step -1
        I = Math.GetRandomNumber(J)
        T = NIntegers[J]
        NIntegers[J] = NIntegers[I]
        NIntegers[I] = T
      EndFor
    EndSub
    N = 20
    NRandomIntegers()
    PhotoIndex = NIntegers
    For I = 1 To 20
      If (PhotoIndex[I] > 10) Then 
        PhotoIndex[I] = PhotoIndex[I] - 10
      EndIf
    EndFor
    GraphicsWindow.DrawResizedImage(Photo[PhotoIndex[I]], BoxX[I], BoxY[I], BoxW, BoxH)
    Sub KeyDownSub
      K = Text.ConvertToUpperCase(GraphicsWindow.LastKey)
      If (GameStatus = "Stopped") Then
        If (K = "E") Then
          Program.End()
        ElseIf (K = "C") Then
          SetOptions()
        ElseIf (K = "S") Then
          StartGame()
        EndIf
      EndIf
    EndSub
    Sub StartGame
      GameStatus = "Playing"
      'clear screen
      GraphicsWindow.BrushColor = GraphicsWindow.BackgroundColor
      GraphicsWindow.FillRectangle(0, 0, 625, 530)
      Score[1] = 0
      Score[2] = 0
      PhotosRemaining = 20
      N = 20
      NRandomIntegers()
      PhotoIndex = NIntegers
      For I = 1 To 20
        If (PhotoIndex[I] > 10) Then 
          PhotoIndex[I] = PhotoIndex[I] - 10
        EndIf
        PhotoFound[I] = "false"
        GraphicsWindow.DrawResizedImage(Cover, BoxX[I], BoxY[I], BoxW, BoxH)
      EndFor
      PlayerNumber = 1
      ChoiceNumber = 1
      If (NumberPlayers = 1) Then
        Message = "Pick a Box"
        MessageX = 45
      Else
        Message = "Player 1, Pick a Box"
        MessageX = 10
      EndIf
      DisplayMessage()
      'reset buttons
      GraphicsWindow.BrushColor = "Black"
      GraphicsWindow.FontSize = 16
      Controls.SetButtonCaption(StartStopButton, "Stop Game")
      Controls.HideControl(OptionsButton)
      Controls.HideControl(ExitButton)
      CanClick = "true"
    EndSub
    Sub StopGame
      'restore buttons
      GameStatus = "Stopped"
      GraphicsWindow.BrushColor = "Black"
      GraphicsWindow.FontSize = 16
      Controls.SetButtonCaption(StartStopButton, "Start Game")
      Controls.ShowControl(OptionsButton)
      Controls.ShowControl(ExitButton)
      CanClick = "false"
      DisplayMessage()
    EndSub
    Sub ShowPhotoAndCheck
      'show image behind selected Photo box (BoxNumber)
      GraphicsWindow.DrawResizedImage(Photo[PhotoIndex[BoxNumber]], BoxX[BoxNumber], BoxY[BoxNumber], BoxW, BoxH)
      PhotoFound[BoxNumber] = "true"
      If (ChoiceNumber = 1) Then
        Choice[1] = BoxNumber
        ChoiceNumber = 2
        If (NumberPlayers = 2) Then
          Message = "Player " + PlayerNumber + ", Pick Another"
          MessageX = 5
          DisplayMessage()
        Else
          'one player logic
        EndIf
      Else
        CanClick = "false"
        Timer.Resume()
      EndIf
    EndSub
    
    Sub TickSub
      Choice[2] = BoxNumber
        ChoiceNumber = 1
        If (PhotoIndex[Choice[1]] = PhotoIndex[Choice[2]]) Then
          'a match
          Sound.Stop(Program.Directory + "\tada.wav")
          Sound.PlayAndWait(Program.Directory + "\tada.wav")
          'blank out boxes
          GraphicsWindow.BrushColor = "White"
          GraphicsWindow.FillRectangle(BoxX[Choice[1]], BoxY[Choice[1]], BoxW, BoxH)
          GraphicsWindow.FillRectangle(BoxX[Choice[2]], BoxY[Choice[2]], BoxW, BoxH)
          Score[PlayerNumber] = Score[PlayerNumber] + 1
          PhotosRemaining = PhotosRemaining - 2
          If (PhotosRemaining = 0) Then
            Sound.Stop(Program.Directory + "\wow.wav")
            Sound.Play(Program.Directory + "\wow.wav")
            If (NumberPlayers = 2) Then
              If (Score[1] > Score[2]) Then
                Message = "Player 1 Wins!"
                MessageX = 30
              ElseIf (Score[2] > Score[1]) Then
                Message = "Player 2 Wins!"
                MessageX = 30
              Else
                Message = "It's a Tie!"
                MessageX = 45
              EndIf
            Else
              'one player logic
            EndIf
            StopGame()
          EndIf
          'another turn?
          If (PhotosRemaining <> 0) Then
            If (NumberPlayers = 2) Then
              Message = "Player " + PlayerNumber + ", Pick Again"
              MessageX = 10
              DisplayMessage()
            Else
              'one player logic
            EndIf
          EndIf
        Else
          'no match
          Sound.Stop(Program.Directory + "\boing.wav")
          Sound.PlayAndWait(Program.Directory + "\boing.wav")
          PhotoFound[Choice[1]] = "false"
          PhotoFound[Choice[2]] = "false"
          GraphicsWindow.DrawResizedImage(Cover, BoxX[Choice[1]], BoxY[Choice[1]], BoxW, BoxH)
          GraphicsWindow.DrawResizedImage(Cover, BoxX[Choice[2]], BoxY[Choice[2]], BoxW, BoxH)
          'swap players
          If (NumberPlayers = 2) Then
            If (PlayerNumber = 1) Then
              PlayerNumber = 2
            Else
              PlayerNumber = 1
            EndIf
            Message = "Player " + PlayerNumber + ", Pick a Box"
            MessageX = 10
            DisplayMessage()
          Else
            'one player logic
          EndIf
        EndIf
        CanClick = "true"
    EndSub 


     


    • Edited by Johnpan0321 Monday, February 23, 2015 10:07 PM
    Monday, February 23, 2015 9:56 PM

Answers

  • To debug this we need to do a couple of things which are common to most debugging problems.

    1] Add TextWindow.WriteLine statements in lots of places to see exactly where the problem occurs - this will help to write the simple test code to show the issue.  Start by one in the start and end of each Subroutine for example, then add more to identify the line at which the problem occurs.

    Then, if the issue is not clear:

    2] Simplify the code as much as possible to show the error - i.e write a simple test program or start from your program and delete bits that are not relevant to your problem.  If you don't need images or sounds in other files to show the problem then remove them so that some-one else can run the file and see the problem.  If extra images or files are needed to show it, then use test web files, again so the program can be run by some-one else.

    Once you do this we can see that the issue is with Sound.Stop().

    And a simple test file:

    musicFile = "http://litdev.co.uk/game_images/backgroundMusic.mp3"
    Sound.Play(musicFile)
    Timer.Interval = 5000
    Timer.Tick = OnTick
    
    Sub OnTick
      Sound.Stop(musicFile)
    EndSub

    So the problem is that we cannot act on the music with Sound commands from inside an event subroutine - they work on a separate thread to the main UI thread.

    Solution: do all work on the main UI thread and only use the event subroutines to set flags:

    musicFile = "http://litdev.co.uk/game_images/backgroundMusic.mp3"
    Sound.Play(musicFile)
    Timer.Interval = 5000
    Timer.Tick = OnTick
    
    While ("True")
      If (restartMusic) Then
        Sound.Stop(musicFile)
        Program.Delay(100) 'To hear restart
        Sound.Play(musicFile)
        restartMusic = "False"
      EndIf
      Program.Delay(20) 'Don't mash cpu while waiting for input in game loop
    EndWhile
    
    Sub OnTick
      restartMusic = "True" 'Flag that the music should restart
    EndSub

    Or more moduar:

    musicFile = "http://litdev.co.uk/game_images/backgroundMusic.mp3"
    Sound.Play(musicFile)
    Timer.Interval = 5000
    Timer.Tick = OnTick
    
    While ("True")
      CheckRestart()
      Program.Delay(20)
    EndWhile
    
    Sub OnTick
      restartMusic = "True"
    EndSub
    
    Sub CheckRestart
      If (restartMusic) Then
        Sound.Stop(musicFile)
        Program.Delay(100) 'To hear restart
        Sound.Play(musicFile)
        restartMusic = "False"
      EndIf
    EndSub

    For more info see the following wiki articles

    http://social.technet.microsoft.com/wiki/contents/articles/15060.small-basic-event-basics.aspx

    http://social.technet.microsoft.com/wiki/contents/articles/22264.small-basic-threading.aspx

    and for the game loop (While )"True" loop

    http://social.technet.microsoft.com/wiki/contents/articles/20865.small-basic-dynamic-graphics.aspx

    PS nice program interface by the way - the game loop and event flags is the best way in Small Basic and often in other languages - good luck

    • Edited by litdev Monday, February 23, 2015 10:34 PM
    • Marked as answer by Johnpan0321 Monday, February 23, 2015 10:47 PM
    Monday, February 23, 2015 10:31 PM

All replies

  • To debug this we need to do a couple of things which are common to most debugging problems.

    1] Add TextWindow.WriteLine statements in lots of places to see exactly where the problem occurs - this will help to write the simple test code to show the issue.  Start by one in the start and end of each Subroutine for example, then add more to identify the line at which the problem occurs.

    Then, if the issue is not clear:

    2] Simplify the code as much as possible to show the error - i.e write a simple test program or start from your program and delete bits that are not relevant to your problem.  If you don't need images or sounds in other files to show the problem then remove them so that some-one else can run the file and see the problem.  If extra images or files are needed to show it, then use test web files, again so the program can be run by some-one else.

    Once you do this we can see that the issue is with Sound.Stop().

    And a simple test file:

    musicFile = "http://litdev.co.uk/game_images/backgroundMusic.mp3"
    Sound.Play(musicFile)
    Timer.Interval = 5000
    Timer.Tick = OnTick
    
    Sub OnTick
      Sound.Stop(musicFile)
    EndSub

    So the problem is that we cannot act on the music with Sound commands from inside an event subroutine - they work on a separate thread to the main UI thread.

    Solution: do all work on the main UI thread and only use the event subroutines to set flags:

    musicFile = "http://litdev.co.uk/game_images/backgroundMusic.mp3"
    Sound.Play(musicFile)
    Timer.Interval = 5000
    Timer.Tick = OnTick
    
    While ("True")
      If (restartMusic) Then
        Sound.Stop(musicFile)
        Program.Delay(100) 'To hear restart
        Sound.Play(musicFile)
        restartMusic = "False"
      EndIf
      Program.Delay(20) 'Don't mash cpu while waiting for input in game loop
    EndWhile
    
    Sub OnTick
      restartMusic = "True" 'Flag that the music should restart
    EndSub

    Or more moduar:

    musicFile = "http://litdev.co.uk/game_images/backgroundMusic.mp3"
    Sound.Play(musicFile)
    Timer.Interval = 5000
    Timer.Tick = OnTick
    
    While ("True")
      CheckRestart()
      Program.Delay(20)
    EndWhile
    
    Sub OnTick
      restartMusic = "True"
    EndSub
    
    Sub CheckRestart
      If (restartMusic) Then
        Sound.Stop(musicFile)
        Program.Delay(100) 'To hear restart
        Sound.Play(musicFile)
        restartMusic = "False"
      EndIf
    EndSub

    For more info see the following wiki articles

    http://social.technet.microsoft.com/wiki/contents/articles/15060.small-basic-event-basics.aspx

    http://social.technet.microsoft.com/wiki/contents/articles/22264.small-basic-threading.aspx

    and for the game loop (While )"True" loop

    http://social.technet.microsoft.com/wiki/contents/articles/20865.small-basic-dynamic-graphics.aspx

    PS nice program interface by the way - the game loop and event flags is the best way in Small Basic and often in other languages - good luck

    • Edited by litdev Monday, February 23, 2015 10:34 PM
    • Marked as answer by Johnpan0321 Monday, February 23, 2015 10:47 PM
    Monday, February 23, 2015 10:31 PM
  • Wow thanks for help but where do I put this coding at?
    Monday, February 23, 2015 10:48 PM
  • I'm afraid it isn't just a case of slotting some code in.  It will take a refoctoring (redesign) based on the ideas in the links (game loop and using events to set flags).

    I suggest the following.

    1] Read the links and ask any questionns on them, and prototype a bit.  Also maybe these:

    http://social.technet.microsoft.com/wiki/contents/articles/15081.small-basic-programming-tips.aspx

    2] In general, write the structure of the program first, then add the detail.

    3] When ready to modify your code:

    • Clearly identify the main sections of code (Initialisation, Game Loop, Event subroutines and other subroutines).
    • Don't mix code in and out of subroutines - put all subroutines after non-subroutine code
    • Create a game loop with short delay
    • All event subroutines should set a flag
    • The event flags are checked in the game loop directly or in subroutines called from the game loop

    This is the kindof structure I mean - I have no images or sounds and there may be other problems, but this is now structed OK, import TJX132.

    • Edited by litdev Tuesday, February 24, 2015 7:24 PM
    Tuesday, February 24, 2015 7:12 PM