Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Rectangle Collisions Demo using SAT
#1
Hi all, 

I love this kinda stuff so whilst im now onto incorporating circles and polygons into this, for now heres rectangles! 

SAT btw is separated axis theory, basically if you can draw a line between two things then they arent colliding, if you cant they are...its faster acurate and easily expandable to fit almost complex shapes....

This demo runs as is, needs no files or anyting for once! 

Code: (Select All)
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
'// Unseen GDK 3 -  Collisions Dev - Rects Only For now Just To Demo \\
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
RANDOMIZE TIMER
'// REM $INCLUDE:'UnseenGDK2\GDK2.bi'

CONST GDK_Vector_Size = 8
CONST GDK_TRUE = -1, GDK_FALSE = 0
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
'///////////////////////////////////// System Initialisation ////////////////////////////////////////////////
'/GDK2_System_New "UnseenGDK2\Projects\", 800, 600, GDK_FALSE, "GDK Rectangle Demo"
SCREEN _NEWIMAGE(800, 600, 32)
_SCREENMOVE 0, 0
_DELAY 1

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////

CONST MAX_BOXES = 10

CONST FLASH_TIME = 0.5
DIM SHARED Boxes(MAX_BOXES) AS GDK_Box

DIM SHARED GT#, LastGT#
GT# = TIMER(.001): LastGT# = GT#
InitBoxes


'//////////////////////////////////////// Main Loop ////////////////////////////////////////////////////////////
DO

  GT# = TIMER(.001)
  dt = GT# - LastGT#
  UpdateBoxes dt

  CLS

  FOR I = 0 TO MAX_BOXES - 1
    IF Boxes(I).FlashTimer > 0 THEN
      GDK_DrawBox Boxes(I), _RGB32(255, 255, 0)
    ELSE
      GDK_DrawBox Boxes(I), Boxes(I).pColor
    END IF
  NEXT I


  _DISPLAY
  LastGT# = GT#
LOOP UNTIL INKEY$ = CHR$(27)

SYSTEM

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
'REM $INCLUDE:'UnseenGDK2\GDK2.bm'
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////

TYPE GDK_Vector
  X AS SINGLE
  Y AS SINGLE
END TYPE

'The new GDK_Box type for a rotated rectangle, without pre-calculated corners
TYPE GDK_Box
  Position AS GDK_Vector
  WidthHeight AS GDK_Vector
  Rotation AS SINGLE
  RotationPointOffset AS GDK_Vector
  Rotation_Speed AS SINGLE ' New: For rotational speed
  Velocity AS GDK_Vector ' Movement vector for this box
  pColor AS LONG ' Color for drawing
  IsColliding AS INTEGER ' Flag to indicate collision state
  FlashTimer AS SINGLE ' Timer for collision flash
END TYPE


'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Draws a box using its calculated corner vertices.
SUB GDK_DrawBox (Box AS GDK_Box, bcolor AS LONG)
  DIM Corners(3) AS GDK_Vector
  CALL GDK_GetBoxCorners(Box, Corners())
  LINE (Corners(0).X, Corners(0).Y)-(Corners(1).X, Corners(1).Y), bcolor
  LINE (Corners(1).X, Corners(1).Y)-(Corners(2).X, Corners(2).Y), bcolor
  LINE (Corners(2).X, Corners(2).Y)-(Corners(3).X, Corners(3).Y), bcolor
  LINE (Corners(3).X, Corners(3).Y)-(Corners(0).X, Corners(0).Y), bcolor
END SUB

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////

' Rotates a point around a pivot point by a given angle (in radians)
SUB GDK_RotatePoint (Result AS GDK_Vector, p AS GDK_Vector, pivot AS GDK_Vector, angle AS SINGLE)
  DIM temp_x AS SINGLE, temp_y AS SINGLE
  temp_x = p.X - pivot.X
  temp_y = p.Y - pivot.Y
  Result.X = temp_x * COS(angle) - temp_y * SIN(angle)
  Result.Y = temp_x * SIN(angle) + temp_y * COS(angle)
  Result.X = Result.X + pivot.X
  Result.Y = Result.Y + pivot.Y
END SUB

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Calculates the corner vertices for a given box
SUB GDK_GetBoxCorners (Box AS GDK_Box, Corners() AS GDK_Vector)
  DIM HalfW AS SINGLE, HalfH AS SINGLE
  DIM UnrotatedCorner AS GDK_Vector
  DIM Center AS GDK_Vector

  HalfW = Box.WidthHeight.X / 2
  HalfH = Box.WidthHeight.Y / 2
  Center = Box.Position

  UnrotatedCorner.X = Center.X - HalfW
  UnrotatedCorner.Y = Center.Y - HalfH
  CALL GDK_RotatePoint(Corners(0), UnrotatedCorner, Center, Box.Rotation)

  UnrotatedCorner.X = Center.X + HalfW
  UnrotatedCorner.Y = Center.Y - HalfH
  CALL GDK_RotatePoint(Corners(1), UnrotatedCorner, Center, Box.Rotation)

  UnrotatedCorner.X = Center.X + HalfW
  UnrotatedCorner.Y = Center.Y + HalfH
  CALL GDK_RotatePoint(Corners(2), UnrotatedCorner, Center, Box.Rotation)

  UnrotatedCorner.X = Center.X - HalfW
  UnrotatedCorner.Y = Center.Y + HalfH
  CALL GDK_RotatePoint(Corners(3), UnrotatedCorner, Center, Box.Rotation)
END SUB

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////

' Checks for overlap of projections on a given axis
FUNCTION GDK_Overlap_On_Axis (Corners1() AS GDK_Vector, Corners2() AS GDK_Vector, Axis AS GDK_Vector)
  DIM Min1 AS SINGLE, Max1 AS SINGLE, Min2 AS SINGLE, Max2 AS SINGLE
  DIM Projection AS SINGLE, I AS LONG

  Min1 = 9999999!: Max1 = -9999999!
  Min2 = 9999999!: Max2 = -9999999!

  FOR I = 0 TO 3
    Projection = Corners1(I).X * Axis.X + Corners1(I).Y * Axis.Y
    IF Projection < Min1 THEN Min1 = Projection
    IF Projection > Max1 THEN Max1 = Projection
  NEXT I

  FOR I = 0 TO 3
    Projection = Corners2(I).X * Axis.X + Corners2(I).Y * Axis.Y
    IF Projection < Min2 THEN Min2 = Projection
    IF Projection > Max2 THEN Max2 = Projection
  NEXT I

  IF Max1 >= Min2 AND Max2 >= Min1 THEN GDK_Overlap_On_Axis = -1 ELSE GDK_Overlap_On_Axis = 0
END FUNCTION

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////

' Checks for intersection using SAT
FUNCTION GDK_Box_Intersect (Box1 AS GDK_Box, Box2 AS GDK_Box)
  DIM Box1_Corners(3) AS GDK_Vector, Box2_Corners(3) AS GDK_Vector
  DIM Axis(3) AS GDK_Vector, length AS SINGLE, I AS LONG

  CALL GDK_GetBoxCorners(Box1, Box1_Corners())
  CALL GDK_GetBoxCorners(Box2, Box2_Corners())

  Axis(0).X = -(Box1_Corners(1).Y - Box1_Corners(0).Y): Axis(0).Y = Box1_Corners(1).X - Box1_Corners(0).X
  Axis(1).X = -(Box1_Corners(0).Y - Box1_Corners(3).Y): Axis(1).Y = Box1_Corners(0).X - Box1_Corners(3).X

  FOR I = 0 TO 1
    length = SQR(Axis(I).X * Axis(I).X + Axis(I).Y * Axis(I).Y)
    IF length > 0 THEN Axis(I).X = Axis(I).X / length: Axis(I).Y = Axis(I).Y / length
    IF NOT GDK_Overlap_On_Axis(Box1_Corners(), Box2_Corners(), Axis(I)) THEN GDK_Box_Intersect = 0: EXIT FUNCTION
  NEXT I

  Axis(2).X = -(Box2_Corners(1).Y - Box2_Corners(0).Y): Axis(2).Y = Box2_Corners(1).X - Box2_Corners(0).X
  Axis(3).X = -(Box2_Corners(0).Y - Box2_Corners(3).Y): Axis(3).Y = Box2_Corners(0).X - Box2_Corners(3).X

  FOR I = 2 TO 3
    length = SQR(Axis(I).X * Axis(I).X + Axis(I).Y * Axis(I).Y)
    IF length > 0 THEN Axis(I).X = Axis(I).X / length: Axis(I).Y = Axis(I).Y / length
    IF NOT GDK_Overlap_On_Axis(Box1_Corners(), Box2_Corners(), Axis(I)) THEN GDK_Box_Intersect = 0: EXIT FUNCTION
  NEXT I

  GDK_Box_Intersect = -1
END FUNCTION


'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
'// DEMO For Simple Box collision
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////

' --- Main Logic ---
SUB InitBoxes
  DIM I AS LONG, W AS SINGLE, H AS SINGLE, R AS SINGLE, S AS SINGLE
  FOR I = 0 TO MAX_BOXES - 1
    W = 10 + RND * 50
    H = 10 + RND * 50
    R = RND * 2 * _PI
    S = 10 + RND * 50
    Boxes(I).Position.X = RND * 800
    Boxes(I).Position.Y = RND * 600 '_HEIGHT
    Boxes(I).WidthHeight.X = W
    Boxes(I).WidthHeight.Y = H
    Boxes(I).Rotation = R
    Boxes(I).Rotation_Speed = (RND * .3)
    Boxes(I).RotationPointOffset.X = W / 2: Boxes(I).RotationPointOffset.Y = H / 2
    Boxes(I).Velocity.X = (RND * 5) * S
    Boxes(I).Velocity.Y = (RND * 5) * S
    Boxes(I).pColor = _RGB32(RND * 255, RND * 255, RND * 255)
    Boxes(I).IsColliding = GDK_FALSE
    Boxes(I).FlashTimer = 0
  NEXT I
END SUB

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
SUB UpdateBoxes (dt AS SINGLE)
  DIM I AS LONG, J AS LONG
  DIM Box_Hit AS INTEGER
  ' Screen wrapping
  DIM BoxLeft AS SINGLE, BoxRight AS SINGLE, BoxTop AS SINGLE, BoxBottom AS SINGLE
  DIM tempX AS SINGLE, tempY AS SINGLE

  FOR I = 0 TO MAX_BOXES - 1
    ' Update position and rotation
    Boxes(I).Position.X = Boxes(I).Position.X + Boxes(I).Velocity.X * dt
    Boxes(I).Position.Y = Boxes(I).Position.Y + Boxes(I).Velocity.Y * dt
    Boxes(I).Rotation = Boxes(I).Rotation + Boxes(I).Rotation_Speed * dt

    BoxLeft = Boxes(I).Position.X - Boxes(I).WidthHeight.X / 2
    BoxRight = Boxes(I).Position.X + Boxes(I).WidthHeight.X / 2
    BoxTop = Boxes(I).Position.Y - Boxes(I).WidthHeight.Y / 2
    BoxBottom = Boxes(I).Position.Y + Boxes(I).WidthHeight.Y / 2

    IF BoxRight < 0 THEN Boxes(I).Position.X = _WIDTH + Boxes(I).WidthHeight.X / 2
    IF BoxLeft > _WIDTH THEN Boxes(I).Position.X = 0 - Boxes(I).WidthHeight.X / 2
    IF BoxBottom < 0 THEN Boxes(I).Position.Y = _HEIGHT + Boxes(I).WidthHeight.Y / 2
    IF BoxTop > _HEIGHT THEN Boxes(I).Position.Y = 0 - Boxes(I).WidthHeight.Y / 2

    ' Check for collision with other boxes
    Box_Hit = GDK_FALSE
    FOR J = I + 1 TO MAX_BOXES - 1
      IF GDK_Box_Intersect(Boxes(I), Boxes(J)) THEN
        Box_Hit = GDK_TRUE
        ' Simple bounce logic
        tempX = Boxes(I).Velocity.X: tempY = Boxes(I).Velocity.Y
        Boxes(I).Velocity.X = Boxes(J).Velocity.X: Boxes(I).Velocity.Y = Boxes(J).Velocity.Y
        Boxes(J).Velocity.X = tempX: Boxes(J).Velocity.Y = tempY
        Boxes(J).FlashTimer = FLASH_TIME
      END IF
    NEXT J

    ' Manage flash timer
    IF Box_Hit THEN
      Boxes(I).FlashTimer = FLASH_TIME
    ELSEIF Boxes(I).FlashTimer > 0 THEN
      Boxes(I).FlashTimer = Boxes(I).FlashTimer - dt
    END IF
  NEXT I
END SUB

'///////////////////////////////////////////////////////////////////////////////////////////////////////////////
SUB GDK_ClosestPointOnBox (Result AS GDK_Vector, Box AS GDK_Box, vPoint AS GDK_Vector)
  DIM Box_Corners(3) AS GDK_Vector
  CALL GDK_GetBoxCorners(Box, Box_Corners())

  DIM Closest AS GDK_Vector
  DIM I AS LONG, J AS LONG
  DIM MinDistanceSquared AS SINGLE, CurrentDistanceSquared AS SINGLE

  MinDistanceSquared = 9999999!

  FOR I = 0 TO 3
    J = (I + 1) MOD 4
    DIM SegmentStart AS GDK_Vector, SegmentEnd AS GDK_Vector
    SegmentStart = Box_Corners(I)
    SegmentEnd = Box_Corners(J)

    DIM dx AS SINGLE, dy AS SINGLE
    dx = SegmentEnd.X - SegmentStart.X
    dy = SegmentEnd.Y - SegmentStart.Y

    DIM t AS SINGLE
    t = ((vPoint.X - SegmentStart.X) * dx + (vPoint.Y - SegmentStart.Y) * dy) / (dx * dx + dy * dy)

    IF t < 0 THEN t = 0
    IF t > 1 THEN t = 1

    DIM ClosestPointOnSegment AS GDK_Vector
    ClosestPointOnSegment.X = SegmentStart.X + t * dx
    ClosestPointOnSegment.Y = SegmentStart.Y + t * dy

    CurrentDistanceSquared = (vPoint.X - ClosestPointOnSegment.X) ^ 2 + (vPoint.Y - ClosestPointOnSegment.Y) ^ 2

    IF CurrentDistanceSquared < MinDistanceSquared THEN
      MinDistanceSquared = CurrentDistanceSquared
      Closest = ClosestPointOnSegment
    END IF
  NEXT I

  Result = Closest
END SUB

Unseen
Reply


Messages In This Thread
Rectangle Collisions Demo using SAT - by Unseen Machine - 09-06-2025, 08:00 AM
RE: Rectangle Collisions Demo using SAT - by Pete - 09-06-2025, 03:52 PM
RE: Rectangle Collisions Demo using SAT - by Pete - 09-08-2025, 07:12 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  WinAPI Mouse Demo Pete 0 184 12-20-2025, 06:40 PM
Last Post: Pete
  Hyperlink Demo in SCREEN 0 Pete 2 366 11-02-2025, 07:13 PM
Last Post: madscijr
  Space Invaders knock off to show simple collisions Unseen Machine 10 1,100 09-09-2025, 07:23 PM
Last Post: Pete
  Email Demo (from 6/6/2014) SMcNeill 0 599 11-13-2023, 06:39 AM
Last Post: SMcNeill
  Qix Demo james2464 4 1,097 11-23-2022, 07:01 PM
Last Post: johnno56

Forum Jump:


Users browsing this thread: 1 Guest(s)