Collision physics

BlitzMax Forums/BlitzMax Programming/Collision physics

Arska(Posted 2015) [#1]
I need some help with physics for my game. I have 'asteroids like' physics in 2D space shooter game, so there is xspeed and yspeed variables for ships. Now i am trying to do collisions with asteroids, but i have no idea where to start. Problem is that ships have xspeed and yspeed, but so does the asteroids. I need some kind of way to make ship bounce from asteroids. My mathematic skills are not very good for this, so that's why i ask here.


Xerra(Posted 2015) [#2]
For the asteroids you could just have four fields within the type like

Xposition
Yposition
Xdirection=1
Ydirection=1

then if a check shows the current asteroid in the list being updated Xposition and Yposition match somewhere the ship would be - taking into account the size of the ship image, you could set Xdirection and Ydirection to -1.

Then your update loop could just be incrementing Xposition and Yposition + XDirection and YDirection and it would just start travelling in the opposite direction from the impact.

That needs a lot of fine tuning but gives you an idea of how to start perhaps?


Arska(Posted 2015) [#3]
Yes i have been thinking that kind of way to do it, but i haven't been able to figure out this situtation. If ship is stationary and asteroid collides with ship. With my tests i always end up inside of that asteroid and stuck.


Xerra(Posted 2015) [#4]
Don't forget that you need to take into the account the size of the images when you're doing the X and Y checks. If your screenmode is 640 * 480, your ship is 20 * 20 and each asteroid is 40*40 then to check asteroid hitting ship you would be reading through each asteroid and checking if X position of ship -60 or Y position of ship -60 = asteroids X or Y position then a bump on the edge of the asteroid has occurred so switch the asteroid to moving the opposite way, destroy it or whatever.

I know that doesn't explain it brilliant but it's kind of hard to explain without actually knocking up a bit of code which I can't do right now as i'm not at home. Have you looked in the archives for other blitz asteroid games and seen how they handle collisions? Seem to remember there was a nice one with BlitzMax when it first came out.


Arska(Posted 2015) [#5]
Collision isn't the problem. Calculating physics is.


zoqfotpik(Posted 2015) [#6]
Hope this here helps. I got this working without understanding it very well. This is actually Monkey code but should work under Blitzmax without all that much modification, but you should probably rewrite it yourself so you have at least *some* idea what's going on. This is someone else's code from this forum, can't remember whose but it's either in the archive or in the forum. Works real well.

There are a bunch of ways you can optimize this but worry about it later, this works plenty well enough for a
reasonable number of collisions and if you exceed that you're going to be entering exponential territory (20,000 x 20,000 objects starts to slow down) and then you're going to need to do some sort of binning, and if that's the case I can help you with that too but dividing the play area into quadrants, and doing collisions per quadrant, should solve the problem.

For Local c:=Eachin objectlist			
		c.update()
		'----------------------------------------------------
		' BEGIN PHYSICS BLOCK
		
		If c.mattertype <> 9  ' that is to say, noncollidable
		
		For Local c2:clickcircle = Eachin objectlist
	
			Local collisionDistance# = c.radius+c2.radius
			Local actualDistance# = Sqrt(Pow(c2.x-c.x, 2)+Pow(c2.y-c.y, 2))
			
			'Collided Or Not?
			If actualDistance<collisionDistance And c2.mattertype <> 2 Then
				
				Local collNormalAngle#=ATan2(c2.y-c.y, c2.x-c.x)

				'Position exactly touching, no intersection 
' here if I remember right it is moving the two circles directly away from each other so they are exactly touching but not actually intersecting
				Local moveDist1#=(collisionDistance-actualDistance)*(c2.mass/Float((c.mass+c2.mass)))
				Local moveDist2#=(collisionDistance-actualDistance)*(c.mass/Float((c.mass+c2.mass)))
				c.x=c.x + moveDist1*Cos(collNormalAngle+180)
				c.y=c.y + moveDist1*Sin(collNormalAngle+180)
				c2.x=c2.x + moveDist2*Cos(collNormalAngle)
				c2.y=c2.y + moveDist2*Sin(collNormalAngle)
				
				
				'------------------------------------------
				'COLLISION RESPONSE
				'------------------------------------------
				'n = vector connecting the centers of the circles.
				'we are finding the components of the normalised vector n
				Local nX#=Cos(collNormalAngle)
				Local nY#=Sin(collNormalAngle)
				
				'now find the length of the components of each movement vectors
				'along n, by using dot product.
				Local a1# = c.vx*nX  +  c.vy*nY
				Local a2# = c2.vx*nX +  c2.vy*nY
				
				'optimisedP = 2(a1 - a2)
				'             ----------
				'              m1 + m2
				Local optimisedP# = (2.0 * (a1-a2)) / (c.mass + c2.mass)
				
				'now find out the resultant vectors
				'r1 = c1.v - optimisedP * mass2 * n
				c.vx = c.vx - (optimisedP*c2.mass*nX)
				c.vy = c.vy - (optimisedP*c2.mass*nY)
				
				'r2 = c2.v - optimisedP * mass1 * n
				c2.vx = c2.vx + (optimisedP*c.mass*nX)
				c2.vy = c2.vy + (optimisedP*c.mass*nY)
				
				

			End If
			

		Next
		'---------------------------------------
		
		Endif
		
		Next



zoqfotpik(Posted 2015) [#7]
Here's another quick and dirty way.

For each object, keep track of the position before adding the velocity in, that is oldx and oldy.

If objects are colliding, figure out the angle between them using arctan, or just use the slope between the two objects.

The depth of collision is: (radius1+radius2 + dist(x1,y1,x2,y2))

Your vector is collisiondepth at the above angle.

Subtract 1/2 the vector from the position of one object and add 1/2 the vector to the position of the other object.

If you don't know this stuff, you need to learn it if you want to do game development. The good news is, this particular set of math (in this comment and the previous) is almost all you need, and writing an asteroids game with collisions is the best way to learn it.


Arska(Posted 2015) [#8]
This is very confusing to me and you are right, i need to study more trigonometry. Well i have come up something simple:
' Player collision to asteroid
If TPlayer.list
	For Local Ship:TPlayer = EachIn TPlayer.list
		If Distance( X, Y, Ship.X, Ship.Y ) < Radius
			
			Local collisionAngle:Float = AngleTO:Float( x, y, Ship.X, Ship.Y )
			
			' Change direction of player
			Ship.XSpeed = Ship.XSpeed / -1
			Ship.YSpeed = Ship.YSpeed / -1	
			
			' If asteroid is small change also it's direction
			If size = SMALL Then
				XSpeed = XSpeed / - 1
				YSpeed = YSpeed / - 1	
			EndIf
			
		EndIf
	Next
EndIf


Currently it's just makes xspeed and yspeed negative so there is illusion of bounce, but i need to make it bounce for real. So i have those variables to use in this.

'AngleTO' function:
Function AngleTO:Float(X1:Float,Y1:Float,X2:Float,Y2:Float)
	Local dx:Float=x2-x1
	Local dy:Float=y2-y1
	Local Angle:Float=(ATan2(dy,dx)) + 180
	
	If Angle>0 Then
		While(Angle>= 360)
			Angle:-360
		Wend
	ElseIf Angle<0 Then
		While(Angle<0)
			Angle:+360
		Wend
	End If
	Return Angle 
End Function


Am i even in right path?


zoqfotpik(Posted 2015) [#9]
It's something that you're going to need to poke at and prod at until you get it working to your satisfaction. That will be the process of learning it. We can't just explain it because to understand you will have to learn the material :) I would suggest that you ignore the "exactly touching" part of the math because that's hairiest and unnecessary-- just do "if collided, don't actually add the velocity to the position and just go ahead and do collision response anyway." Nowadays ticks are moving fast enough that you shouldn't need to have objects moving in huge steps anyway.

The best thing I can suggest is to look at that code snippet I posted and really dissect it until you can see what's going on.

You're definitely going in the right direction with atan and all the annoying modulos that you have to do. At some point, either with this or something else, you will run into angle-related things that will have to happen differently depending on which of the four quadrants the angle falls into.

Since this is an asteroids game, you have to ask yourself how physically precise the collisions really need to be anyway. If the roids are irregularly shaped, there can be a pretty good degree of random slop and have it still look pretty realistic.

At some point in an asteroids game, if you are using vector graphics, you are going to want to do line-wise collision detection. You do that by finding one of the line intersection tests in the code archives, getting that working with two lines, and then loop through each of the line segments in the two colliding objects to see which line segments crossed, if any.

But don't worry about that right now :) Do some simple billiards test until you figure out how it works.

Your code said this:
Ship.XSpeed = Ship.XSpeed / -1
Ship.YSpeed = Ship.YSpeed / -1


The problem with that is, you're going to find that a lot of the time, that method of bouncing is going to yield an extremely unrealistic result. Think about it, imagine if you were bouncing a ball off a wall and it always bounced off in exactly the reverse of the angle it was moving when it hit the wall! You're better off just getting some thrust vector from your arctan angle, and then adding that thrust vector to your ship velocity vector. That'll make it at least SOMEWHAT realistic.

EASIEST approach is to see which of four quadrants the asteroid is in with relation to your ship (if it's to my upper left, do this... if it's to my upper right do this...) while remembering to take into account if it's directly above or to the side of you. If you can do that, you should be able to get a general idea of what direction the bounce should take, and add that, plus some random factor, to your ship velocity vector.

http://www.education.com/science-fair/article/linear-momentum-find-perfect-90/

Here's one hint if you want to take the more physical approach.

Add 90 degrees to your collision angle that you got with atan. That line is the line that your ball is going to REFLECT OFF OF, just the way a billiard would bounce off a wall in pool. But then you're going to need to add some portion of the velocity of the asteroid to your ship, and vice versa-- say 40% of the velocity of each gets kicked off onto the other, and the remaining 20% goes away as heat, acting as a damping factor. If you don't have damping factors in physics simulations things tend to go totally haywire really fast.

You should decide if you want game physics or realistic physics. If you want realistic physics you need to make an effort to get to grips with some of the math, so if you just want a working game, do something quick and dirty and worry about perfecting it later (that would be my advice.) Then again, this is pretty easy math and you should have it in a few days if that.

One last suggestion I'll just throw out there is to make SURE you know what you are doing with your ints and floats here because if you don't you can end up with some extremely strange behavior and not know why.


Arska(Posted 2015) [#10]
Before your last post i had come up something like this. It looks pretty realistic now. Any problems with this approach?
' Player collision to asteroid
If TPlayer.list
	For Local Ship:TPlayer = EachIn TPlayer.list
		If Distance( X, Y, Ship.X, Ship.Y ) < Radius
			
			Local collisionAngle:Float = AngleTO:Float( x, y, Ship.X, Ship.Y )
			
			
			Ship.XSpeed:+ ( ( - Cos(collisionAngle) / 2) * Delta.Time) * DeltaMultiplier
			Ship.YSpeed:+ ( ( - Sin(collisionAngle) / 2) * Delta.Time) * DeltaMultiplier
			
			' If asteroid is small change also it's direction
			If size = SMALL Then
				XSpeed:+ ( ( Cos(collisionAngle) / 2) * Delta.Time) * DeltaMultiplier
				YSpeed:+ ( ( Sin(collisionAngle) / 2) * Delta.Time) * DeltaMultiplier	
			EndIf
			
		EndIf
	Next
EndIf



zoqfotpik(Posted 2015) [#11]
That's about right :) Now implement the following:

* get them rotating in the right direction after collision. Is the velocity vector closer to the collisionangle or is it closer to collisionangle+90? That will tell you whether the collision is more direct or more tangential.

* give them mass and have them respond appropriately to collisions based on mass-- dividing the velocity by 2 is what you do if the mass is 50/50, what if the mass is 60/40 or 90/10?

* get an exact point of collision. One easy way to do this if you don't want to deal with the math is this:
if newx, newy collides, start with oldx, oldy. Divide your velocity vector by, say, ten, and then add that vector to oldx, oldy until you have a collision.

As you can see it's really not all that hard and you underestimate your own ability, the math is pretty easy from where you are standing right now.


Arska(Posted 2015) [#12]
Hmm.. One problem came up. I implemented same collisions to asteroid to asteroid collision. I looks like asteroids are building up enormous speeds. I don't want that there comes 'random energy' somewhere. Energy should be reducing because of 'heat' or something you said.


zoqfotpik(Posted 2015) [#13]
Xspeed = xspeed + such and such * delta / dampingfactor, which might be 1.1 or something.


Arska(Posted 2015) [#14]
' Asteroid collision to asteroid
		If TRock.List
			For Local Rock2:TRock = EachIn TRock.List
				If Rock2.id <> id Then
					
					If ImagesCollide(Image, x, y, 0, Rock2.image, Rock2.X, Rock2.Y, 0)
						Local collisionRockAngle:Float = AngleTO:Float( x, y, Rock2.X, Rock2.Y )
						
						Rock2.XSpeed:+ (( ( - Cos(collisionRockAngle) / 2) * Delta.Time) * DeltaMultiplier) / 2
						Rock2.YSpeed:+ (( ( - Sin(collisionRockAngle) / 2) * Delta.Time) * DeltaMultiplier) / 2
						
						If size < rock2.size Then
							XSpeed:+ ( ( ( Cos(collisionRockAngle) / 2) * Delta.Time) * DeltaMultiplier) / 2
							YSpeed:+ ( ( ( Sin(collisionRockAngle) / 2) * Delta.Time) * DeltaMultiplier) / 2
						EndIf
					EndIf
					
				EndIf
				
			Next
		EndIf


It is still building very much speed when collision occurs. :/ Maybe because in start they may be overlapping and speed increasing stops when it get out...


zoqfotpik(Posted 2015) [#15]
Try this:
XSpeed:+ ( ( ( Cos(collisionRockAngle) / 4) * Delta.Time) * DeltaMultiplier)

and see if that drastically decreases the speed on impact. If not you have something else going on.


Arska(Posted 2015) [#16]
Hmm... Now it's working fine, but sometimes other object bounces in wrong direction. Something wrong with increasing X and YSpeed.

Edit: Looks like those asteroids only want to be pushed from left to right and up to down. If collision comes from down, it won't bounce up.


Arska(Posted 2015) [#17]
Nevermind that was my fault and not in codes above. Looks good now. Thank you very much zoqfotpik.


zoqfotpik(Posted 2015) [#18]
Could you post a small demo of your code in the code archive?


Arska(Posted 2015) [#19]
I added it. Here you http://www.blitzbasic.com/codearcs/codearcs.php?code=3197

I used Tibit's "How to make Asteroids in an hour or so" -tutorial for this example.