|
Post by Rod on Jan 27, 2021 19:07:36 GMT
My latest project, chess got too much! So now I need to read up on collisions. I have circle to circle collisions code from my billiards program and baubles program. The sides are easy but it should have a curved top. Also the bumper bars, triangles, and the flippers. Collisions collisions collisions. Off to study Andy's tutorial as a starting point. This is the Pinball Game link.
|
|
|
Post by B+ on Jan 27, 2021 20:36:39 GMT
Ball (circle) hits line TF (QB64 but you can translate I am sure) x,y,r from ball (x1, y1), (x2, y2) are end points of line.
FUNCTION hitLine (x, y, r, xx1, yy1, xx2, yy2) x1 = xx1: y1 = yy1: x2 = xx2: y2 = yy2 IF x1 > x2 THEN SWAP x1, x2: SWAP y1, y2 IF x < x1 OR x > x2 THEN hitLine = 0: EXIT SUB IF ((y2 - y1) / (x2 - x1)) * (x - x1) + y1 - r < y AND y < ((y2 - y1) / (x2 - x1)) * (x - x1) + y1 + r THEN hitLine = 1 ELSE hitLine = 0 END IF END FUNCTION
I have circle Intersect circle also if needed.
Those triangle shapes are triangles = 3 lines with 3 circles on end. Probably how you do flippers? 2 lines with 2 circles on end.
I probably have the point of intersect for line and circle as well, I also have circle Intersect circle for Intersect Points if needed.
You have a Billiards program? Is it really Billiards or (hopefully) Pool like 8 Ball, Straight or 9 Ball.
|
|
|
Post by Rod on Jan 27, 2021 21:08:29 GMT
Thanks that looks useful. I do have a really good snooker program over at Liberty but it uses Dan Teals gfxDLL which allows bmp and line drawing to coexist. It double buffers. However, here is my "simple" ball to ball collision code. It has an ATAN() function. Not sure the ATAN function can be avoided, I thought I read an article someplace that said vector maths was enough but I aint no mathematician. The graphics are poor it was designed to test the collisions. '------------------------------------------ '2D BILLIARD STYLE COLLISION '------------------------------------------ 'converted from a Blitz BASIC demo 'By Joseph 'Phish' Humfrey ' 'converted with the help of 'Anatoly and Stefan
nomainwin WindowWidth = 800+themeWidth WindowHeight = 600+themeHeight UpperLeftX = (DisplayWidth-WindowWidth)/2 UpperLeftY = (DisplayHeight-WindowHeight)/2 graphicbox #1.g, 0,0,800,600 open "2D Bumping" for graphics_nf_nsb as #1 #1 "trapclose [quit]"
#1.g "down ; fill black ; flush"
[setup] width=800 height=600 friction=.99 maxballs=15 dim ball(maxballs,6) 'x y delta x delta y radius mass b.x=1 'ball x b.y=2 'ball y b.dx=3 'ball delta x b.dy=4 'ball delta y b.r=5 'ball radius b.m=6 'ball mass
b.i=0 'ball index
'set out red balls in a triangle ballTriangleSize=5 for xloop = ballTriangleSize to 1 step -1 for yloop = 1 to xloop b.i=b.i+1 ball(b.i,b.x) = (5-xloop)*27 + 200 ball(b.i,b.y) = yloop*31-(xloop*31)/2.0 + 300 ball(b.i,b.dx)= 0 ball(b.i,b.dy)= 0 ball(b.i,b.r) = 15 ball(b.i,b.m) = 50 next next
'set out white cue ball cue=0 ball(cue,b.x) = 600 ball(cue,b.y) = 300 +20 ball(cue,b.dx)= -20 ball(cue,b.dy)= 2-int(rnd(0)*4+.5) ball(cue,b.r) = 15 ball(cue,b.m) = 50
#1.g "when characterInput [setup]" #1.g "setfocus"
timer 17,[updateballs] wait
[updateballs] for b.i= 0 to maxballs
'update positions ball(b.i,b.x)=ball(b.i,b.x)+ball(b.i,b.dx) ball(b.i,b.y)=ball(b.i,b.y)+ball(b.i,b.dy)
'gradually slow down ball(b.i,b.dx)=ball(b.i,b.dx)*friction ball(b.i,b.dy)=ball(b.i,b.dy)*friction
'------------------------------------------ 'COLLISION CHECKING '------------------------------------------ ' Check each ball against every other
for b= b.i to maxballs
' not against itself if b<>b.i then
collisionDistance = ball(b.i,b.r)+ball(b,b.r) actualDistance = sqr((ball(b,b.x)-ball(b.i,b.x))^2 + (ball(b,b.y)-ball(b.i,b.y))^2)
' the collision distance is the some of the radius of both balls ' if the actual distance appart is less than that then they have collided If actualDistance<collisionDistance Then
' calculate the angle the colliding ball is travelling at collNormalAngle=atan2(ball(b,b.y)-ball(b.i,b.y),ball(b,b.x)-ball(b.i,b.x))
' re position the balls as exactly touching, no intersection ' this prevents overlapping balls being seen as still colliding moveDist1=(collisionDistance-actualDistance)*(ball(b,b.m)/(ball(b.i,b.m)+ball(b,b.m))) moveDist2=(collisionDistance-actualDistance)*(ball(b.i,b.m)/(ball(b.i,b.m)+ball(b,b.m))) ball(b.i,b.x)=ball(b.i,b.x) + moveDist1*Cos(collNormalAngle+3.14159) ball(b.i,b.y)=ball(b.i,b.y) + moveDist1*Sin(collNormalAngle+3.14159) ball(b,b.x)=ball(b,b.x) + moveDist2*Cos(collNormalAngle) ball(b,b.y)=ball(b,b.y) + moveDist2*Sin(collNormalAngle)
'------------------------------------------ 'COLLISION RESPONSE '------------------------------------------ 'n = vector connecting the centers of the circles. 'we are finding the components of the normalised vector n nX=Cos(collNormalAngle) nY=Sin(collNormalAngle)
'now find the length of the components of each movement vectors 'along n, by using dot product. a1 = ball(b.i,b.dx)*nX + ball(b.i,b.dy)*nY a2 = ball(b,b.dx)*nX + ball(b,b.dy)*nY
'optimisedP = 2(a1 - a2) ' ---------- ' m1 + m2 optimisedP = (2.0 * (a1-a2)) / (ball(b.i,b.m) + ball(b,b.m))
'now find out the resultant vectors 'r1 = c1\v - optimisedP * mass2 * n ball(b.i,b.dx) = ball(b.i,b.dx) - (optimisedP*ball(b,b.m)*nX) ball(b.i,b.dy) = ball(b.i,b.dy) - (optimisedP*ball(b,b.m)*nY)
'r2 = c2\v - optimisedP * mass1 * n ball(b,b.dx) = ball(b,b.dx) + (optimisedP*ball(b.i,b.m)*nX) ball(b,b.dy) = ball(b,b.dy) + (optimisedP*ball(b.i,b.m)*nY)
end If end if next
'Simple Bouncing off walls. if ball(b.i,b.x)<ball(b.i,b.r) then ball(b.i,b.x)=ball(b.i,b.r) ball(b.i,b.dx)=ball(b.i,b.dx)*-0.9 end if if ball(b.i,b.x)>width-ball(b.i,b.r) then ball(b.i,b.x)=width-ball(b.i,b.r) ball(b.i,b.dx)=ball(b.i,b.dx)*-0.9 end if if ball(b.i,b.y)<ball(b.i,b.r) then ball(b.i,b.y)=ball(b.i,b.r) ball(b.i,b.dy)=ball(b.i,b.dy)*-0.9 end if if ball(b.i,b.y)>height-ball(b.i,b.r) then ball(b.i,b.y)=height-ball(b.i,b.r) ball(b.i,b.dy)=ball(b.i,b.dy)*-0.9 end if
next
[drawballs] #1.g "discard ; redraw" for b.i= 0 to maxballs if b.i=0 then #1.g "color white ; backcolor white" else #1.g "color red ; backcolor red" end if #1.g "place ";ball(b.i,b.x);" ";ball(b.i,b.y) #1.g "circlefilled "; ball(b.i,b.r) #1.g "color white ;\";b.i next
wait
[quit] close #1 end
function atan2(y,x) pi = asn(1) * 2 if x <> 0 then arctan = atn(y/x)
select case case x > 0 atan2 = arctan
case y>=0 and x<0 atan2 = pi + arctan
case y<0 and x<0 atan2 = arctan - pi
case y>0 and x=0 atan2 = pi / 2
case y<0 and x=0 atan2 = pi / -2 end select end function
|
|
|
Post by B+ on Jan 27, 2021 21:50:48 GMT
Yes ATAN2 is great for calculation of the angle the balls should go after the collision, it uses the difference of the 2 balls x, y positions to calc the angle one is to the other.
A little lesson:
The angle in Radians (Ra) of ball 2 (x2, y2) to ball 1 (x1, y1) (or any point to any other)
Ra= ATAN2(y2-y1, x2-x1) and ball 1 to ball 2 is just reversing 1's and 2's ATAN2(y1 - y2, x1 - x2) the base point is the 2nd of the two pairs with y first listed in arguments and x listed second.
I don't know if you want to do collisions without ATAN2 but you can surely do them without Dot Product the tangent line is just 90 degrees off the angle of one ball to the other.
Here is a simple version I use to great success in Air Hockey Game
IF SQR((px - psx) ^ 2 + (py - psy) ^ 2) < (pr + pr2) THEN 'contact player striker pa = _ATAN2(py - psy, px - psx) 'boost puck away px = px + .5 * speed * COS(pa) py = py + .5 * speed * SIN(pa) snd 2200, 4 END IF
|
|
|
Post by Rod on Feb 10, 2021 19:34:33 GMT
ok, new project progresses. here I am looking at the ball and the large circles which will eventually bounce the ball harder rather than just deflect it. so I am at the stage of recognising the collision, stepping the ball back to the kissing point of the impact. think that is working. Now I need to use the ball's vector and the imaginary ring/bumpers vector to set the recoil direction. I am trying to avoid atan2 and use just vectors. Any wise words from maths gurus very welcome. gamebin.webs.com/Liberty/Pinball.zip
|
|
|
Post by B+ on Feb 11, 2021 0:21:25 GMT
... Now I need to use the ball's vector and the imaginary ring/bumpers vector to set the recoil direction. I am trying to avoid atan2 and use just vectors. Any wise words from maths gurus very welcome. Ironic. There is a Zen koan about some Master hanging by his teeth over a cliff and asked a question by some novice about some high spiritual something and the koan question is what was his reply? A vector is a x, y and x, y are usually calc'd from an angle with SIN and COS which need some angle argument (oh those words are probably being avoided as well LOL) and how is that angle determined without ATAN2, well we just have to reinvent ATAN2 and all of Trig with different words I guess ;-))
|
|
|
Post by B+ on Feb 11, 2021 2:06:59 GMT
And what is ATAN or ATAN2?
Well first what is TAN?
Merely the ratio of SIN / COS
So that makes ATAN the angle whose TAN = SIN / COS.
So ATAN is some angle whose TAN = SIN / COS usually comes in the form of Radian Units as opposed to Degrees.
|
|
|
Post by B+ on Feb 11, 2021 18:26:50 GMT
B+ making Pinball dreams come true!
'Rod's Pinball with B+ fix of math 2021-02-11
nomainwin 'hey watch the ball
loadbmp "bump", "bumper.bmp" loadbmp "ring", "ring.bmp" loadbmp "butt","button.bmp" loadbmp "board","pinboard.bmp" loadbmp "ball","ball.bmp" loadbmp "ball1","ball1.bmp" loadbmp "ball2","ball2.bmp" loadbmp "flip1","flip1.bmp" loadbmp "flip2","flip2.bmp" loadbmp "flip3","flip3.bmp" loadbmp "c0","num0.bmp" loadbmp "c1","num1.bmp" loadbmp "c2","num2.bmp" loadbmp "c3","num3.bmp" loadbmp "c4","num4.bmp" loadbmp "c5","num5.bmp" loadbmp "c6","num6.bmp" loadbmp "c7","num7.bmp" loadbmp "c8","num8.bmp" loadbmp "c9","num9.bmp"
'open a window and graphicbox WindowHeight = 740 WindowWidth = 820 graphicbox #w.g, 0, 0, 800, 700 open "Pinball" for window_nf as #w #w "trapclose [quit]"
'add the background and sprite to the sprite engine 'add background using bmp landscape 'add a sprite called smiley #w.g "background board" #w.g "addsprite b1 bump ; spritexy b1 60 160 ; spriteorient b1 flip" #w.g "addsprite b2 bump ; spritexy b2 56 400 " #w.g "addsprite b3 bump ; spritexy b3 656 160 ; spriteorient b3 rotate 180" #w.g "addsprite b4 bump ; spritexy b4 660 400 ; spriteorient b4 mirror" #w.g "addsprite r1 ring ; spritexy r1 135 290" #w.g "addsprite r2 ring ; spritexy r2 515 290" #w.g "addsprite s1 butt ; spritexy s1 259 150" #w.g "addsprite s2 butt ; spritexy s2 290 470" #w.g "addsprite s3 butt ; spritexy s3 465 150" #w.g "addsprite s4 butt ; spritexy s4 434 470" #w.g "addsprite fl flip1 flip2 flip3 ; spritexy fl 275 590 ; cyclesprite fl 1" #w.g "addsprite fr flip1 flip2 flip3 ; spritexy fr 430 590 ; spriteorient fr mirror ; cyclesprite fr 1"
#w.g "addsprite b ball ; spritexy b 800 700" #w.g "addsprite b1 ball1 ;spritexy b1 800 700" #w.g "addsprite b2 ball2 ;spritexy b2 800 700"
'load the numbers xx=20 yy=20 for x=1 to 12 #w.g "addsprite clock";str$(x);" c0 c1 c2 c3 c4 c5 c6 c7 c8 c9" #w.g "spritexy clock";str$(x);" ";xx;" ";yy xx=xx+35 if x=6 then xx=xx+332 next 'gravity=.099 see change in dx and dy 'friction=.9999999 bx=100 by=100 br=24 bd=48 rr=69 dx=rnd(0)*30+10 dy=rnd(0)*10 dA = atan(dy/dx) ' find ball speed from dx, dy 'dx = speed * cos(dA) speed = dx / cos(dA)
dim ring(2,3) 'x, y, radius dim scor(4,3) x=1 'ring x y=2 'ring y r=3 'ring radius ring(1,1)=135+rr ring(1,2)=290+rr ring(1,3)=69 ring(2,1)=515+rr ring(2,2)=290+rr ring(2,3)=69
scor(1,1)=298 scor(1,2)=188 scor(1,3)=75 scor(2,1)=504 scor(2,2)=188 scor(2,3)=75 scor(3,1)=329 scor(3,2)=509 scor(3,3)=75 scor(4,1)=473 scor(4,2)=509 scor(4,3)=75
'start the keyboard checking event print #w.g, "when characterInput [keyboardinterrupt]" print #w.g, "setfocus"
' now start the timer to repeatedly call the drawing loop timer 56, [drawingloop]
'now sit and wait for the first event to trigger wait
[drawingloop]
'move ball by dx,dy and check simple wall, floor and ceiling boundaries for now
' apply friction and gravity to the free moving ball's delta dx,dy dx = dx - .1 'friction dy = dy + .7 'friction and gravity
bx=bx+dx 'take some force off the ball when hits a wall if bx<br then bx=br : dx=0-dx*.98 if bx>800-br then bx=800-br : dx=0-dx*.98 by=by+dy if by<100+br then by=100+br : dy=0-dy*.98 if by>700-br then by=700-br : dy=0-dy*.93 '<< when it hits the bottom wall we've got to knock some wind out of it!
'check each ring 'collision distance is radius plus radius so 24 + 79 = 103 cd=103 'what happened to 103? 'print using("###",bx);" ";using("###",by);" ";using("###.#",dx);" ";using("###.#",dy) for i=1 to 2
'calculate actual distance apart ad = sqr( (ring(i,x)-bx)^2 + (ring(i,y)-by)^2)
'if less than collision distance they have collided if ad<cd then
'show collision for debugging 'print "ring ";using("###",ring(i,x));" ";using("###",ring(i,y)) 'print "ball ";using("###",bx);" ";using("###",by) 'print " dist ";cd-ad #w.g "spritexy b1 ";bx-br;" ";by-br #w.g "drawsprites"
'the ball will have gone beyond the rings perimiter as it steps a large amount at a time 'so what is the overlap when we saw the collision d=cd-ad
'get the normalised vector x,y the ball was travelling 'vl=sqr(dx*dx+dy*dy) 'vx=dx/vl 'vy=dy/vl 'print "delta ";dx;" : ";dy;" vector ";vx;" : ";vy 'apply negative vector to the overlap distance d and step back to the perimiter 'bx=bx+d*(0-vx) 'by=by+d*(0-vy) 'print i;" ";bx;" : ";by;" delta ";d*(0-vx);" : ";d*(0-vy)
'show the step back for debugging '#w.g "spritexy b2 ";bx-br;" ";by-br '#w.g "drawsprites"
'get the vector between ball centre and ring centre 'vx=bx-ring(i,x) 'vy=by-ring(i,y) 'vl=sqr(vx*vx+vy*vy) 'vx=vx/vl 'vy=vy/vl 'print "ring vector ";vl;" ";vx;" : ";vy
'now calculate the result of two vectors colliding
'dx=0-dx+5*vx 'dy=0-dy+5*vy
ba = Atan2((by - ring(i,y)) , (bx - ring(i,x))) ' for angle of ring to ball
'punch those balls away for extra kick to ball off rings dx = 1.28*speed * COS(ba) 'speed is not reduced because rings are not movable, all the force goes back into ball dy = 1.28*speed * SIN(ba) bx = bx + dx by = by + dy
print #w.g, "drawsprites"
end if
next #w.g "spritexy b ";bx-br;" ";by-br 'now redraw the sprites
print #w.g, "drawsprites" wait
[keyboardinterrupt] 'this loop gets called every time a key is pressed wait
[quit] timer 0 close #w
Function Atan2(y,x) 'Atan2 is a function which determines the angle between points 'x1, y1 and x2, y2. The angle returned is in radians 'The angle returned is always in the range of '-PI to PI radians (-180 to 180 degrees) '============================================================== 'NOTE the position of Y and X arguments 'This keeps Atan2 function same as other language versions '============================================================== If x = 0 Then If y < 0 Then Atan2 = -1.5707963267948967 Else Atan2 = 1.5707963267948967 End If Else chk = atn(y/x) If x < 0 Then If y < 0 Then chk = chk - 3.1415926535897932 Else chk = chk + 3.1415926535897932 End If End If Atan2 = chk End If 'thanks Andy Amaya End Function
A note about Pinball Bumper Rings - there is a wire that gets pushed into a metal magnet or spring so ball pushes into bumper a little until wire ring makes contact with inside ring causing electric signal (or maybe just spring) to pop hard against ball.
Something like that, the point is that you don't have to worry so much where the ball kissed the ring because it pushes a little or allot and then is punched back hard!
|
|
|
Post by Rod on Feb 11, 2021 18:53:44 GMT
Thank you for that. My Atan2 avoidance stems from reading a tutorial once about vectors. In it it was stated that Atan2 was not really required. Now I can't find the tutorial again but the code was about ray tracing. There is ray/sphere intersect code that just seems to use the vectors to calculate the reflection/refraction. Its 3d but pretty sure it would work in 2d. Perhaps the point I am missing is that the ray tracing is point to point, eye to intersect, intersect to light
I am well out my depth, perhaps Atan2 will simply do.
nomainwin 'constants to access the point arrays global X,Y,Z,R,Red,Green,Blue,Xp,Yp,DistToScreen,maxObjects X=0 Y=1 Z=2 R=3 Red=4 Green=5 Blue=6
'the world is 24x24 units '0,0 is centre of the world '-12 far left 12 far right '12 top -12 bottom 'objects are specified in world units 'rays are specified in world units 'pixel coordinates are converted by 'dividing the world width by the screen width 'and the world height by the screen height 'this gives a per pixel world increment value
ScrXLeft=-12 ScrXRight=12 ScrYTop=-12 ScrYBottom=12 ImgX=800 'pixel width of screen ImgY=600 'pixel height of screen ScrX=2*ScrXRight/ImgX 'per pixel world increment value ScrY=2*ScrYBottom/ImgY
'Calculate how much space windows borders take 'Anatoly's tip WindowWidth = 200 WindowHeight = 200 open "Ajusting..." for graphics_nsb as #1 #1, "home ; down ; posxy w h" w=200-2*w : h = 200-2*h 'w and h now contain the number of pixels 'the Windows scheme/theme takes close #1 WindowWidth = ImgX+w WindowHeight = ImgY+h UpperLeftX = int((DisplayWidth-WindowWidth)/2) UpperLeftY = int((DisplayHeight-WindowHeight)/2) open "Raytrace" for graphics_nsb as #1 #1 "down ; fill 192 192 192 ; trapclose [quit]"
'sphere data x,y,z,radius,color 'three spheres ,red green and blue 'evenly spaced around centre of screen 'z order front to back blue, green, red
'there are five massive spheres positioned 'all round the edges and to the rear to create walls
maxObjects=7 dim sp(maxObjects,6) sp(0,X)=0 sp(0,Y)=0 sp(0,Z)=5 sp(0,R)=4 sp(0,Red)=255 sp(0,Green)=0 sp(0,Blue)=0 sp(1,X)=-6 sp(1,Y)=0 sp(1,Z)=5 sp(1,R)=2 sp(1,Red)=0 sp(1,Green)=255 sp(1,Blue)=0 sp(2,X)=6 sp(2,Y)=0 sp(2,Z)=2 sp(2,R)=2 sp(2,Red)=0 sp(2,Green)=0 sp(2,Blue)=255 sp(3,X)=-36 sp(3,Y)=0 sp(3,Z)=20 sp(3,R)=26 sp(3,Red)=64 sp(3,Green)=128 sp(3,Blue)=255 sp(4,X)=36 sp(4,Y)=0 sp(4,Z)=20 sp(4,R)=26 sp(4,Red)=64 sp(4,Green)=128 sp(4,Blue)=255 sp(5,X)=0 sp(5,Y)=-36 sp(5,Z)=20 sp(5,R)=26 sp(5,Red)=64 sp(5,Green)=128 sp(5,Blue)=25 sp(6,X)=0 sp(6,Y)=36 sp(6,Z)=20 sp(6,R)=26 sp(6,Red)=64 sp(6,Green)=128 sp(6,Blue)=25 sp(7,X)=0 sp(7,Y)=0 sp(7,Z)=36 sp(7,R)=24 sp(7,Red)=128 sp(7,Green)=128 sp(7,Blue)=128
'point origin of camera 'centre screen, 24 world units back from the image plane cam(X)=0 cam(Y)=0 cam(Z)=-24
'point origin of light source 'centre top, on the image plane lit(X)=12 lit(Y)=5 lit(Z)=-10
'now cast a ray through every pixel on the screen for Xp=0 to ImgX for Yp=0 to ImgY scan
'point0, the camera x,y,z o(X)=cam(X) 'world x camera 0 o(Y)=cam(Y) 'world y camera 0 o(Z)=cam(Z) 'world z camera -10
'point1, the screen x,y,z 'which is ScrXLeft (-12) + .5 * ScrX, the per pixel world increment value 'to which we add xp * ScrX to give world x in world units r(X)=ScrXLeft+.5*ScrX+Xp*ScrX 'world x r(Y)=ScrYTop+.5*ScrY+Yp*ScrY 'world y r(Z)=0 'world z image plane 0
'flat parrallell ray no perspective 'o(X)=r(X) 'o(Y)=r(Y) 'o(Z)=-10
'subtract point0 from point1 to get vector direction d() d(X)=r(X)-o(X) d(Y)=r(Y)-o(Y) d(Z)=r(Z)-o(Z)
'normalize it by dividing by its length to make a unit vector ie it sums to 1 l=sqr(d(X)*d(X)+d(Y)*d(Y)+d(Z)*d(Z)) d(X)=d(X)/l d(Y)=d(Y)/l d(Z)=d(Z)/l DistToScreen=l
'go look for an intersect null=raySphereIntersect() next 'y next 'x
wait
[quit] close #1 end
function raySphereIntersect() maxdist=1000000 for o=0 to maxObjects b=2*d(X)*(o(X)-sp(o,X))+2*d(Y)*(o(Y)-sp(o,Y))+2*d(Z)*(o(Z)-sp(o,Z)) c=(o(X)-sp(o,X))^2+(o(Y)-sp(o,Y))^2+(o(Z)-sp(o,Z))^2-sp(o,R)^2 d = b * b - 4 * c if d>0 then t=(b*-1 - sqr(b*b-4*c))/2 if t>DistToScreen then 'store the shortest intersect of all sphere intersects if t<maxdist then maxdist=t : id=o end if end if next if maxdist<1000000 then
'establish the sphere surface intersect point i(X)=o(X)+d(X)*maxdist i(Y)=o(Y)+d(Y)*maxdist i(Z)=o(Z)+d(Z)*maxdist
'get unit normal vector from sphere centre to surface intersect n(X)=(i(X)-sp(id,X))/sp(id,R) n(Y)=(i(Y)-sp(id,Y))/sp(id,R) n(Z)=(i(Z)-sp(id,Z))/sp(id,R)
'get the unit normal vector from sphere surface intersect to the light l(X)=lit(X)-i(X) l(Y)=lit(Y)-i(Y) l(Z)=lit(Z)-i(Z) l=sqr(l(X)*l(X)+l(Y)*l(Y)+l(Z)*l(Z)) l(X)=l(X)/l l(Y)=l(Y)/l l(Z)=l(Z)/l 'the dot product of these vectors gives an indication of the light color=n(X)*l(X)+n(Y)*l(Y)+n(Z)*l(Z)
'cast a ray from intersect to the light to check for shadow 'we have done most of the ray prep 'point0 is the intersect point1 is the light 'so l() is our ray direction, point1-point0, normalized shadow=1 for o=0 to maxObjects if o<>id then 'check all other spheres not the one we are currently considering b=2*l(X)*(i(X)-sp(o,X))+2*l(Y)*(i(Y)-sp(o,Y))+2*l(Z)*(i(Z)-sp(o,Z)) c=(i(X)-sp(o,X))^2+(i(Y)-sp(o,Y))^2+(i(Z)-sp(o,Z))^2-sp(o,R)^2 d = b * b - 4 * c if d>0 then t=(b*-1 - sqr(b*b-4*c))/2 if d>0 and t>0 then shadow=.8 : exit for end if next
'add some ambient light if color < .5 then color = .5
'color the pixel #1 "color ";sp(id,Red)*color*shadow;" ";sp(id,Green)*color*shadow;" ";sp(id,Blue)*color*shadow;" ; set ";Xp;" ";Yp
end if
end function
|
|
|
Post by B+ on Feb 11, 2021 18:54:43 GMT
Tweak cd= 24 + 45 ' for less contacts with ring bumpers, more brush-byes
Yeah! Actual physics can be quite detailed due to numbers being able to simulate reality to such a very fine degree! We know (no, they know) theoretically what Universe was like fractions of a second after it's birth!
But we don't need all that precision and accuracy to play with a "good enough" sim. After all, Newton's gravity was good enough for Napoleon's Artilleries, didn't need Einstein to do even more damage.
|
|
|
Post by B+ on Feb 12, 2021 17:57:04 GMT
So Rod how are the other bumpers coming along? I imagine the 4 peg bumpers are just like 2 ring only without the kicker effect. Might want to check collision a little further away than radius of bumper + ball, so ball isn't seen embedding itself into bumper.
|
|
|
Pinball
Feb 13, 2021 13:57:37 GMT
B+ likes this
Post by tsh73 on Feb 13, 2021 13:57:37 GMT
I am not in sprites but I happen to have got a library for 2d vectors (and vector is vector$ and stored just as "3 12" so you can "place ";vector$ - it probably can't get easier) With it, it is really easy to get "angle of incidence = angle of reflection" rule You have vector v$ falling to a plane base$ there are two functions vectTangent$(dv$,base$) vectNorm$(dv$,base$) which splits vector v$ into sum or orthogonal vectors Then mirroring, tangent part stays the same, while normal one negates. You can use vectScale$(a,v$) to scale vector by factor a - introducing a friction (a<1) or bumping things up (a>1)
So have a look. It really make things look easier. (circle to line collision taken from Andy's tutorial on collisions)
'collision of a ball ' with a unmoving plane ' with bigger unmoving ball '0. Just run a ball of size r '1. add a line, reflect from it '2. add a circle or size R, reflect from it '3 turn on both, loop
nomainwin global pi pi = acs(-1) 'randomize 0.5[div align="justify"][/div]
r=24: R=79
'open a window and graphicbox WindowHeight = 740 WindowWidth = 820 graphicbox #w.g, 0, 0, 800, 700 open "Pinball" for window_nf as #w #w "trapclose [quit]" #w.g "home; posxy cx cy" #w.g "down"
' now start the timer to repeatedly call the drawing loop dt = 56 'dt = 5
[restart] v$ = vect$(cx, cy/2) 'the small ball 'print v$ #w.g "place ";v$ #w.g "circle ";r
'add some speed - should not be more then "r" 'dx=rnd(0)*30+10 'dy=rnd(0)*10 dx=20 'dx=60 'this one is too big dy=10 dv$ = vect$(dx, dy) if vectLen(dv$)>2*r then timer 0 notice "Speed too big! Ball could run THROUGH obstacle without touching it" end if 'print dv$ 'print
'a line a = rnd(0)*pi/2 - 3*pi/4 rr= 0.5*cx x1 = 1/3*cx+rr*cos(a): y1=cy+rr*sin(a) x2 = 1/3*cx+rr*cos(a+pi): y2=cy+rr*sin(a+pi) base$=vectSub$(vect$(x1,y1),vect$(x2,y2))
'a circle cxx=5/3*cx cyy=cy cv$=vect$(cxx, cyy)
hit = 0 timer dt, [drawingloop]
'now sit and wait for the first event to trigger wait
[drawingloop] 'calc v$=vectAdd$(v$,dv$) x=vectX(v$) y=vectY(v$) if x<r or x>2*cx-r then dv$=vect$(0-vectX(dv$), vectY(dv$)) if y<r or y>2*cy-r then dv$=vect$(vectX(dv$), 0-vectY(dv$))
'goto [skipLine] 'reflection from line if c2L(x1, y1, x2, y2, x, y, r) then hit = 5 'frames hit line visible 'timer 0 'got a hit. Unwind back just before that dv0$=vectUnit$(dv$) '1 pixel do v$=vectSub$(v$,dv0$) loop while c2L(x1, y1, x2, y2, vectX(v$), vectY(v$), r) 'now, get reflection speed dv$=vectSub$(vectTangent$(dv$,base$), vectNorm$(dv$,base$)) end if
[skipLine] 'reflection from circle if vectLen(vectSub$(v$, cv$))<r+R then hit2 = 5 'frames hit circle visible dv0$=vectUnit$(dv$) '1 pixel do v$=vectSub$(v$,dv0$) loop while vectLen(vectSub$(v$, cv$))<r+R 'now, get reflection speed 'radius to radius, norm is norm$=vectSub$(v$,cv$) 'now just cwitch tangent and norm dv$=vectSub$(vectNorm$(dv$,norm$), vectTangent$(dv$,norm$)) end if
'print v$
'draw #w.g "cls" #w.g "place ";v$ #w.g "circle ";r
#w.g "place ";cv$ #w.g "circle ";R
#w.g "line ";x1;" ";y1;" ";x2;" ";y2
if hit>0 then #w.g "color red" #w.g "line ";x1;" ";y1;" ";x2;" ";y2 #w.g "color black" 'timer 0 hit = hit-1 end if
if hit2>0 then #w.g "color green" #w.g "place ";cv$ #w.g "circle ";R #w.g "color black" 'timer 0 hit2 = hit2-1 end if
wait
[keyboardinterrupt] 'this loop gets called every time a key is pressed wait
[quit] timer 0 close #w
Function Atan2(y,x) 'Atan2 is a function which determines the angle between points 'x1, y1 and x2, y2. The angle returned is in radians 'The angle returned is always in the range of '-PI to PI radians (-180 to 180 degrees) '============================================================== 'NOTE the position of Y and X arguments 'This keeps Atan2 function same as other language versions '============================================================== If x = 0 Then If y < 0 Then Atan2 = -1.5707963267948967 Else Atan2 = 1.5707963267948967 End If Else chk = atn(y/x) If x < 0 Then If y < 0 Then chk = chk - 3.1415926535897932 Else chk = chk + 3.1415926535897932 End If End If Atan2 = chk End If 'thanks Andy Amaya End Function
'------------------------------------ sub pause ms t0=time$("ms") while time$("ms")<t0+ms scan wend exit sub [quit] end sub
'==================================== 'vector 2d lib 'vectors as "x y" pairs, to be splitted by Word$
function vect$(x,y) vect$=x;" ";y end function
function vectX(v$) vectX=val(word$(v$,1)) end function
function vectY(v$) vectY=val(word$(v$,2)) end function
function vectLen(v$) x=val(word$(v$,1)) y=val(word$(v$,2)) vectLen=sqr(x*x+y*y) end function
function vectUnit$(v$) x=val(word$(v$,1)) y=val(word$(v$,2)) vectLen=sqr(x*x+y*y) vectUnit$=x/vectLen;" ";y/vectLen end function
function vectAdd$(v1$,v2$) x1=val(word$(v1$,1)) y1=val(word$(v1$,2)) x2=val(word$(v2$,1)) y2=val(word$(v2$,2)) vectAdd$=x1+x2;" ";y1+y2 end function
function vectSub$(v1$,v2$) x1=val(word$(v1$,1)) y1=val(word$(v1$,2)) x2=val(word$(v2$,1)) y2=val(word$(v2$,2)) vectSub$=x1-x2;" ";y1-y2 end function
function vectDotProduct(v1$,v2$) x1=val(word$(v1$,1)) y1=val(word$(v1$,2)) x2=val(word$(v2$,1)) y2=val(word$(v2$,2)) vectDotProduct=x1*x2+y1*y2 end function
function vectScale$(a,v$) 'a * vector v$ x=val(word$(v$,1)) y=val(word$(v$,2)) vectScale$=a*x;" ";a*y end function
function vectTangent$(v$,base$) n$=vectUnit$(base$) vectTangent$=vectScale$(vectDotProduct(n$,v$),n$) end function
function vectNorm$(v$,base$) vectNorm$=vectSub$(v$,vectTangent$(v$,base$)) end function
function vectAngle(v$) x=val(word$(v$,1)) y=val(word$(v$,2)) vectAngle=atan2(y,x) end function
function vectFromPolar$(rho, phi) vectFromPolar$=rho*cos(phi);" ";rho*sin(phi) end function
function vectRotate$(v$,alpha) x=val(word$(v$,1)) y=val(word$(v$,2)) rho=sqr(x*x+y*y) phi=atan2(y,x)+alpha vectRotate$=rho*cos(phi);" ";rho*sin(phi) end function
function dePi$(x) 'pure aestetics dePi$=x/pi;"Pi" end function
'--------------------------- function atan2(y,x) 'pi = asn(1) * 2 'global if x <> 0 then arctan = atn(y/x)
select case case x > 0 atan2 = arctan
case y>=0 and x<0 atan2 = pi + arctan
case y<0 and x<0 atan2 = arctan - pi
case y>0 and x=0 atan2 = pi / 2
case y<0 and x=0 atan2 = pi / -2 end select end function
'+++++++++++++++++++++++++++++++++++++++++++++++ 'from AndyAmaya JB Advanced Collision Tutorial 'https://jbfilesarchive.com/phpBB3/./viewtopic.php?f=5&t=1504
Function c2L(x1, y1, x2, y2, cx, cy, cr) '================================================================= ' Circle To Line Function '================================================================= ' This function checks if a circle has collided with a line ' ' c2L returns a 1 if the circle has collided with the line ' ' c2L returns a 0 (zero) if no collision has occurred '================================================================= ' x1, y1, x2, y2 are the two coordinates defining the line to check ' ' cx, cy are the coordinates of the center of the circle ' cr is the radius of the circle '=================================================================
d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) If d <> 0 Then d = ((cx - x1) * (x2 - x1) + (cy - y1) * (y2 - y1)) / d 'Clip To the line segments legal bounds If d < 0.0 Then d = 0 If d > 1.0 Then d = 1 dx = cx - ( (x2 - x1) * d + x1) dy = cy - ( (y2 - y1) * d + y1) c2L = (dx * dx + dy * dy <= cr * cr) End Function
|
|
|
Post by B+ on Feb 13, 2021 15:04:41 GMT
Nice demo tsh73!
We have the needed code for ball reflection off line, beautiful.
|
|
|
Post by tenochtitlanuk on Feb 13, 2021 15:35:29 GMT
Lovely demo Anatoly. I've never been into coding this kind of thing except as a coding challenge. I don't end up playing screen games. But am enjoying followin this thread ( and the chess stuff form you, B+, Rod..)
|
|
|
Pinball
Feb 13, 2021 17:55:07 GMT
via mobile
Post by Rod on Feb 13, 2021 17:55:07 GMT
Thanks for all those contributions I have still to study the code.
|
|