## More Collision (again)

I can understand you not wanting to throw out code that you have already written and start again from scratch, but...

>>

Ok I have one last problem and my collision should work. here's the code:

<<

Im not sure your code is going to work because you arent attacking the problem fully. I dont think its simply a matter of rejiggering your code to fix 'one last thing' - unless you are planning to only test the front pt and back pt of the laser for collision and leave it at that.

>>

As you may notice the bullet[j].y -45 does not rotate with [self laser] so when you rotate the ground bullet[j].y -45 is to the left or right of the actual laser (instead of being behind it). What kind of formula do I need to calculate bullet[j].x and bullet[j].y as always being -45 behind the space ship?

<<

right - b/c you should do all your collisions in *world* coordinates. You are mixing object coordinates and world coordinates.

>>

distance1b = sqrt(((bullet[j].x - rockx1[k])*(bullet[j].x - rockx1[k])) +

(((bullet[j].y - 45) - rocky1[k])*((bullet[j].y -45) - rocky1[k])));

<<

You do *not* have to use oop if you dont want to. I think its the bees knees personally but YMMV. Define structs. bullet[j].x - rockx1[k] ??? These are two points and they are defined differently - why? You are asking for many many headaches Ice. Define Point/Vector/Matrix structure/objects types and use them consistently. Then define some functions/methods for adding vectors, subtracting points, dot products, cross products etc... You need to have this library down before you can go forward. I know you want to just dive right in to the code - but your code will just get more and more confusing. Define a method/function that takes two points and gives you the distance between them - then you can use that function over and over instead of copying and pasting the formula over and over for each situation.

Even better download one of the many many math libraries and use that. If you dont want to use oop - download PLIB and use the SG library http://www.woodsoup.org/projs/plib/sg/index.html and look at the SG sourcecode (sg.h, sg.cxx files). All the math is there in regular non-oop c. If you do want to use oop - Eberly's free-magic is an excellent c++ library (thats what I use), or if you want objective-c check out 3dkit (used to be at 3dkit.org - but that isnt responding for me right now so I cant be sure of the url)

Eberly has two versions. Free-magic which is free to anyone (you must give him some credit in the about... dialog) - or wild-magic (which adds a scene renderer) which you have to purchase his book to use. Free-magic has lots of useful 'find' methods - as in find the first point of collision between a ray and a circle.

>>

P.S. The reason I'm moving the ground is because I'm using a nanosaur type camera view.

<<

moving the ground around the camera - or moving the camera around the ground... same thing, same math.

OK - so how would I solve this problem?

Option 1: The correct way (but harder)

In my laser object/struct I would store a point for the location of the laser (the beginning part - in world coords), a unit vector for its direction (in world coords), a scalar for its length, a vector for its velocity. Each frame I would update the point for the location by += it with its velocity vector. A rock object/struct would have a point for the center of its location (in world coords), a scalar for its radius, a vector for its velocity. To collide them I would use a method/function that returned the first point of intersection between a ray (defined by the laser location point and the unit vector for its direction) and a circle (using the rock's center point and radius). If there is an intersection point - and the distance to it from the laser location point is <= the laser's length then I would have a hit.

Or - I could use the gamasutra article I linked to earlier. It showed how to collide between two moving spheres/circles. A laser is just a fast moving circle with a tiny (or zero) radius. This would work better since it is a more generic solution - and accounts for movement between frames.

But to do the ray/circle test you need to understand parametric equations for the ray/circle test. The only thing you need to understand the gamasutra article is some point/vector math including dot-product.

In fact what I would do - is make an abstract class called generic_sprite_object which stored location, direction, etc... and all the methods to manage and collide that - and then derive my lasers, rocks, and ships, from that abstract class.

Option 2; The easy way and still mostly accurate.

While I still would draw my laser as one object on screen - mathematically I would keep it as a set of points. Each laser shot would store 20 points (in world coords) along the length of the shot - then do a simple 'is this point inside the circle' test for all 20 points. Use enough points that a rock cant fit between - the more points, the more accurate the collision test will be - but the more cpu the collision test will take. It wont slow you down as much as you think - point-within-a-circle test is pretty fast and you can afford to do many of them. No matter how many points you test for 10, 20, 40, 1,000 - you can still technically miss a collision that should of taken place - but you quickly get to the point where you are accurate enough that no one will notice or care.

- or - you can keep hacking away at the code you have. Maybe you will find a solution that I cant see. Good luck,

hth,

Codemattic

As you may notice the bullet[j].y -45 does not rotate with [self laser] so when you rotate the ground bullet[j].y -45 is to the left or right of the actual laser (instead of being behind it). What kind of formula do I need to calculate bullet[j].x and bullet[j].y as always being -45 behind the space ship?

<<

what I mean by world vs object coordinates is that with respect to the laser the vector (0,-45) always points from the back to the tip of the laser - wrt the laser's coords. However in world coords - the laser might be pointed in any direction, and wrt the world's coords its a different vector each time. A quick way to transform the vector into world coords is to use the 2d rotation formulas.

x' = (x*cos(theta))+(y*sin(theta))

y' = (-x*sin(theta))+(y*cos(theta))

where theta is the direction the laser is pointed. The vector (x',y') will now get you from the back to the tip of the laser in world coordinates.

Also - this might be clearer. If you d/l the plib library and look at the sg code - its made to model opengl functions as closely as possible. So make a vector (0,-45,0). Then make an equivalent transformation matrix to what you are using to transform the laser. Then use sgXformVec3 to transform the vector from object to world coords.

hth,

Codemattic

2d ray-circle collision is relatively easy:

given a parametric line:

{x0+t*x1, y0+t*y1} (t=0 to 1)

and there is a circle at the origin of radius r

the line to the center of the circle is the same as the line's position:

{x0+t*x1, y0+t*y1}

The length of this vector squared is:

(x0+t*x1)^2 + (y0+t*y1)^2

= x0^2 + y0^2 + x0*t*x1 + y0*t*y1 + (t*x1)^2 + (t*y1)^2

= x0^2 + y0^2 + t*(x0*x1 + y0*y1) + t^2 * (x1^2 + y1^2)

To find the single local minima for this function, we take the derivative:

x0*x1 + y0*y1 + 2*t*(x1^2 + y1^2)

and solve for zero:

x0*x1 + y0*y1 + 2*t*(x1^2 + y1^2) = 0

t = -(x0*x1 + y0*y1) / (2*(x1^2 + y1^2))

calculate this and check if it's between 0 and 1. If it is, plug it into the length squared formula and check if the reslt is less than r^2. If it isn't, check if the endpoints are inside the circle.

now, more useful is the ray intersection test. that means:

x0^2 + y0^2 + t*(x0*x1 + y0*y1) + t^2 * (x1^2 + y1^2) = r^2

x0^2 + y0^2 - r^2 + t*(x0*x1 + y0*y1) + t^2 * (x1^2 + y1^2) = 0

To solve this you use the quadratic equation, which I can't remember off the top of my head right now. It gives you a quicker check for line intersection by just testing whether what's inside the radical is >0. If it is, you have two solutions - one where the line enters, the other where it exits. Figuring out which is which is fairly trivial.

given a parametric line:

{x0+t*x1, y0+t*y1} (t=0 to 1)

and there is a circle at the origin of radius r

the line to the center of the circle is the same as the line's position:

{x0+t*x1, y0+t*y1}

The length of this vector squared is:

(x0+t*x1)^2 + (y0+t*y1)^2

= x0^2 + y0^2 + x0*t*x1 + y0*t*y1 + (t*x1)^2 + (t*y1)^2

= x0^2 + y0^2 + t*(x0*x1 + y0*y1) + t^2 * (x1^2 + y1^2)

To find the single local minima for this function, we take the derivative:

x0*x1 + y0*y1 + 2*t*(x1^2 + y1^2)

and solve for zero:

x0*x1 + y0*y1 + 2*t*(x1^2 + y1^2) = 0

t = -(x0*x1 + y0*y1) / (2*(x1^2 + y1^2))

calculate this and check if it's between 0 and 1. If it is, plug it into the length squared formula and check if the reslt is less than r^2. If it isn't, check if the endpoints are inside the circle.

now, more useful is the ray intersection test. that means:

x0^2 + y0^2 + t*(x0*x1 + y0*y1) + t^2 * (x1^2 + y1^2) = r^2

x0^2 + y0^2 - r^2 + t*(x0*x1 + y0*y1) + t^2 * (x1^2 + y1^2) = 0

To solve this you use the quadratic equation, which I can't remember off the top of my head right now. It gives you a quicker check for line intersection by just testing whether what's inside the radical is >0. If it is, you have two solutions - one where the line enters, the other where it exits. Figuring out which is which is fairly trivial.

"He who breaks a thing to find out what it is, has left the path of wisdom."

- Gandalf the Gray-Hat

Bring Alistair Cooke's America to DVD!

note: xpos and ypos are the vectors of xtrans and ytrans.

laserx = -xtrans;

lasery = -ytrans;

laserz = 0.0;

laserxv = -xpos + 15.0 * sin(2 * M_PI * angle / 360.0);

laseryv = -ypos + 15.0 * cos(2 * M_PI * angle / 360.0);

laserx += laserxv;

lasery += laseryv;

glRotatef(angle, 0.0, 0.0, 1.0);

glTranslatef( xtrans, ytrans, -14.9); // ground movement

glTranslatef( laserx, lasery, laserz);

glRotatef(angle, 0.0, 0.0,-1.0);

[self laser];

Replacing angle with 0 and removing the glRotatefs almost fixes the problem except that it looks like the lasers are following the ship instead of flying off in the direction they were shot at originally. What do I need to add or subtract to get the glRotatef(angle, 0.0, 0.0,-1.0); in the vectors or wherever they need to be? B.t.w. codematic I couldn't find any source code (except for some header code) for that plib thing.

Thanks,

Iceman

As for your code - I dont really understand all of it (why two rotates? for example). But I outlined earlier how I would write the code. If you want to post your code I will take a look. Remember - I solved the last problem you had with getting multiple shots to work - but it was only after looking through the source code that I could see where the problem was - I never would of got it with snippets alone.

I should warn you that one of the things Im working on is something vaguely similar - except its more 2d and an overhead view.

cheers,

Codemattic

Iceman