Esimerkkinä on kuinka kirjastolla voidaan tehdä sulavasti maanpinnalla kulkeva pallo. Raycast on ehkä paras tapa välttää "tunneloituminen" tämän tyyppisessä törmäys moottorissa, sillä esine kulkee yleensä todella nopeasti.
Nykyiset funktiot:
CheckPlotCollision() - epätarkka ehkä jopa hyödytön reunan etsintä tapa.
RayPlotCollision() - paras, nopein ja tarkin raycaster mitä cb:llä voi tehdä getpixel2() funktioon perustuen. (jos on vieläkin nopeampi, niin kertokaa ihmeessä)
CheckPlotAngleCollision() - maaston normaali CheckPlotCollision():lla. arvaa tarkuus...
RayPlotAngleCollision() - maaston normaali, toimiva ja kätevä.
RayPlotEnvCollision() - maaston normaali hitaille jutuille.
Funktiot:
Code: Select all
// Pixel-Perfect Collision, Edge Detector(s)!
Const COLLISION_PIXEL = -16777216
' Cords are in image cordinates, (0,0) is the left top corner. :
Global CPC_PlotX As Float
Global CPC_PlotY As Float
// Divide & Test Raycaster. What smaller acc# is that better is the result.
// Note that this algorithm can "skip" pixels pretty easily, so use it for
// a thick walls and when new-coordinates are inside the wall!
// Returns: -2 if coordinates are out of image bonds.
// false if there is no collision.
// true if there (may be) is collision.
// Solution is placed in globals CPC_PlotX and CPC_PlotY.
Function CheckPlotCollision(img%, oldx#, oldy#, newx#, newy#, acc# = 10)
'Check out of bounds:
xb% = ImageWidth(img)
yb% = ImageHeight(img)
If (oldx < 0 Or oldy < 0 Or oldx > xb Or oldy > yb) Or (newx < 0 Or newy < 0 Or newx > xb Or newy > yb)
Return -2
EndIf
'Check the collision
buffer% = Image(img)
Lock Image(img)
//New coordinates *must* bus be in side the wall
If GetPixel2(newx, newy, buffer) = COLLISION_PIXEL
Unlock buffer
Return 0
EndIf
'FIND the edge by divinding coordinates Step by Step.
to_new_x# = newx
to_new_y# = newy
to_old_x# = oldx
to_old_y# = oldy
to_x# = oldx
to_y# = oldy
current_x# = newx
current_y# = newy
While Distance(to_x, to_y, current_x, current_y) > acc
movx# = (current_x + to_x) / 2.0
movy# = (current_y + to_y) / 2.0
If GetPixel2(Int(current_x), Int(current_y), buffer) <> COLLISION_PIXEL
to_new_x = current_x
to_new_y = current_y
to_x = to_old_x#
to_y = to_old_y#
Else
to_old_x# = current_x
to_old_y# = current_y
to_x = to_new_x
to_y = to_new_y
EndIf
current_x = movx
current_y = movy
'Circle current_x-2,current_y-2,4 'For algorithm testing purpose..
Wend
Unlock buffer
CPC_PlotX = (current_x + to_x) / 2.0
CPC_PlotY = (current_y + to_y) / 2.0
Return 1
EndFunction
// Full Raycaster.
// Returns: -2 if coordinates are out of image bonds.
// false when there is no collision.
// true if there is an collision.
Function RayPlotCollision(img%, x1%, y1%, x2%, y2%)
'Check out of bounds:
xb% = ImageWidth(img)
yb% = ImageHeight(img)
If (x1 < 0 Or y1 < 0 Or x1 > xb Or y1 > yb) Or (x2 < 0 Or y2 < 0 Or x2 > xb Or y2 > yb)
Return -2
EndIf
'Check the collision
buffer% = Image(img)
Lock buffer
dx% = Abs(x2 - x1)
dy% = Abs(y2 - y1)
y% = y1
x% = x1
error% = 0
If dx > dy
add% = 1 - (y2 < y1) Shl 1
If x1 < x2
While x <= x2
If GetPixel2(x, y, buffer) <> COLLISION_PIXEL
CPC_PlotX = x - 1
CPC_PlotY = y
Unlock buffer
Return 1
EndIf
error = error + dy
If error Shl 1 >= dx
y = y + add
error = error - dx
EndIf
x = x + 1
Wend
Else
While x >= x2
If GetPixel2(x, y, buffer) <> COLLISION_PIXEL
CPC_PlotX = x + 1
CPC_PlotY = y
Unlock buffer
Return 1
EndIf
error = error + dy
If error Shl 1 >= dx
y = y + add
error = error - dx
EndIf
x = x - 1
Wend
EndIf
Else
add% = 1 - (x2 < x1) Shl 1
If y1 < y2
While y <= y2
If GetPixel2(x, y, buffer) <> COLLISION_PIXEL
CPC_PlotX = x
CPC_PlotY = y - 1
Unlock buffer
Return 1
EndIf
error = error + dx
If error Shl 1 >= dy
x = x + add
error = error - dy
EndIf
y = y + 1
Wend
Else
While y >= y2
If GetPixel2(x, y, buffer) <> COLLISION_PIXEL
CPC_PlotX = x
CPC_PlotY = y + 1
Unlock buffer
Return 1
EndIf
error = error + dx
If error Shl 1 >= dy
x = x + add
error = error - dy
EndIf
y = y - 1
Wend
EndIf
EndIf
Unlock buffer
Return 0
EndFunction
// Finds the collision normal of ray. (if there is an collision)
// Uses CheckPlotCollision to detect the edge. acc# is the accuary and
// wide# (pixels) is how far away two other rays are casted from
// orginal ray to calculate the angle.
// Returns: -2 if coordinates are out of image bonds.
// -1 if there is no collision.
// 0.0-360.0 if there is an collison. Collision point is
// placed in globals CPC_PlotX and CPC_PlotY
Function CheckPlotAngleCollision(img%, oldx#, oldy#, newx#, newy#, wide# = 4, acc# = 5)
If CheckPlotCollision(img, oldx, oldy, newx, newy, acc) = 0 Then Return -1
cx# = CPC_PlotX
cy# = CPC_PlotY
// "Left side"
angle# = GetAngle(oldx, oldy, newx, newy)
_addx# = Cos(angle + 90) * wide
_addy# = -Sin(angle + 90) * wide
If CheckPlotCollision(img, oldx + _addx, oldy + _addy, newx + _addx, newy + _addy, acc) = 0 Then Return -1
lx# = CPC_PlotX
ly# = CPC_PlotY
'Line oldx + _addx, oldy + _addy, lx, ly
// "Right side"
_addx# = Cos(angle - 90) * wide
_addy# = -Sin(angle - 90) * wide
If CheckPlotCollision(img, oldx + _addx, oldy + _addy, newx + _addx, newy + _addy, acc) = 0 Then Return -1
rx# = CPC_PlotX
ry# = CPC_PlotY
CPC_PlotX = cx
CPC_PlotY = cy
'Line oldx + _addx, oldy + _addy, rx, ry
//Calculate the angle:
ang_to_l# = GetAngle(cx, cy, lx, ly)
ang_to_r# = GetAngle(cx, cy, rx, ry)
pnormal1# = (((ang_to_l + 180) + ang_to_r) / 2.0)
pnormal2# = (((ang_to_r - 180) + ang_to_l) / 2.0)
normal# = (pnormal1 + pnormal2) / 2.0
// correct, If normal is upside-down:
angle + 180
_addx = Cos(angle)
_addy = Sin(angle)
If Distance(_addx,_addy,Cos(normal),Sin(normal)) < Distance(_addx,_addy,Cos(normal + 180),Sin(normal + 180))
Return normal
Else
Return WrapAngle(normal + 180)
EndIf
EndFunction
// Finds the collision normal of ray. (if there is an collision)
// Uses RayPlotCollision to detect the edge.
// wide# (pixels) is how far away two other rays are casted from
// orginal ray to calculate the angle. Actually this casts always
// tree rays at same time!
// Returns: -1 if there is no collision.
// 0.0-360.0 if there is an collison (the normal angle).
// Collision point is placed in globals CPC_PlotX and CPC_PlotY
Function RayPlotAngleCollision(img%, oldx#, oldy#, newx#, newy#, wide# = 4)
// "main ray"
If RayPlotCollision(img, oldx, oldy, newx, newy) < 1 Then Return -1
cx# = CPC_PlotX
cy# = CPC_PlotY
// "Left side"
angle# = GetAngle(oldx, oldy, newx, newy)
_addx# = Cos(angle + 90) * wide
_addy# = -Sin(angle + 90) * wide
If RayPlotCollision(img, oldx + _addx, oldy + _addy, newx + _addx, newy + _addy) < 1 Then Return -1
lx# = CPC_PlotX
ly# = CPC_PlotY
'Line oldx + _addx, oldy + _addy, lx, ly
// "Right side"
_addx# = Cos(angle - 90) * wide
_addy# = -Sin(angle - 90) * wide
If RayPlotCollision(img, oldx + _addx, oldy + _addy, newx + _addx, newy + _addy) < 1 Then Return -1
rx# = CPC_PlotX
ry# = CPC_PlotY
CPC_PlotX = cx
CPC_PlotY = cy
'Line oldx + _addx, oldy + _addy, rx, ry
//Calculate the angle:
ang_to_l# = GetAngle(cx, cy, lx, ly)
ang_to_r# = GetAngle(cx, cy, rx, ry)
pnormal1# = (((ang_to_l + 180) + ang_to_r) / 2.0)
pnormal2# = (((ang_to_r - 180) + ang_to_l) / 2.0)
normal# = (pnormal1 + pnormal2) / 2.0
// correct, If normal is upside-down:
angle + 180
If Distance(Cos(angle),Sin(angle),Cos(normal),Sin(normal)) < Distance(Cos(angle),Sin(angle),Cos(normal + 180),Sin(normal + 180))
Return normal
Else
Return WrapAngle(normal + 180)
EndIf
EndFunction
// Gains useful data around position (org_x#, org_y#)
// Function casts total amount of ray_count%, range# lenght rays.
// #1: Finds the ray, that collide point is nearest to (org_x#, org_y#)
// Solution is placed to globals CPC_NEAR_ENV_X and CPC_NEAR_ENV_Y
// #2: Finds the ray, that collide point is furthest to (org_x#, org_y#)
// Solution is placed to globals CPC_FAR_ENV_X and CPC_FAR_ENV_Y
// Uses RayPlotCollision to detect the edge.
// Returns: false when there is no collision.
// true if there is a collision.
Global CPC_NEAR_ENV_X As Float, CPC_NEAR_ENV_Y As Float
Global CPC_FAR_ENV_X As Float, CPC_FAR_ENV_Y As Float
Function RayPlotEnvCollision(img%, org_x#, org_y#, range#, ray_count%)
i% = 0
near_dist# = -1
far_dist# = 0
collied% = False
While i < ray_count
cast_angle# = (360.0 / ray_count) * i
cast_x# = org_x + Cos(cast_angle) * range
cast_y# = org_y - Sin(cast_angle) * range
If RayPlotCollision(img, org_x, org_y, cast_x, cast_y)
dist# = Distance(org_x, org_y, CPC_PlotX, CPC_PlotY)
If dist > far_dist
far_dist = dist
CPC_FAR_ENV_X = CPC_PlotX
CPC_FAR_ENV_Y = CPC_PlotY
EndIf
If dist < near_dist Or near_dist = -1
near_dist = dist
CPC_NEAR_ENV_X = CPC_PlotX
CPC_NEAR_ENV_Y = CPC_PlotY
EndIf
collied = True
EndIf
i + 1
Wend
Return collied
EndFunction
// ###############################
// Sub library: BoolCollisionMaps
// ###############################
// Scans img and returns memblock containing the collision able areas.
Function CreateCollisionMap(img%, hit_color% = -16777216)
EndFunction
// "Draws" collision src% to dst% in (x%, y%)
Function ApplyCollisionMapTo(src%, dst%, x%, y%)
EndFunction
// "ImagesCollide" routine.
// Returns: true when there is a collision.
Function CollisionTest(test1%, x1%, y1%, test2%, x2%, y2%)
EndFunction
// Like the CollisionTest expect, is it ultimately more useful!
Function CollisionTestAdv(test1%, x1%, y1%, test2%, x2%, y2%, hx%, hy%, pass% = -1)
EndFunction
Code: Select all
SCREEN 800, 600
'Include "PixelCollisionLib.cb"
// (lähes nopeuden säilyttävä) varustettu maata myöten liukuva pallo
// pikselin tarkalla törmäystunnistuksella!
Global img As integer
Global mouse_scroll As Float
img = MakeImage(800,600)
DrawToImage img
Color cbblue
Circle 100, 100, 200, 1
Circle 400, 200, 200, 1
Color cbblack
Circle 105, 105, 190, 1
DrawToScreen
Color cbwhite
Const gravitation = 0.1
Const push_out = 0.5
plot_prev_x# = 0
plot_prev_y# = 0
plot_x# = 0
plot_y# = 0
plot_speedx# = 0
plot_speedy# = 0
plot_accelx# = 0
plot_accely# = 0
mouse_scroll = 10
Repeat
If MouseHit(1)
plot_x = MouseX()
plot_y = MouseY()
plot_speedx = 0
plot_speedy = 0
test = ON
EndIf
If MouseDown(2)
DrawToImage img
Color cbblue
Circle MouseX(),MouseY(),mouse_scroll,1
DrawToScreen
EndIf
old = mouse_scroll
mouse_scroll = mouse_scroll + MouseMoveZ()
DrawImage img,0,0
Color 0,255,0
Circle plot_x - mouse_scroll / 2, plot_y - mouse_scroll / 2, mouse_scroll, OFF
If old <> mouse_scroll
Color cbblue
Circle MouseX(),MouseY(),mouse_scroll,0
EndIf
If test Then Gosub DoSimulation
If (plot_x < 1 Or plot_y < 1 Or plot_x > ScreenWidth()-1 Or plot_y > ScreenHeight())
test = OFF
EndIf
DrawScreen
Forever
End
DoSimulation:
plot_prev_x = plot_x
plot_prev_y = plot_y
// Vaihe 1: Korjataan paikkaa & Liu'utetaan pistettä.
normal# = -1
If RayPlotEnvCollision(img, plot_x, plot_y, mouse_scroll/2, 36)
normal# = GetAngle(CPC_NEAR_ENV_X, CPC_NEAR_ENV_Y, plot_x, plot_y)
dist# = Max(mouse_scroll/2 - Distance(CPC_NEAR_ENV_X, CPC_NEAR_ENV_Y, plot_x, plot_y), 0)
plot_x = plot_x + Cos(normal) * dist
plot_y = plot_y - Sin(normal) * dist
plot_speedx = plot_speedx + Cos(normal) * push_out
plot_speedy = plot_speedy - Sin(normal) * push_out
EndIf
// Vaihe 2: siirretään pistettä käyttäjän mukaan.
plot_accely = gravitation
plot_accelx = 0
//Kiihtyvyys:
If KeyDown(cbkeya) then plot_accelx = -0.3
If KeyDown(cbkeyd) then plot_accelx = 0.3
//Nopeus:
If KeyDown(cbkeyright) Then plot_speedx = 2
If KeyDown(cbkeyleft) Then plot_speedx = -2
//Hyppy:
If KeyHit(cbkeyspace) Then plot_speedy = -5
plot_speedy = plot_speedy + plot_accely
plot_speedx = plot_speedx + plot_accelx
plot_x = plot_x + plot_speedx
plot_y = plot_y + plot_speedy
// ExtremE nopea liike:
If KeyHit(cbkeyr)
plot_x = MouseX()
plot_y = MouseY()
EndIf
// Vaihe 3: Korjataan pahasti tapahtunut uppoaminen.
If RayPlotCollision(img, plot_prev_x, plot_prev_y, plot_x, plot_y)
plot_x = CPC_PlotX*0.8 + plot_prev_x*0.2
plot_y = CPC_PlotY*0.8 + plot_prev_y*0.2
plot_speedx = 0
plot_speedy = 0
EndIf
Return