09-27-2025, 06:34 AM
(This post was last modified: 09-27-2025, 06:50 AM by madscijr.
Edit Reason: added some instructions
)
Here's my collision code - see attached "MultiCustomFontsAndCollisions131.bas" for the full working program.
To run around and shoot things, use the cursor keys and the right Ctrl key. Also it breaks if you choose both the max robots AND max big robots. If you want to see a lot of things moving and colliding, choose 127 robots and 3 or 4 big robots.
First I'll explain that the graphics / sprites / etc. are all done with custom fixed-width fonts, drawn to the screen with _PrintString. It's possible to do "multicolor" sprites by layering 2 or more characters, so you will see the sprite type has members fgChar, bgChar, fgColor, bgColor, although this demo only really uses fgChar for single color "sprites". All graphics (fonts) are defined in text (makes it easy to edit without ever leaving the code) and are used to generate BDF font files on the fly everytime the program runs (this can be easily changed to only create the files if they're not found in the program folder, but I've been constantly editing and adding graphics so it's just easier to have it regenerate them, and it's fast enough that it doesn't cause any noticeable extra delay when the program starts).
To see the actual graphic definitons for the fonts, search in the code for these routines:
Next, here are a couple UDTs and array definitions the collision detection uses:
This routine pre-populates the bit mask array for all characters/fonts that gets compared to detect overlapping pixels:
And finally, the collision detection routine:
Note that I also started playing with shrinking the boundary box by storing the edges for each character (see Sub FindEdges) but I realized it was redundant as GetBitMask was already storing them in x1, x2, y1, y2, and I saw it would be unwieldy to have to keep recalculating, so in the next version I started developing a 2nd version of GetBitMask that generates the bit mask and finds edges for each unique fgChar / bgChar combination. But I didn't need it for this game, and so it's neither here nor there. The point is that when I get to it, it will use the precalculated edges for each character, for the boundary boxes, and thus avoid having to compare empty rows / columns.
Anyway, I had this checking collisions for a few hundred sprites ranging in size from 8x8 upto 64x64 and it was plenty fast.
Attached also is "MultiCustomFontsAndCollisions144.bas" which is the updated version with more developed logic for shooting and players/robots/bullets being shot, and upto 16 players on the screen, where I started adding the updated "find edges" and collision routines for sprites using both fgChar and bgChar. The current part I'm stuck at is that when a robot gets shot, it's supposed to explode, but that animation isn't displaying. I have similar animation when the player gets shot, and that's visible, so I'm not sure what's going on. Also there's a weird bug that happens every so often, where if a player gets shot by another player's bullet, ALL the players die. Very weird.
Try running it and let me know what you think.
To run around and shoot things, use the cursor keys and the right Ctrl key. Also it breaks if you choose both the max robots AND max big robots. If you want to see a lot of things moving and colliding, choose 127 robots and 3 or 4 big robots.
First I'll explain that the graphics / sprites / etc. are all done with custom fixed-width fonts, drawn to the screen with _PrintString. It's possible to do "multicolor" sprites by layering 2 or more characters, so you will see the sprite type has members fgChar, bgChar, fgColor, bgColor, although this demo only really uses fgChar for single color "sprites". All graphics (fonts) are defined in text (makes it easy to edit without ever leaving the code) and are used to generate BDF font files on the fly everytime the program runs (this can be easily changed to only create the files if they're not found in the program folder, but I've been constantly editing and adding graphics so it's just easier to have it regenerate them, and it's fast enough that it doesn't cause any noticeable extra delay when the program starts).
To see the actual graphic definitons for the fonts, search in the code for these routines:
Code: (Select All)
' The base 8x8 font:
Sub GetTileText8x8 (arrTileText() As String)
...
' The special 8x8 character definitionss specific to this program:
Sub GetTileText8x8_Override (arrTileText() As String)
...
' The base 16x16 font:
Sub GetTileText16x16 (arrTileText() As String)
...
' The special 16x16 character definitionss specific to this program:
Sub GetTileText16x16_Override (arrTileText() As String)
...
' The base 32x32 font:
Sub GetTileText32x32 (arrTileText() As String)
...
' The special 32x32 character definitionss specific to this program:
Sub GetTileText32x32_Override (arrTileText() As String)
...
' The base 64x64 font:
Sub GetTileText64x64 (arrTileText() As String)
...
' The special 64x64 character definitionss specific to this program:
Sub GetTileText64x64_Override (arrTileText() As String)
Next, here are a couple UDTs and array definitions the collision detection uses:
Code: (Select All)
' USED BY PlayGame
Type MyFontType
FontHandle As Long
Height As Integer
Width As Integer
MaxRow As Integer
MaxCol As Integer
End Type
' USED BY PlayGame
Type PixelInfoType
pxCount As Long ' pixel count
x1 As Long ' column first pixel is at
x2 As Long ' column last pixel is at
y1 As Long ' row first pixel is at
y2 As Long ' row last pixel is at
End Type
ReDim arrFont(0 To 3) As MyFontType ' arrFont(FontIndex) holds font handle, character dimensions, etc., for a custom fixed-width font
ReDim arrPxInfo(0 To 3, 0 To 255) As PixelInfoType ' arrPxInfo(FontIndex, ChrCode), holds pixel count and edge info for each character 0-255 for 1 or more fonts
ReDim arrBitMask(0 To 3, 0 To 255, 0 To 63, 0 To 63) As _Byte ' arrBitMask(FontIndex, ChrCode, LocalY, LocalX), holds true/false bitmask array for each character 0-255 for 1 or more fonts
This routine pre-populates the bit mask array for all characters/fonts that gets compared to detect overlapping pixels:
Code: (Select All)
' /////////////////////////////////////////////////////////////////////////////
' GET PIXEL COUNT + POPULATE BIT MASK ARRAY
' RECEIVES
' FontIndex as Integer = which font (index for arrFont, arrBitMask, arrPxInfo) to populate arrPxInfo & arrBitMask for
' arrFont(0 To 3) As MyFontType ' arrFont(FontIndex) holds font handle, character dimensions, etc., for a custom fixed-width font
' arrPxInfo(0 To 3, 0 To 255) As PixelInfoType ' arrPxInfo(FontIndex, ChrCode), holds pixel count and edge info for each character 0-255 for 1 or more fonts
' arrBitMask(0 To 3, 0 To 255, 0 To 63, 0 To 63) As _Byte ' arrBitMask(FontIndex, ChrCode, LocalY, LocalX), holds true/false bitmask array for each character 0-255 for 1 or more fonts
' FOR EACH CHARACTER:
' IMAGE IS BLANK IF: arrPxInfo(FontIndex, iChar).pxCount = 0
' LEFT-MOST PIXEL: arrPxInfo(FontIndex, iChar).x1 ' NONE = _Width(MyImage)
' RIGHT-MOST PIXEL: arrPxInfo(FontIndex, iChar).x2 ' NONE = -1
' TOP-MOST PIXEL: arrPxInfo(FontIndex, iChar).y1 ' NONE = _Height(MyImage)
' BOTTOM-MOST PIXEL: arrPxInfo(FontIndex, iChar).y2 ' NONE = -1
Sub GetBitMask (FontIndex As Integer, arrFont() As MyFontType, arrPxInfo() As PixelInfoType, arrBitMask() As _Byte)
Dim MyImage As Long
Dim iChar As Integer
Dim y As Integer
Dim x As Integer
Dim p1 As _Unsigned Long
' CREATE TEMP IMAGE
InitImage MyImage, arrFont(FontIndex).Width, arrFont(FontIndex).Height, _RGBA32(0, 0, 0, 0)
_Font arrFont(FontIndex).FontHandle ' SWITCH FONT
_MapUnicode 32 To 32 ' FIX CHAR MAPPING (I WAS GETTING WEIRD CHARACTERS FOR SPACE)
_MapUnicode 255 To 255 ' FIX CHAR MAPPING SO WE GET CHR$(255)
' GET BIT MASK ARRAY + PIXEL COUNT FOR ALL CHARACTER CODES 0-255 FOR THIS FONT
For iChar = 0 To 255
_Dest MyImage: Cls , _RGBA32(0, 0, 0, 0)
Color _RGB32(0, 0, 0), _RGBA32(0, 0, 0, 0)
_PrintString (0, 0), Chr$(iChar), MyImage
' INITIALIZE
arrPxInfo(FontIndex, iChar).pxCount = 0
arrPxInfo(FontIndex, iChar).x1 = _Width(MyImage)
arrPxInfo(FontIndex, iChar).x2 = -1
arrPxInfo(FontIndex, iChar).y1 = _Height(MyImage)
arrPxInfo(FontIndex, iChar).y2 = -1
For y = LBound(arrBitMask, 3) To UBound(arrBitMask, 3)
For x = LBound(arrBitMask, 4) To UBound(arrBitMask, 4)
arrBitMask(FontIndex, iChar, y, x) = _FALSE
Next x
Next y
' COUNT PIXELS + POPULATE BITMASK
_Source MyImage
For y = 0 To _Height(MyImage) - 1
For x = 0 To _Width(MyImage) - 1
p1 = Point(x, y)
If _Alpha32(p1) > 0 Then
' FIND EDGES (FIRST PIXEL ALONG TOP+LEFT EDGES + LAST PIXEL AT BOTTOM+RIGHT EDGES)
If y < arrPxInfo(FontIndex, iChar).y1 Then arrPxInfo(FontIndex, iChar).y1 = y
If y > arrPxInfo(FontIndex, iChar).y2 Then arrPxInfo(FontIndex, iChar).y2 = y
If x < arrPxInfo(FontIndex, iChar).x1 Then arrPxInfo(FontIndex, iChar).x1 = x
If x > arrPxInfo(FontIndex, iChar).x2 Then arrPxInfo(FontIndex, iChar).x2 = x
' COUNT PIXELS
arrPxInfo(FontIndex, iChar).pxCount = arrPxInfo(FontIndex, iChar).pxCount + 1
' MARK LOCATION IN BIT MASK ARRAY
arrBitMask(FontIndex, iChar, y, x) = _TRUE
End If
Next x
Next y
Next iChar
' FREE MEMORY
FreeImage MyImage
End Sub ' GetBitMask
And finally, the collision detection routine:
Code: (Select All)
' /////////////////////////////////////////////////////////////////////////////
' Detect collision (regular bounding box test + bit mask overlap)
' Checks if the iSprite1's and iSprite2's bounding boxes overlap,
' if they do, compares bitmask arrays to determine if any pixels overlap,
' and returns _TRUE if collision detected or _FALSE if not.
' RECEIVES:
' iSprite1 = index in arrSprite of object #1 to check for collision
' iSprite2 = index in arrSprite of object #2 to check for collision
' arrSprite() = array of type SpriteType containing the sprite details
' arrBitMask() = byte array containing _TRUE/_FALSE
' for all pixels of all characters for all fonts
' (used to check for pixel-perfect collision)
Function ShapesCollided% (iSprite1 As Integer, iSprite2 As Integer, arrSprite() As SpriteType, arrBitMask() As _Byte)
Dim bCollision As Integer
Dim iOverlapLeft As Long
Dim iOverlapTop As Long
Dim iOverlapRight As Long
Dim iOverlapBottom As Long
Dim iX As Long
Dim iY As Long
Dim iLocalX1 As Long
Dim iLocalY1 As Long
Dim iLocalX2 As Long
Dim iLocalY2 As Long
bCollision = _FALSE
' =============================================================================
' BEGIN CHECK BOUNDARY OVERLAP
' =============================================================================
if ( _
((arrSprite(iSprite1).x1 + arrSprite(iSprite1).width) < arrSprite(iSprite2).x1) or _
((arrSprite(iSprite2).x1 + arrSprite(iSprite2).width) < arrSprite(iSprite1).x1) or _
((arrSprite(iSprite1).y1 + arrSprite(iSprite1).height) < arrSprite(iSprite2).y1) or _
((arrSprite(iSprite2).y1 + arrSprite(iSprite2).height) < arrSprite(iSprite1).y1) _
) = _FALSE then
' -----------------------------------------------------------------------------
' BEGIN SEE IF PIXELS OVERLAP
' -----------------------------------------------------------------------------
' Calculate the overlapping region
iOverlapLeft = _Max(arrSprite(iSprite1).x1, arrSprite(iSprite2).x1)
iOverlapTop = _Max(arrSprite(iSprite1).y1, arrSprite(iSprite2).y1)
iOverlapRight = _Min(arrSprite(iSprite1).x1 + arrSprite(iSprite1).width, arrSprite(iSprite2).x1 + arrSprite(iSprite2).width)
iOverlapBottom = _Min(arrSprite(iSprite1).y1 + arrSprite(iSprite1).height, arrSprite(iSprite2).y1 + arrSprite(iSprite2).height)
' Loop through the overlapping area
For iY = iOverlapTop To iOverlapBottom - 1
For iX = iOverlapLeft To iOverlapRight - 1
' Get local coordinates for each sprite
iLocalX1 = iX - arrSprite(iSprite1).x1
iLocalY1 = iY - arrSprite(iSprite1).y1
iLocalX2 = iX - arrSprite(iSprite2).x1
iLocalY2 = iY - arrSprite(iSprite2).y1
' Check if pixels are opaque in both masks
If _
( _
arrBitMask(arrSprite(iSprite1).FontIndex, arrSprite(iSprite1).fgChar, iLocalY1, iLocalX1) = _TRUE _
or _
arrBitMask(arrSprite(iSprite1).FontIndex, arrSprite(iSprite1).bgChar, iLocalY1, iLocalX1) = _TRUE _
) _
And _
( _
arrBitMask(arrSprite(iSprite2).FontIndex, arrSprite(iSprite2).fgChar, iLocalY2, iLocalX2) = _TRUE _
or _
arrBitMask(arrSprite(iSprite2).FontIndex, arrSprite(iSprite2).bgChar, iLocalY2, iLocalX2) = _TRUE _
) _
Then
' Collision detected
bCollision = _TRUE
Exit For
End If
Next iX
If bCollision = _TRUE Then Exit For
Next iY
' -----------------------------------------------------------------------------
' END SEE IF PIXELS OVERLAP
' -----------------------------------------------------------------------------
End If
' =============================================================================
' END CHECK BOUNDARY OVERLAP
' =============================================================================
ShapesCollided% = bCollision
End Function ' ShapesCollided%
Note that I also started playing with shrinking the boundary box by storing the edges for each character (see Sub FindEdges) but I realized it was redundant as GetBitMask was already storing them in x1, x2, y1, y2, and I saw it would be unwieldy to have to keep recalculating, so in the next version I started developing a 2nd version of GetBitMask that generates the bit mask and finds edges for each unique fgChar / bgChar combination. But I didn't need it for this game, and so it's neither here nor there. The point is that when I get to it, it will use the precalculated edges for each character, for the boundary boxes, and thus avoid having to compare empty rows / columns.
Anyway, I had this checking collisions for a few hundred sprites ranging in size from 8x8 upto 64x64 and it was plenty fast.
Attached also is "MultiCustomFontsAndCollisions144.bas" which is the updated version with more developed logic for shooting and players/robots/bullets being shot, and upto 16 players on the screen, where I started adding the updated "find edges" and collision routines for sprites using both fgChar and bgChar. The current part I'm stuck at is that when a robot gets shot, it's supposed to explode, but that animation isn't displaying. I have similar animation when the player gets shot, and that's visible, so I'm not sure what's going on. Also there's a weird bug that happens every so often, where if a player gets shot by another player's bullet, ALL the players die. Very weird.
Try running it and let me know what you think.

