Jump to content

Tracking Collisions Between Controls


_Kurt
 Share

Recommended Posts

Update2:

- New $i_OnlyCorners Parameter added to only check the controls corners

- _GUICtrlCheckCollision function added: easily check the collision between 2 moving controls!!

Update1:

- New $i_Pixel Parameter added -- Improves performance (50-90% faster)

- Fixed small bug

- Will first search corners of control and afterwards the parameter

Purpose of project:

To create a method to track collisions of a control without checking each obstacle and using lots of memory. It's great because it can check as many control obstacles as you desire and it won't slow anything down.

So, instead of searching your whole GUI, like below

Posted Image

We're only searching the collider control that collides with the obstacle controls, like this:

Posted Image

Limitations:

-Does not work with moving obstacle controls yet (moving colliders accepted) - See _GUICtrlCheckCollision

-Will not return anything if the Collider is out of the boundaries of width/height of GUI

How it works: (Skip if not interested)

I apologize for my brutal attempts to explain this, I can clarify anything if someone has a problem. What it does is creates a (fairly large) array relative to the width/height of your GUI. For example if your GUI is sized at (300, 200), it will create an array $Array[300][200]. Next, from _GUICtrlSetCollision, we take a controls X,Y,W,H coordinates and set values in $Array according to them. For example, if we had a control at (20, 50, 100, 20) -- X,Y,Width,Height -- we would set the value from $Array[20][50] to $Array[120][70] to that control ID, since X + Width = 120, and Y + Height = 70. Once our array is set, we simply call the function _GUICtrlGetCollision in the While..WEnd loop, which retrieves the Collider's position and compares it to the arrays value, so if our collider were at (30,50) which is in contact with our control, we would see that $Array[30][50] <> "" meaning that it has collided with a control.

Bugs/Errors:

-None Yet <_<

The Functions:

;==================================================================================================
; Function Name:         _GUICtrlCheckCollision($GUI, $Control1, $Control2)
;
; Parameters:            $GUI:        The GUI Handle (returned from GUICreate)
;                        $Control1:   ControlID of your first control (order doesn't matter)
;                        $Control2:   ControlID of your second control (order doesn't matter)
;
; Description:           Checks for collision between two controls
;
; Requirement(s):        None
;
; Return Value(s)
;                        On Success:  Returns 1 (the controls have collided)
;                        On Failure:  Returns 0
;
; Author(s):             _Kurt
;==================================================================================================

Func _GUICtrlCheckCollision($GUI, $Control1, $Control2)
    $1pos = ControlGetPos($GUI, "", $Control1)
    $2pos = ControlGetPos($GUI, "", $Control2)
    $X1 = $1pos[0]
    $Y1 = $1pos[1]
    $W1 = $1pos[2]+$1pos[0]
    $H1 = $1pos[3]+$1pos[1]
    $X2 = $2pos[0]
    $Y2 = $2pos[1]
    $W2 = $2pos[2]+$2pos[0]
    $H2 = $2pos[3]+$2pos[1]
    If $X1 >= $X2 AND $X1 <= $W2 Then
        If $Y1 >= $Y2 AND $Y1 <= $H2 Then Return 1
        If $H1 >= $Y2 AND $H1 <= $H2 Then Return 1
    EndIf
    If $W1 >= $X2 AND $W1 <= $W2 Then
        If $H1 >= $Y2 AND $H1 <= $H2 Then Return 1
        If $Y1 >= $Y2 AND $Y1 <= $H2 Then Return 1
    EndIf
    If $X1 <= $X2 AND $Y1 <= $Y2 Then
        If $W1 >= $X2 AND $H1 >= $Y2 Then Return 1
        If $W1 >= $W2 AND $H1 >= $H2 Then Return 1
    EndIf
    If $X1 >= $X2 AND $X1 <= $W2 AND $Y1 <= $Y2 Then
        If $W1 >= $W2 AND $H1 >= $H2 Then Return 1
    EndIf
    If $Y1 >= $Y2 AND $X1 <= $X2 AND $Y1 <= $H2 Then
        If $H1 >= $H2 AND $W1 >= $W2 Then Return 1
    EndIf
    Return 0
EndFunc

;==================================================================================================
; Function Name:         _GUICtrlGetCollision($GUI, $i_Collider [, $i_Pixel = 2 [, $i_Perimeter = 1 [, $i_OnlyCorners = 0]]])
;
; Parameters:            $GUI:   The GUI Handle (returned from GUICreate)
;                        $i_Collider:   ControlID of your desired Collider
;                        $i_Pixel:     (optional) The number of pixels to skip, default is 2, meaning that it will search
;                                                 every other pixel within the $Collider's perimeter. The higher the number,
;                                                 the faster the function will go.
;                        $i_Perimeter: (optional) 0 = Check each pixel of the whole control
;                                                 1 = (default & recommended) Check the perimeter of the control
;                        $i_OnlyCorners: (optional) 0 = (default) Check the whole perimeter of the control
;                                                   1 = Only check the corners of the control (MUCH faster)
;
; Description:           Checks for any controls that has collided with a desired control
;
; Requirement(s):        Must use _GUICtrlSetCollision on your desired Obstacles
;
; Return Value(s)
;                        On Success:  Returns the corresponding controlID that has collided with $Collider
;                        On Failure:  Returns -1 , No control is colliding with the $Collider
;                                     Returns -2 , _GUICtrlSetCollision was not previously called,
;                                                  so the function has nothing to search for.
;
; Author(s):             _Kurt
;==================================================================================================

Func _GUICtrlGetCollision($GUI, $i_Collider, $i_Pixel = 2, $i_Perimeter = 1, $i_OnlyCorners = 0)
    If NOT IsArray($Data) Then Return -2
    Local $Array = 0
    $ColliderPos = ControlGetPos($GUI, "", $i_Collider)
    $X = $ColliderPos[0]
    $Y = $ColliderPos[1]
    $W = $ColliderPos[2]
    $H = $ColliderPos[3]
    $1Dim = UBound($Data)
    $2Dim = UBound($Data,2)
    If $W > $1Dim-1 Then $W = $1Dim-1
    If $H > $2Dim-1 Then $H = $2Dim-1
    If $Y > $2Dim-1 Then $Y = $2Dim-1
    If $X < 0 Then $X = 0
    If GUICtrlGetHandle($Data[$X][$Y]) <> 0 AND $i_Collider <> $Data[$X][$Y] Then Return $Data[$X][$Y]
    If GUICtrlGetHandle($Data[$X+$W][$Y]) <> 0 AND $i_Collider <> $Data[$X+$W][$Y] Then Return $Data[$X+$W][$Y]
    If GUICtrlGetHandle($Data[$X][$Y+$H]) <> 0 AND $i_Collider <> $Data[$X][$Y+$H] Then Return $Data[$X][$Y+$H]
    If GUICtrlGetHandle($Data[$X+$W][$Y+$H]) <> 0 AND $i_Collider <> $Data[$X+$W][$Y+$H] Then Return $Data[$X+$W][$Y+$H]
    If $i_OnlyCorners = 1 Then Return -1
    If $i_Perimeter = 0 Then
        For $z = 0 To $W Step $i_Pixel
            If $Data[$X+$z][$Y] <> "" Then
                If GUICtrlGetHandle($Data[$X+$z][$Y]) <> 0 AND $i_Collider <> $Data[$X+$z][$Y] Then
                    Return $Data[$X+$z][$Y]
                Else
                    $Data[$X+$z][$Y] = ""
                EndIf
            EndIf
            If $Data[$X+$z][$Y+$H] <> "" Then
                If GUICtrlGetHandle($Data[$X+$z][$Y+$H]) <> 0 AND $i_Collider <> $Data[$X+$z][$Y+$H] Then
                    Return $Data[$X+$z][$Y+$H]
                Else
                    $Data[$X+$z][$Y+$H] = ""
                EndIf
            EndIf
        Next
        For $a = 0 To $H Step $i_Pixel
            If $Data[$X][$Y+$a] <> "" Then
                If GUICtrlGetHandle($Data[$X][$Y+$a]) <> 0 AND $i_Collider <> $Data[$X][$Y+$a] Then
                    Return $Data[$X][$Y+$a]
                Else
                    $Data[$X][$Y+$a] = ""
                EndIf
            EndIf
            If $Data[$X+$W][$Y] <> "" Then
                If GUICtrlGetHandle($Data[$X][$Y+$a]) <> 0 AND $i_Collider <> $Data[$X][$Y+$a] Then
                    Return $Data[$X][$Y+$a]
                Else
                    $Data[$X][$Y+$a] = ""
                EndIf
            EndIf
        Next
    Else
        For $z = 0 To $W Step $i_Pixel
            For $a = 0 To $H Step $i_Pixel
                If $Data[$X+$z][$Y+$a] <> "" Then
                    If GUICtrlGetHandle($Data[$X+$z][$Y+$a]) <> 0 AND $i_Collider <> $Data[$X+$z][$Y+$a] Then
                        Return $Data[$X+$z][$Y+$a]
                    Else
                        $Data[$X+$z][$Y+$a] = ""
                    EndIf
                EndIf
            Next
        Next
    EndIf
    Return -1
EndFunc

;==================================================================================================
; Function Name:         _GUICtrlSetCollision($GUI, $Ctrl)
;
; Parameters:            $GUI:   The GUI Handle (returned from GUICreate)
;                        $Ctrl:  The ControlID of the desired obstacle
;
; Description:           Sets data that will be used in _GUICtrlGetCollision
;
; Requirement(s):        None
;
; Return Value(s)
;                        On Success:  Returns  1
;                        On Failure:  Returns -1 , your GUI handle or ControlID is not valid
;
; Author(s):             _Kurt
;==================================================================================================

Func _GUICtrlSetCollision($GUIhandle, $Ctrl)
    If WinGetHandle($GUIhandle) = "" Or GUICtrlGetHandle($Ctrl) = 0 Then Return -1
    If IsDeclared("Data") = 0 Then
        $GUISize = WinGetPos($GUIhandle)
        Global $Data[$GUISize[2]+1][$GUISize[3]+1]
    EndIf
    If NOT IsArray($Data) Then
        $GUISize = WinGetPos($GUIhandle)
        Global $Data[$GUISize[2]+1][$GUISize[3]+1]
    EndIf
    $CTRLSize = ControlGetPos($GUIhandle, "", $Ctrl)
    For $i = 0 To $CTRLSize[2]
        For $x = 0 To $CTRLSize[3]
            $Data[$CTRLSize[0]+$i][$CTRLSize[1]+$x] = $Ctrl
        Next
    Next
    Return 1
EndFuncoÝ÷ Ø ÞÅ©©æ®¶­sd÷D¶W6WBgV÷C·´DõtçÒgV÷C²ÂgV÷C´Ö÷fTF÷vâgV÷C²´ÆÂöbFW6R÷F¶WV@¤÷D¶W6WBgV÷C·µUÒgV÷C²ÂgV÷C´Ö÷fUWgV÷C²¶gVæ7Föç2&R6×Ǥ÷D¶W6WBgV÷C·´ÄTeGÒgV÷C²ÂgV÷C´Ö÷fTÆVgBgV÷C²¶f÷"FW7FærW'÷6W0¤÷D¶W6WBgV÷C·µ$tGÒgV÷C²ÂgV÷C´Ö÷fU&vBgV÷C²¤vÆö&Âb33c´FF¢b33c¶væDuTÒuT7&VFRgV÷C´6öÆÆ6öâFW7BgV÷C²ÂCÂC¢b33c´×6öÆÆFW"ÒuT7G&Ä7&VFT'WGFöâgV÷C²gV÷C²ÂÂÂ3Â3²fÇC²ÒÒ÷W"uT6öçG&öÂFB6â6öÆÆFRvF÷FW"6öçG&öÇ0 ¢b33c´×Æ&VÂÒwV7G&Ä7&VFTÆ&VÂgV÷C³ÓÒgV÷C²ÂsÂ3Â#Â#¤uT7G&Å6WD&´6öÆ÷"ÓÃdddddb ¢b33c´×'WGFöâÒuT7G&Ä7&VFT'WGFöâgV÷C³ÓÒgV÷C²ÂSÂ#Â#Â#¤uT7G&Å6WD&´6öÆ÷"ÓÃdddddb¥ôuT7G&Å6WD6öÆÆ6öâb33c¶væDuTÂb33c´×'WGFöâ²b333²b333´Õõ%DåBb333²b333²vR6WBF2âGfæ6P ¢b33c´×÷FW$'WGFöâÒuT7G&Ä7&VFT'WGFöâgV÷C³ÓÒgV÷C²ÂÂ3´æ÷FS¢æ÷FærvÆÂVâöæ6R6öÆÆFVBvFF2'WGFöà¤uT7G&Å6WD&´6öÆ÷"ÓÃdddddb·6æ6RôuT7G&Å6WD6öÆÆ6öâ2æ÷B&VVâ6WBvFF26öçG&öÄ@¤uT7G&Ä7&VFTÆ&VÂgV÷CµF2'WGFöâvÆÂæ÷BfRçVffV7B6æ6RôuT7G&Å6WD6öÆÆ6öâv2æ÷B6WBFòF26öçG&öÄBgV÷C²ÂÂ3#RÂcÂS¤uT6WE7FFR¥vÆR 6ÆVW bôuT7G&Ä6V6´6öÆÆ6öâb33c¶væDuTÂb33c´×Æ&VÂÂb33c´×6öÆÆFW"FVà FööÅFgV÷C²b33c´×Æ&VÂæBb33c´×6öÆÆFW"fR6öÆÆFVBgV÷C² VÇ6P FööÅFgV÷C²gV÷C² VæD` ³³²6V6²6öÆÆ6öç2³³° ²b33c´6V6´6öÆÆ6öâÒÓ b33c´6V6´6öÆÆ6öâÒôuT7G&ÄvWD6öÆÆ6öâb33c¶væDuTÂb33c´×6öÆÆFW"ÂR´fæBå6öçG&öÂg&öÒôuT7G&Å6WD6öÆÆ6öà °·FB26öÆÆFVBvFb33c´×6öÆÆFW  bb33c´6V6´6öÆÆ6öâfÇC²fwC²ÓFVâ´bFR6V6²FöW6âb33·BfÂÖVæærB&WGW&æVB6öçG&öÄB bb33c´6V6´6öÆÆ6öâÒb33c´×'WGFöâFVâ×6t&÷ÂgV÷C²gV÷C²ÂgV÷C²b33c´×6öÆÆFW"26öÆÆFVBvFb33c´×'WGFöâgV÷C² uT7G&ÄFVÆWFRb33c´6V6´6öÆÆ6öâ´FVÆWFRFR6öçG&öÀ VæD` ³³²uTWfVçG2³³° b33c¶×6rÒuTvWD×6r bb33c¶×6rÒÓ2FVâW@¥tVæ@ £³³²Ö÷færgVæ7Föç2³³° ¤gVæ2Ö÷fTF÷vâ b33c·÷2Ò6öçG&öÄvWE÷2b33c¶væDuTÂgV÷C²gV÷C²Âb33c´×6öÆÆFW" uT7G&Å6WE÷2b33c´×6öÆÆFW"Âb33c·÷5³ÒÂb33c·÷5³Ò³b¤VæDgVæ0 ¤gVæ2Ö÷fUW b33c·÷2Ò6öçG&öÄvWE÷2b33c¶væDuTÂgV÷C²gV÷C²Âb33c´×6öÆÆFW" uT7G&Å6WE÷2b33c´×6öÆÆFW"Âb33c·÷5³ÒÂb33c·÷5³ÒÓb¤VæDgVæ0 ¤gVæ2Ö÷fTÆVgB b33c·÷2Ò6öçG&öÄvWE÷2b33c¶væDuTÂgV÷C²gV÷C²Âb33c´×6öÆÆFW" uT7G&Å6WE÷2b33c´×6öÆÆFW"Âb33c·÷5³ÒÓbÂb33c·÷5³Ò¤VæDgVæ0 ¤gVæ2Ö÷fU&vB b33c·÷2Ò6öçG&öÄvWE÷2b33c¶væDuTÂgV÷C²gV÷C²Âb33c´×6öÆÆFW" uT7G&Å6WE÷2b33c´×6öÆÆFW"Âb33c·÷5³Ò³bÂb33c·÷5³Ò¤VæDgVæ

I'd like to hear some feedback on what people think of the idea, Thanks.

Kurt

Edited by _Kurt

Awaiting Diablo III..

Link to comment
Share on other sites

This is great! My _IsOn function isn't that good, so I've been looking for something like this for a while.

Edited by BananaFredSoft
Link to comment
Share on other sites

Thanks, and the best thing is: No matter how many obstacle controls you have, it won't slow it down at all since we're searching the array relative to the $Collider's position.

Kurt

Awaiting Diablo III..

Link to comment
Share on other sites

Im created that example but if $Block collide with $Left or $Right not work true and i cant understand what im did wrong :

HotKeySet("{LEFT}", "MoveLeft")
HotKeySet("{RIGHT}", "MoveRight")

Global $Data, $Left = True, $Right = True

$Form = GUICreate("Test", 633, 449, 193, 115)
$Block = GUICtrlCreateButton("", 208, 400, 193, 25, 0)
$Bottom = GUICtrlCreateButton("", 8, 432, 617, 9, 0)
_GUICtrlSetCollision($Form, $Bottom)
$Left = GUICtrlCreateButton("", 8, 16, 9, 417, 0)
_GUICtrlSetCollision($Form, $Left)
$Top = GUICtrlCreateButton("", 8, 8, 617, 9, 0)
_GUICtrlSetCollision($Form, $Top)
$Right = GUICtrlCreateButton("", 616, 16, 9, 417, 0)
_GUICtrlSetCollision($Form, $Right)
$Ball = GUICtrlCreateRadio("<--- Ball", 304, 48, 57, 17)
_GUICtrlSetCollision($Form, $Ball)

GUISetState()

While 1
    $msg = GUIGetMsg()
    If $msg = -3 Then Exit
WEnd
    
Func CheckCollision()
    Sleep(10)
    $CheckCollisionBall = _GUICtrlGetCollision($Form, $Ball)
    If $CheckCollisionBall <> -1 Then
    ;If $CheckCollisionBall = $Top Then Down()
       ;If $CheckCollisionBall = $Block Then Up()
       ;If $CheckCollisionBall = $Bottom Then Lose()
    EndIf
    $CheckCollisionBlock = _GUICtrlGetCollision($Form, $Block)
    If $CheckCollisionBlock <> -1 Then
        If $CheckCollisionBall = $Left Then $Left = False
        If $CheckCollisionBall = $Right Then $Right = False
    EndIf
EndFunc

Func MoveLeft()
    CheckCollision()
    If $Left = True Then
        $xPos = ControlGetPos($Form, "", $Block)
        GUICtrlSetPos($Block, $xPos[0]-10, $xPos[1])
        $Right = True
    Else
        MsgBox("","","You can't go left!")
        MoveRight()
    EndIf
EndFunc

Func MoveRight()
    CheckCollision()
    If $Right = True Then
        $xPos = ControlGetPos($Form, "", $Block)
        GUICtrlSetPos($Block, $xPos[0]+10, $xPos[1])
        $Left = True
    Else
        MsgBox("","","You can't go right!")
        MoveLeft()
    EndIf
EndFunc
Link to comment
Share on other sites

Note: These functions will not work with moving obstacles - See _GUICtrlCheckCollision

Im created that example but if $Block collide with $Left or $Right not work true and i cant understand what im did wrong :

You didn't call the function wrong, but instead your MoveLeft/MoveRight functions weren't correct, I fixed em up for you:

Func CheckCollision()
    $CheckCollisionBlock = _GUICtrlGetCollision($Form, $Block)
    If $CheckCollisionBlock <> -1 Then
        If $CheckCollisionBlock = $Left Then $Left = False
        If $CheckCollisionBlock = $Right Then $Right = False
    EndIf
EndFunc

Func MoveLeft()
    CheckCollision()
    $xPos = ControlGetPos($Form, "", $Block)
    If $Left = True Then
        GUICtrlSetPos($Block, $xPos[0]-10, $xPos[1])
    Else
        GUICtrlSetPos($Block, $xPos[0]+10, $xPos[1])
        MsgBox("","","You can't go left!")
        $Left = True
    EndIf
EndFunc

Func MoveRight()
    CheckCollision()
    $xPos = ControlGetPos($Form, "", $Block)
    If $Right = True Then
        GUICtrlSetPos($Block, $xPos[0]+10, $xPos[1])
    Else
        GUICtrlSetPos($Block, $xPos[0]-10, $xPos[1])
        MsgBox("","","You can't go right!")
        $Right = True
    EndIf
EndFunc

I realized that calling CheckCollision() slows down the movement a lot, I will be working on a new feature to make this function much faster. I will post a solution within an hour, hold tight till then. Thanks for a second example, and post any other problems you have.

Kurt

EDIT: Just realized, in your code, you declared $Left & $Right for a button and a True/False string, that was your problem too!

Edited by _Kurt

Awaiting Diablo III..

Link to comment
Share on other sites

  • 3 weeks later...

Updated again for those who care :P

Added new function: _GUICtrlCheckCollision - Will check the collision between 2 moving controls

I will be coming up with a really cool game I created using these functions.

Kurt

Awaiting Diablo III..

Link to comment
Share on other sites

  • 2 weeks later...

I get an error:

Array variable subscript badly formatted.:

$Data[$CTRLSize[0]+$i][$CTRLSize[1]+$x] = $Ctrl

$Data[^ ERROR

:(

Any help?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...