none
Accurate collision detection between Circle and Rectangle. RRS feed

  • Question

  • Hello Guys,

    First of all HAPPY NEW YEAR to all of you! 

    Now i have couple of question here i am making a brick game similar to Azizi's Brick Smasher but the problem is with the collision detection method (the basic one) i am using is not accurate. Is there any other better method to detect collision between a circle and a rectangle accurately?

    Second,

    Whenever the ball hit the upper left corner of the graphic window variable "Score" automatically increases by more than one unit some time by 20 some time even more. Check and run the codes

    GameWidth = 700
    GameHeight = 450
    BrickWidth = 40
    BrickHight = 20
    BallWidth = 15
    BallHeight = BallWidth
    PaddleWidth = 120
    PaddleHeight = 15
    GameRunning = "False"
    deltaX = -0.09
    deltaY = -0.09
    Score = 0
    GraphicsWindow.Width = GameWidth
    GraphicsWindow.Height = GameHeight
    GraphicsWindow.MouseMove = onMouseMove
    GraphicsWindow.MouseDown = onClick
    DrawBricks()
    CreateBallAndPaddle()
    Sub CreateBallAndPaddle
      Paddle = Shapes.AddRectangle(PaddleWidth,PaddleHeight)
      Shapes.Move(Paddle,GameWidth/2-PaddleWidth/2,GameHeight-PaddleHeight)
      Ball = Shapes.AddEllipse(BallWidth,BallHeight)
      Shapes.Move(Ball,GameWidth/2-BallWidth/2,GameHeight-PaddleHeight-BallHeight)
    EndSub
    Sub DrawBricks
      For Collumn = 1 To 5
        For Row = 1 To 8
          GraphicsWindow.BrushColor = GraphicsWindow.GetRandomColor()
          Bricks[Collumn][Row] = Shapes.AddRectangle(BrickWidth,BrickHight)
          Shapes.Animate(Bricks[Collumn][Row],(BrickWidth+35)*Row,(BrickHight+10)*Collumn,500)
        EndFor
      EndFor
    EndSub
    Sub onMouseMove
      PadX = GraphicsWindow.MouseX
      If PadX < PaddleWidth/2 Then
        PadX = PaddleWidth/2
      ElseIf  PadX > GameWidth - PaddleWidth/2 Then
        PadX =GameWidth - PaddleWidth/2
      EndIf
      PadPos = PadX - PaddleWidth/2
      Shapes.Move(Paddle,PadPos,GameHeight-PaddleHeight)
      If GameRunning = "False" Then
        Shapes.Move(Ball,(PadPos+PaddleWidth/2)-BallWidth/2,GameHeight-PaddleHeight-BallHeight)
        XPos = Shapes.GetLeft(Ball)
        YPos = Shapes.GetTop(Ball)
      EndIf
    EndSub
    Sub onClick
      XPos = Shapes.GetLeft(Ball)
      YPos = Shapes.GetTop(Ball)
      GameRunning = "True"
      While GameRunning
        XPos = XPos + deltaX
        YPos = YPos + deltaY
        If XPos > GameWidth - BallWidth Or XPos < 0 Then
          deltaX = -deltaX
        EndIf
        If YPos <= 0 Then
          deltaY = -deltaY
        EndIf
        PaddleX = Shapes.GetLeft(Paddle)
        If YPos = GameHeight-BallHeight-PaddleHeight And XPos > PaddleX And XPos < paddleX + PaddleWidth Then
          deltaY = -deltaY
        EndIf
        If YPos > GameHeight - BallHeight Then
          GameRunning = "False"
          deltaX = -0.08
          deltaY = -0.08
          Shapes.Move(Ball,(PadPos+PaddleWidth/2)-BallWidth/2,GameHeight-PaddleHeight-BallHeight)
          GraphicsWindow.Title = "You missed!"
          onMouseMove()
        EndIf
        Shapes.Move(Ball,XPos,YPos)
        'GraphicsWindow.SetPixel(XPos,YPos,"Red")
        CollisionCheck()
      EndWhile
    EndSub
    Sub CollisionCheck
      For Collumn = 1 To 5
        For Row = 1 To 8
          BrickTop = Shapes.GetTop(Bricks[Collumn][Row])
          BrickLeft = Shapes.GetLeft(Bricks[Collumn][Row])
          If XPos + BallWidth >= BrickLeft And XPos <= BrickLeft + BrickWidth And YPos < BrickTop + BrickHight And YPos + BallHeight > BrickTop Then
            deltaY = -deltaY
            Shapes.Remove(Bricks[Collumn][Row])
            Score = Score + 1
            GraphicsWindow.Title = Score * Clock.Second
            If Score = 40 Then
              Sound.PlayChime()
              deltaX = 0.09
              deltaY = -0.09
              Score = 0
              GameRunning = "False"
              'Remove all remaining shaped
              For Collumn = 1 To 5
                For Row = 1 To 8
                  Shapes.Remove(Bricks[Collumn][Row])
                EndFor
              EndFor
              DrawBricks()
              onMouseMove()
            EndIf
          EndIf
        EndFor
      EndFor
    EndSub


    Thirdly,

    The While.......EndWhile loop doesn't  working smoothly under a subroutine but it's working perfectly outside of subroutines

    GameWidth = 700
    GameHeight = 450
    BrickWidth = 40
    BrickHight = 20
    BallWidth = 15
    BallHeight = BallWidth
    PaddleWidth = 120
    PaddleHeight = 15
    GameRunning = "False"
    deltaX = 1
    deltaY = -1
    Score = 0
    GraphicsWindow.Width = GameWidth
    GraphicsWindow.Height = GameHeight
    GraphicsWindow.MouseMove = onMouseMove
    GraphicsWindow.MouseDown = onClick
    DrawBricks()
    CreateBallAndPaddle()
    Sub CreateBallAndPaddle
      Paddle = Shapes.AddRectangle(PaddleWidth,PaddleHeight)
      Shapes.Move(Paddle,GameWidth/2-PaddleWidth/2,GameHeight-PaddleHeight)
      Ball = Shapes.AddEllipse(BallWidth,BallHeight)
      Shapes.Move(Ball,GameWidth/2-BallWidth/2,GameHeight-PaddleHeight-BallHeight)
    EndSub
    Sub DrawBricks
      For Collumn = 1 To 5
        For Row = 1 To 8
          GraphicsWindow.BrushColor = GraphicsWindow.GetRandomColor()
          Bricks[Collumn][Row] = Shapes.AddRectangle(BrickWidth,BrickHight)
          Shapes.Animate(Bricks[Collumn][Row],(BrickWidth+35)*Row,(BrickHight+10)*Collumn,500)
        EndFor
      EndFor
    EndSub
    Sub onMouseMove
      PadX = GraphicsWindow.MouseX
      If PadX < PaddleWidth/2 Then
        PadX = PaddleWidth/2
      ElseIf  PadX > GameWidth - PaddleWidth/2 Then
        PadX =GameWidth - PaddleWidth/2
      EndIf
      PadPos = PadX - PaddleWidth/2
      Shapes.Move(Paddle,PadPos,GameHeight-PaddleHeight)
      If GameRunning = "False" Then
        Shapes.Move(Ball,(PadPos+PaddleWidth/2)-BallWidth/2,GameHeight-PaddleHeight-BallHeight)
        XPos = Shapes.GetLeft(Ball)
        YPos = Shapes.GetTop(Ball)
      EndIf
    EndSub
    Sub onClick
      XPos = Shapes.GetLeft(Ball)
      YPos = Shapes.GetTop(Ball)
      GameRunning = "True"
    EndSub
     While "True"
        XPos = XPos + deltaX
        YPos = YPos + deltaY
        If XPos > GameWidth - BallWidth Or XPos < 0 Then
          deltaX = -deltaX
        EndIf
        If YPos <= 0 Then
          deltaY = -deltaY
        EndIf
        PaddleX = Shapes.GetLeft(Paddle)
        If YPos = GameHeight-BallHeight-PaddleHeight And XPos > PaddleX And XPos < paddleX + PaddleWidth Then
          deltaY = -deltaY
        EndIf
        If YPos > GameHeight - BallHeight Then
          GameRunning = "False"
          deltaX = 1
          deltaY = -1
          Shapes.Move(Ball,(PadPos+PaddleWidth/2)-BallWidth/2,GameHeight-PaddleHeight-BallHeight)
          GraphicsWindow.Title = "You missed!"
          onMouseMove()
        EndIf
        Shapes.Move(Ball,XPos,YPos)
        'GraphicsWindow.SetPixel(XPos,YPos,"Red")
        CollisionCheck()
        Program.Delay(5)
      EndWhile
    Sub CollisionCheck
      For Collumn = 1 To 5
        For Row = 1 To 8
          BrickTop = Shapes.GetTop(Bricks[Collumn][Row])
          BrickLeft = Shapes.GetLeft(Bricks[Collumn][Row])
          If XPos + BallWidth >= BrickLeft And XPos <= BrickLeft + BrickWidth And YPos < BrickTop + BrickHight And YPos + BallHeight > BrickTop Then
            deltaY = -deltaY
            Shapes.Remove(Bricks[Collumn][Row])
            Score = Score + 1
            GraphicsWindow.Title = Score * Clock.Second
            If Score = 40 Then
              Sound.PlayChime()
              deltaX =1
              deltaY = -1
              Score = 0
              GameRunning = "False"
              'Remove all remaining shaped
              For Collumn = 1 To 5
                For Row = 1 To 8
                  Shapes.Remove(Bricks[Collumn][Row])
                EndFor
              EndFor
              DrawBricks()
              onMouseMove()
            EndIf
          EndIf
        EndFor
      EndFor
    EndSub

    Behnam Azizi could understand this problem more easily... Please give you feedback and suggestion...


    Merry Xmas!


    • Edited by 4mir '- Tuesday, January 1, 2013 2:56 PM
    Tuesday, January 1, 2013 2:54 PM

Answers

  • Added some textwindow debugging - can't see the issue (always score = num of blocks hit), import GPP417.

    You will have to explain more how to see it, otherwise I am only guessing.

    EDIT , now I see

    The top left has coordinates 0,0.  When a shape is removed, the top left of the shape returns 0 (no shape so what should it do?), hence a hit is registered when the ball is here.  Perhaps only check remaining shapes, or discard coords top left of brick when it is 0,0.

    • Edited by litdevModerator Tuesday, January 1, 2013 4:27 PM
    • Marked as answer by 4mir '- Thursday, January 3, 2013 7:53 AM
    Tuesday, January 1, 2013 4:22 PM
    Moderator

All replies

  • 1] For circle rectangle collisions see here or LDShapes.Overlap.

    2] Probably multiple scoring since you effectively hit the border several times - just reversing velocity (it is still outside the area).  Also set the position to be just inside or on the border.

    3] Don't do main game loop (or other long complex work) inside an event subroutine.  See many posts in this forum or here for whys.

    Cant repro all your issues - perhaps small test examples that show the issues clearly.

    Tuesday, January 1, 2013 3:42 PM
    Moderator
  • 1] For circle rectangle collisions see here or LDShapes.Overlap.

    2] Probably multiple scoring since you effectively hit the border several times - just reversing velocity (it is still outside the area).  Also set the position to be just inside or on the border.

    3] Don't do main game loop (or other long complex work) inside an event subroutine.  See many posts in this forum or here for whys.

    Cant repro all your issues - perhaps small test examples that show the issues clearly.

    You mean when ball hit the wall then the ball's X position and X velocity should be set to 0 ? Please Explain a bit more.

    If Ball Collide with Brick Then

    Score = Score + 1

    End If


    As in code above the Score variable has nothing to with velocity and collision with wall. but it still keep increasing whenever the ball hit exactly on the upper left corner.


    Merry Xmas!



    • Edited by 4mir '- Tuesday, January 1, 2013 4:05 PM
    Tuesday, January 1, 2013 4:03 PM
  • Added some textwindow debugging - can't see the issue (always score = num of blocks hit), import GPP417.

    You will have to explain more how to see it, otherwise I am only guessing.

    EDIT , now I see

    The top left has coordinates 0,0.  When a shape is removed, the top left of the shape returns 0 (no shape so what should it do?), hence a hit is registered when the ball is here.  Perhaps only check remaining shapes, or discard coords top left of brick when it is 0,0.

    • Edited by litdevModerator Tuesday, January 1, 2013 4:27 PM
    • Marked as answer by 4mir '- Thursday, January 3, 2013 7:53 AM
    Tuesday, January 1, 2013 4:22 PM
    Moderator
  • Thanks LitDev

    Here is my improved code bases on your suggestion 

    Import : JWJ395

    Now time to work on collision part and increasing difficulty by every level. 


    Merry Xmas!

    Thursday, January 3, 2013 7:53 AM
  • I recon the collision is ok (a complex collision detection might just slow things with no noticeable game improvement), perhaps think more about levels, a shorter bat perhaps or 2 balls, or some blocks that need multiple hits to clear, or flashing blocks that give extra points?
    Thursday, January 3, 2013 9:12 PM
    Moderator