NSpace is a pretty good collision detection algorithm.
'* nspace5.bas
'$checking: off
CONST NXDivs% = 16
CONST NYDivs% = 16
CONST NZDivs% = 16
CONST ubst% = 2519
CONST NDimensions% = 2
CONST MaxObjectRadius% = 3
MaxFPS% = 64
DIM SHARED MinScreenX%, MaxScreenX%, MinScreenY%, MaxScreenY%, NxDivSize%, NyDivSize%
DIM SHARED cstart AS SINGLE, cend AS SINGLE, minx, maxx, miny, maxy
cstart = 0: cend = 6.2
REDIM SHARED PolysInRegion%(NXDivs%, NYDivs%, 0), counts%(NXDivs%, NYDivs%), MaxPolys%
REDIM SHARED SinTable!(0 TO ubst%), CosTable!(0 TO ubst%), PolysInRegion%(NXDivs%, NYDivs%, 0)
'***********
DIM SHARED text$
text$ = " D.K.M Productions"
DIM SHARED word(1 TO LEN(text$) * 8, 1 TO 16)
FOR i& = 0 TO ubst%
SinTable!(i&) = SIN(2 * i& * 3.1415926535 / (ubst% + 1))
CosTable!(i&) = COS(2 * i& * 3.1415926535 / (ubst% + 1))
NEXT
oscreen& = _SCREENIMAGE
MaxScreenX% = _WIDTH(oscreen&) / 2
MaxScreenY% = _HEIGHT(oscreen&) / 2
MaxScreenZ% = 0
_FREEIMAGE oscreen&
MinScreenX% = 0
MinScreenY% = 0
MinScreenZ% = 0
ModNxDivsSx% = (MaxScreenX% - MinScreenX%) MOD NXDivs%
ModNyDivsSy% = (MaxScreenY% - MinScreenY%) MOD NYDivs%
ModNzDivsSz% = (MaxScreenZ% - MinScreenZ%) MOD NZDivs%
NxDivSize% = ((MaxScreenX% - MinScreenX%) - ModNxDivSx%) / NXDivs%
NyDivSize% = ((MaxScreenY% - MinScreenY%) - ModNyDivSy%) / NYDivs%
NzDivSize% = ((MaxScreenZ% - MinScreenZ%) - ModNzDivSz%) / NZDivs%
TYPE Polygons
x AS SINGLE
y AS SINGLE
z AS SINGLE
mass AS SINGLE
radius AS INTEGER
speedx AS SINGLE
speedy AS SINGLE
speedz AS SINGLE
COLOR AS INTEGER
'mass AS SINGLE
nsides AS INTEGER
radius2 AS SINGLE
END TYPE
REDIM b(0 TO 1) AS Polygons
MaxPolys% = 127
DIM SHARED Polys(0 TO MaxPolys%) AS Polygons
SepX% = (MaxScreenX% - MinScreenX%) / (2 * MaxObjectRadius%)
accum% = MaxObjectRadius%
x% = MaxObjectRadius%
y% = MaxObjectRadius%
FOR i% = LBOUND(Polys) TO UBOUND(Polys)
Polys(i%).nsides = SetRand(3, 5)
Polys(i%).radius = MaxObjectRadius% '* SetRand%(0, MaxObjectRadius%)
Polys(i%).x = x% '* SetRand(MinScreenX% + Polys(i%).radius, MaxScreenX% - Polys(i%).radius)
Polys(i%).speedx = SetRand(0, MaxObjectRadius% / 2)
Polys(i%).y = y% '* SetRand(MinScreenY% + Polys(i%).radius, MaxScreenY% - Polys(i%).radius)
Polys(i%).z = SetRand(MinScreenZ% + Polys(i%).radius, MaxScreenZ% - Polys(i%).radius)
Polys(i%).speedy = SetRand(0, MaxObjectRadius% / 2)
Polys(i%).speedz = SetRand(0, MaxObjectRadius% / 2)
Polys(i%).COLOR = SetRand(43, 127)
Polys(i%).mass = Polys(i%).nsides \ 2 + 1
IF x% > MaxScreenX% - MaxObjectRadius% THEN
y% = y% + 2 * MaxObjectRadius%
x% = MaxObjectRadius%
ELSE
x% = x% + 2 * MaxObjectRadius%
END IF
Polys(i%).radius2 = Polys(i%).radius ^ 2
NEXT
DIM logo AS Polygons
logo.z = 0
logo.speedx = 0
logo.speedy = 0
logo.speedz = 0
logo.mass = 1
GameScreen& = _NEWIMAGE(MaxScreenX%, MaxScreenY%, 256)
dimensionFlags% = 1
TempX% = (NDimensions% - 1)
BitSet% = 1
WHILE TempX% > 0
dimensionFlags% = dimensionFlags% OR 2 ^ BitSet%
BitSet% = BitSet% + 1
TempX% = TempX% \ 2
WEND
SCREEN GameScreen&
logo.x = _WIDTH / 2
logo.y = _HEIGHT / 2
LOCATE 2, 1: PRINT text$;
analyse
DO
'_AUTODISPLAY
IF _MOUSEINPUT THEN
PlayerX% = _MOUSEX
PlayerY% = _MOUSEY
lmb% = _MOUSEBUTTON(1)
rmb% = _MOUSEBUTTON(2)
END IF
'* check to see if objects collide with each other
DIM row AS INTEGER, cnt AS INTEGER
DIM xrot AS INTEGER, yrot AS INTEGER, scale AS INTEGER
xrot = 6: yrot = 6: scale = 4
OUT &H3C8, 1: OUT &H3C9, 10: OUT &H3C9, 20: OUT &H3C9, 63
time! = TIMER
DO
CLS
row = 2
Ltime! = TIMER
DO
DO
'LINE (minx, miny)-(max, maxy), 0, BF
minx = 32767
miny = 32767
FOR i = cstart TO cend STEP .04
x = (scale * 60 - (row * xrot)) * (COS(i))
IF x < minx THEN
minx = x
END IF
IF x > maxx THEN
maxx = x
END IF
y = (scale * 60 - (row * yrot)) * (SIN(i))
IF y < miny THEN
miny = y
END IF
IF y > maxy THEN
maxy = y
END IF
cnt = cnt + 1
IF word(cnt, row) > 0 THEN
CIRCLE (x / 2 + _WIDTH / 2, y / 2 + _HEIGHT / 2), scale, 1
PAINT STEP(0, 0), 1, 1
END IF
IF cnt = LEN(text$) * 8 THEN cnt = 0: EXIT DO
NEXT
LOOP
row = row + 1
LOOP UNTIL row = 16
cend = cend + .1
cstart = cstart + .1
IF ABS(maxx) > ABS(maxy) THEN
logo.radius = ABS(maxx) / 2
ELSE
logo.radius = ABS(maxy) / 2
END IF
logo.mass = 1
logo.radius2 = logo.radius ^ 2
IF -1 THEN
FOR i% = LBOUND(polys) TO UBOUND(polys)
IF Collision%(logo, Polys(i%), dimensionFlags%) THEN
IF (logo.x = Polys(i%).x) THEN
logo.speedx = (logo.radius / (scale ^ 2))
logo.speedy = 1
ELSE
slope! = (logo.y - Polys(i%).y) / (logo.x - Polys(i%).x)
IF Polys(i%).y >= logo.y THEN '* either going N or E (270-90)
IF Polys(i%).x >= logo.x THEN 'going east
Theta! = slope! * 90
ELSE 'going north
Theta! = 270 + slope! * 90
END IF
ELSE
IF Polys(i%).x >= logo.x THEN
Theta! = 90 + slope! * 90
ELSE
Theta! = 180 + 90 * slope!
END IF
END IF
logo.speedx = logo.radius / (scale ^ 2) * COS(Theta! * 3.14159 / 180)
logo.speedy = logo.radius / (scale ^ 2) * SIN(Theta! * 3.14159 / 180)
END IF
b(0) = logo
b(1) = Polys(i%)
CalcVelocities b(), 0, 1, dimensionFlags%
Polys(i%) = b(1)
Position Polys(i%), dimensionFlags%
'* DrawPoly Polys(i%)
ELSE
Position Polys(i%), dimensionFlags%
IF 0 THEN
IF Polys(i%).x < _WIDTH / 2 - maxx / 2 THEN
DrawPoly Polys(i%)
'PAINT (Polys(i%).x, Polys(i%).y), Polys(i%).color
ELSEIF Polys(i%).x > maxx / 2 + _WIDTH / 2 THEN
DrawPoly Polys(i%)
'PAINT (Polys(i%).x, Polys(i%).y), Polys(i%).color
ELSE
m% = (m% + 1) MOD 2
IF m% THEN
Polys(i%).x = _WIDTH / 2 - maxx / 2 - 1
ELSE
Polys(i%).x = maxx / 2 + _WIDTH / 2 + 1
END IF
END IF
ELSE
DrawPoly Polys(i%)
END IF
GetPossibleIndexes i%, Polys(i%).x, Polys(i%).y, Polys(i%).radius, MinScreenX%, MaxScreenX%, MinScreenY%, MaxScreenY%
'CollidedWithPlayer% = Collision%(PlayerX%, PlayerY%, 100, Polys(i%).x, Polys(i%).y, Polys(i%).radius)
'IF CollidedWithPlayer% THEN
'END IF
END IF
NEXT
END IF
FOR ax% = 0 TO NXDivs%
FOR ay% = 0 TO NYDivs%
FOR xj% = 0 TO counts%(ax%, ay%) - 1
p1% = PolysInRegion%(ax%, ay%, xj%)
FOR aj% = xj% + 1 TO counts%(ax%, ay%) - 1
p2% = PolysInRegion%(ax%, ay%, aj%)
IF Collision%(Polys(p1%), Polys(p2%), dimensionFlags%) THEN
CalcVelocities Polys(), p1%, p2%, dimensionFlags%
END IF
NEXT
NEXT
counts%(ax%, ay%) = 0
NEXT
NEXT
REDIM PolysInRegion%(NXDivs%, NYDivs%, 0)
Dtime! = ABS(TIMER - Ltime!)
IF ABS(Dtime! - 1 / MaxFPS%) > .010 THEN
MaxPolys% = MaxPolys% + 1
REDIM _PRESERVE Polys(MaxPolys%) AS Polygons
Polys(MaxPolys%).nsides = SetRand(3, 5)
Polys(MaxPolys%).radius = MaxObjectRadius% '* SetRand%(0, MaxObjectRadius%)
IF MaxPolys% MOD 2 THEN
Polys(MaxPolys%).x = SetRand(MinScreenX% + Polys(i%).radius, MinScreenX% + Polys(i%).radius)
Polys(MaxPolys%).y = SetRand(MinScreenY% + Polys(i%).radius, MinScreenY% + Polys(i%).radius)
ELSE
Polys(MaxPolys%).x = SetRand(MaxScreenX% - Polys(i%).radius, MaxScreenX% - Polys(i%).radius)
Polys(MaxPolys%).y = SetRand(MaxScreenY% - Polys(i%).radius, MaxScreenY% - Polys(i%).radius)
END IF
Polys(MaxPolys%).speedx = SetRand(0, MaxObjectRadius% / 2)
Polys(MaxPolys%).z = SetRand(MinScreenZ% + Polys(i%).radius, MaxScreenZ% - Polys(i%).radius)
Polys(MaxPolys%).speedy = SetRand(0, MaxObjectRadius% / 2)
Polys(MaxPolys%).speedz = SetRand(0, MaxObjectRadius% / 2)
Polys(MaxPolys%).COLOR = SetRand(43, 127)
Polys(MaxPolys%).mass = Polys(i%).nsides \ 2 + 1
Polys(i%).radius2 = Polys(i%).radius ^ 2
ELSEIF ABS(Dtime! - 1 / MaxFPS%) < .010 THEN
MaxPolys% = MaxPolys% - 100
REDIM _PRESERVE Polys(MaxPolys%) AS Polygons
END IF
_DISPLAY
_LIMIT 20
LOOP UNTIL ABS(TIMER - time!) > .15
LOOP UNTIL INKEY$ > "" OR rmb%
SYSTEM
SUB Position (P AS Polygons, flags%)
IF flags% AND 4 THEN
IF P.z + P.speedz < MinScreenZ% THEN
P.speedz = -P.speedz
ELSEIF P.z + P.speedz > MaxScreenZ% THEN
P.speedz = -P.speedz
END IF
P.z = P.z + P.speedz
END IF
IF flags% AND 2 THEN
IF P.y + P.speedy < MinScreenY% THEN
P.speedy = -P.speedy
ELSEIF P.y + P.speedy > MaxScreenY% THEN
P.speedy = -P.speedy
END IF
P.y = P.y + P.speedy
END IF
IF flags% AND 1 THEN
IF P.x + P.speedx < MinScreenX% THEN
P.speedx = -P.speedx
ELSEIF P.x + P.speedx > MaxScreenX% THEN
P.speedx = -P.speedx
END IF
P.x = P.x + P.speedx
END IF
END SUB
FUNCTION Collision% (T1 AS Polygons, t2 AS Polygons, flags%)
IF (flags% AND 4) THEN
dx! = (T1.x - t2.x) ^ 2
dy! = (T1.y - t2.y) ^ 2
IF dx! + dy! > (T1.radius2 + t2.radius2) THEN
Collision% = 0
ELSE
IF ABS(T1.z - t2.z) > (T1.radius + t2.radius) THEN
Collision% = 0
ELSE
Collision% = -1
END IF
END IF
EXIT FUNCTION
END IF
IF (flags% AND 2) THEN
dx! = (T1.x - t2.x) ^ 2
dy! = (T1.y - t2.y) ^ 2
IF dx! + dy! > (T1.radius2 + t2.radius2) THEN
Collision% = 0
ELSE
Collision% = -1
END IF
EXIT FUNCTION
END IF
IF flags% AND 1 THEN
IF ABS(T1.x - t2.x) > T1.radius + t2.radius THEN
Collision% = 0
ELSE
Collision% = -1
END IF
EXIT FUNCTION
END IF
END FUNCTION
FUNCTION SetRand% (MinValue%, MaxValue%)
SetRand% = MinValue% + RND * (MaxValue% - MinValue%)
END FUNCTION
SUB GetPossibleIndexes (PolyNumber%, x%, y%, radius%, MinSX%, MaxSX%, MinSY%, MaxSY%)
IF radius% > 0 THEN
oldix% = -1
oldiy% = -1
FOR i% = -radius% TO radius% STEP radius%
SELECT CASE x%
CASE MinSX% + radius% TO MaxSX% - radius%
SELECT CASE y%
CASE MinSY% + radius% TO MaxSY% - radius%
ax% = (x% + i%) \ NxDivSize%
ay% = (y% + i%) \ NyDivSize%
IF ax% <> oldix% OR ay% <> oldiy% THEN
IF counts%(ax%, ay%) > UBOUND(PolysInRegion%, 3) THEN
REDIM _PRESERVE PolysInRegion%(NXDivs%, NYDivs%, counts%(ax%, ay%))
END IF
PolysInRegion%(ax%, ay%, counts%(ax%, ay%)) = PolyNumber%
counts%(ax%, ay%) = counts%(ax%, ay%) + 1
oldix% = ax%
oldiy% = ay%
END IF
END SELECT
END SELECT
NEXT
ELSE
ax% = (x%) \ NxDivSize%
ay% = (y%) \ NyDivSize%
PolysInRegion%(ax%, ay%, counts%(ax%, ay%)) = PolyNumber%
counts%(ax%, ay%) = counts%(ax%, ay%) + 1
END IF
END SUB
SUB CalcVelocities (b() AS Polygons, i&, j&, flags%)
IF flags% AND 1 THEN
temp1 = b(i&).speedx
temp2 = b(j&).speedx
totalMass = (b(i&).mass + b(j&).mass)
b(i&).speedx = (temp1 * (b(i&).mass - b(j&).mass) + (2 * b(j&).mass * temp2)) / totalMass
b(j&).speedx = (temp2 * (b(j&).mass - b(i&).mass) + (2 * b(i&).mass * temp1)) / totalMass
ELSE
EXIT SUB
END IF
IF flags% AND 2 THEN
temp1 = b(i&).speedy
temp2 = b(j&).speedy
b(i&).speedy = (temp1 * (b(i&).mass - b(j&).mass) + (2 * b(j&).mass * temp2)) / totalMass
b(j&).speedy = (temp2 * (b(j&).mass - b(i&).mass) + (2 * b(i&).mass * temp1)) / totalMass
ELSE
EXIT SUB
END IF
IF flags% AND 4 THEN
temp1 = b(i&).speedz
temp2 = b(j&).speedz
b(i&).speedz = (temp1 * (b(i&).mass - b(j&).mass) + (2 * b(j&).mass * temp2)) / totalMass
b(j&).speedz = (temp2 * (b(j&).mass - b(i&).mass) + (2 * b(i&).mass * temp1)) / totalMass
ELSE
EXIT SUB
END IF
END SUB
SUB DrawPoly (T AS Polygons)
IF T.nsides > 0 THEN
IF T.radius > 0 THEN
CircleStepDeg% = (ubst% + 1) / T.nsides
Newx = T.x + T.radius * CosTable!(0)
Newy = T.y + T.radius * SinTable!(0)
angle% = 0
fpx = Newx
fpy = Newy
angle% = CircleStepDeg%
DO
IF angle% > ubst% THEN
LINE (fpx, fpy)-(Newx, Newy), T.COLOR
EXIT DO
ELSE
lastx = Newx
lasty = Newy
Newx = T.x + T.radius * CosTable!(angle%)
Newy = T.y + T.radius * SinTable!(angle%)
LINE (lastx, lasty)-(Newx, Newy), T.COLOR
angle% = angle% + CircleStepDeg%
END IF
LOOP
ELSE
PSET (T.x, T.y), T.COLOR
END IF
ELSE
PSET (T.x, T.y), T.COLOR
END IF
END SUB
SUB analyse
COLOR 2: LOCATE 1, 1: PRINT text$
DIM px AS INTEGER, py AS INTEGER, cnt AS INTEGER, ltrcnt AS INTEGER
px = 1: py = 1
DO
word(px, py) = POINT(px, py)
PSET (px, py), 1
px = px + 1
IF px = LEN(text$) * 8 THEN
px = 1
py = py + 1
END IF
LOOP UNTIL py = 16
END SUB