Target within Field of View
I am trying to make it so that in 2d in an asteroids-like environment there's this ship and it has a special weapon, when used it will pick an enemy within its field of view(An outward arc?) and fire a homing missile. I have a question about how to 'generate' this field of view and pick the closest target within it. What would be the most simplest method to acomplish this?
Thanks
Well using some vector math it should be simple. A field of view in a simple 2D sense is just a maximum angle from the current facing direction.
If you need to calculate the shipFacingVector from an angle just rotate a point at (1,0) by your ship angle.
vector shipToEnemy = enemyPosition - shipPosition;normalize(shipToEnemy);// angle between the facing and the shipfloat angle = acos(dotProduct(shipToEnemy, shipFacingVector));if (angle < SHIP_FOV) { // the ship is in the angle}
If you need to calculate the shipFacingVector from an angle just rotate a point at (1,0) by your ship angle.
The center of the boresite is the ships direction (normally unless its rear firing or a side gun). That direction angle is the 'course' in the xy coordinate system. (usually use of ATAN(dy,dx) )
You then calculate the direction from the ships center to its target's coordinates and get that 'aim angle' and compare the angles (you may need to adjust for wrap to find the minimum angular difference ie - 5 degrees vs 350 degrees is 15 degree difference and not 350-5 = 345 degrees.....
Optimization of candidate targets was usually a box search on the 2d map to cull the potential target list.
When I did this years ago the 'course' calculations were already done for other things like limiting movement turn radius (maximum deflection of the current course) and simple manual controls (adding increments to the current course on button pressing to change the 'course').
It just occured to me that you might want logic to decide between multiple target which are within. the weapons boresite...
--------------------------------------------[size="1"]Ratings are Opinion, not Fact
Quote: Original post by BlodBath
Well using some vector math it should be simple. A field of view in a simple 2D sense is just a maximum angle from the current facing direction.vector shipToEnemy = enemyPosition - shipPosition;normalize(shipToEnemy);// angle between the facing and the shipfloat angle = acos(dotProduct(shipToEnemy, shipFacingVector));if (angle < SHIP_FOV) { // the ship is in the angle}
That's basically right, but there's no need to compute the acos(), which is by far the most expensive operation in that code. Instead, compute the cosine of the threshold angle (once, offline, since this is a constant) and compare the dot product to that. Not that for this particular game and modern hardware this is going to matter, but you should try not to do needless expensive computations.
Quote: If you need to calculate the shipFacingVector from an angle just rotate a point at (1,0) by your ship angle.
The result of that is simply (cos(angle),sin(angle)). However, if you can, try to not use angles at all. The math is much simpler if you do everything with vectors, and it generalizes better to 3D too.
How else can I do this without angles? Right now it's not working, no matter what I set 'FOV_ARC' right now it encompasses about 180 degrees to the right of the ship's facing(I realize this is because I don't take into account the other side but still.).
This is the function I use to get the difference right now, I used acos and atan, atan seems to work 'more' than acos, when I use acos it seems to work on random occasions.
This is the function I use to get the difference right now, I used acos and atan, atan seems to work 'more' than acos, when I use acos it seems to work on random occasions.
float Object::GetDifference(b2Vec2 pos2){ float angle = body->GetAngle(); b2Vec2 facing(sin(angle), cos(angle)); b2Vec2 displacement = pos2 - body->GetPosition(); float difference = atan2(b2Cross(facing, displacement), b2Dot(facing, displacement)); // acos(dotProduct(displacement, facing)); return difference;}if(difference < FOV_ARC){//do stuff}
Difference = vector between the two objects?
In world space d = p2-p1
In object local space dloc = [d.f, d.r, d.u]
... where f, r and u are the object's forward, right and up vectors respectively. For the 2D case you can just ignore the Z component.
But, your difference is a scalar, so idk what it is. If it is an angle you need to normalise the difference vector before you use a trig function. I would recommend using the cross product to find the angle as that tells you which way you need to turn as well (the dot product does not).
cross = norm(dloc)×[1, 0, 0] (=[0, d.u, -d.r] I think)
or
cross = norm(d)×f (in world space; you then need to transform to local space like with d: crosslocal = [cross.f, cross.r, cross.u])
You can test with
if(cross.Mag < sin(maxAngle)) { ... }
... and the sign on the components of cross tell you which way to turn. For the 2D case you only need the Z component of cross which tells you the yaw difference of your current facing and the target; the Y component tells you the pitch difference which will be zero.
In world space d = p2-p1
In object local space dloc = [d.f, d.r, d.u]
... where f, r and u are the object's forward, right and up vectors respectively. For the 2D case you can just ignore the Z component.
But, your difference is a scalar, so idk what it is. If it is an angle you need to normalise the difference vector before you use a trig function. I would recommend using the cross product to find the angle as that tells you which way you need to turn as well (the dot product does not).
cross = norm(dloc)×[1, 0, 0] (=[0, d.u, -d.r] I think)
or
cross = norm(d)×f (in world space; you then need to transform to local space like with d: crosslocal = [cross.f, cross.r, cross.u])
You can test with
if(cross.Mag < sin(maxAngle)) { ... }
... and the sign on the components of cross tell you which way to turn. For the 2D case you only need the Z component of cross which tells you the yaw difference of your current facing and the target; the Y component tells you the pitch difference which will be zero.
You definitely need to normalize the displacement vector. Also, are you sure (sin(angle),cos(angle)) really is where your ship is pointing? This depends entirely on what conventions you are using for coordinates, for what angle is 0 and for whether the angle is expressed clockwise or counterclockwise.
It would be helpful if you could extract specific numbers of a situation where you think the computation is not working, so we can go over them.
It would be helpful if you could extract specific numbers of a situation where you think the computation is not working, so we can go over them.
I half understood your post Janova, what most confused me was the equations with 'loc/local', how to get it.
The angle goes clockwise, 0 being up.
So far I use it on two occasions, on one it works on the other it doesn't.
The angle goes clockwise, 0 being up.
So far I use it on two occasions, on one it works on the other it doesn't.
float Object::GetDifference(b2Vec2 pos){ float angle = body->GetAngle(); b2Vec2 facing(sin(angle), cos(angle)); b2Vec2 displacement = pos - body->GetPosition(); facing.Normalize(); displacement.Normalize(); float difference = atan2(b2Cross(facing, displacement), b2Dot(facing, displacement)); return difference;}//This one works. void ObjectHoming::FaceTarget(){ const float THRESHOLD = DegToRad(0); //Accuracy float difference = GetDifference(target); if(difference > THRESHOLD) Rotate(-ROT_SPEED); else if(difference < THRESHOLD) Rotate(ROT_SPEED); body->SetLinearVelocity(GetDirection(MOVE_SPEED));}//This one doesn't work.bool ObjectEnemy::FaceTarget(){ const float THRESHOLD = DegToRad(0); const float ACCURACY = DegToRad(5); float difference = GetDifference(target); if(difference > THRESHOLD) Rotate(-ROT_SPEED); else if(difference < -THRESHOLD) Rotate(ROT_SPEED);/*I think the problem is here with my use of difference to try and do an 'Act if facing close enough'.*/ if(difference < ACCURACY && difference > -ACCURACY) return true; return false;}
You have two coordinate systems that you're interested in: the world space (un-subscripted variables), and the local coordinate space of the object whose field of view you're interested in (subscripted with loc or local). To get from one coordinate system to another, you use the vector dot product of the vector in one system with the axes of the other (f, r and u for the forward, right and up axes).
Actually for a right handed coordinate system I think you want forward, left and up for local x,y,z?
So vloc = [ v.f, v.l, v.u ] for any vector in world space v. I.e. if the local coordinates of the object are [0,1,0], [-1,0,0] and [0,0,1] (in world space), so it is rotated by 90° to the world coordinates, vloc = [ vy, -vx, vz ].
In order for the object to make an intelligent decision it must know the target's position in its local coordinate system, so you need to transform the difference vector d by this mechanism before doing the check on it.
Once you have the difference vector in local coordinates, then what you are trying to do is find out what angle it subtends with the local forward axis. You can find the magnitude of this angle with the dot product (d here is the local difference vector):
cos |a| = norm(d).[1,0,0] = dx÷|d|
But to find the direction of that angle you need the cross product, which gives you the axis about which the angle turns, and also its sign. The vector
q = norm(d)×[1,0,0] = [0, -dz, dy]÷|d|
... has a magnitude equal to sin a, and a is an anticlockwise rotation if viewed along the vector q (i.e. looking from -q to local origin).
For a 2D case, q will always be of the form [0,0,sin a] and therefore sin a = dy = D.l where D is the world-space difference vector and l the object's local Y axis (left vector).
Thus:
... should work if I followed all the logic through correctly.
I think it's helpful to understand the 3D logic behind that though, and possibly even implement it in the general way.
I don't see anywhere in your code where you are finding anything in local coordinates. However, while (imo) inefficient, finding ([d×f]z ÷ d.f) should correctly give you the tangent of the angle, even in world space. I'm not quite sure what your b2Cross is doing (cross product isn't really defined on 2D vectors), but I'm assuming it returns the value that would be the Z component in a 3D cross product. So, I don't really understand why your solution isn't working.
Actually for a right handed coordinate system I think you want forward, left and up for local x,y,z?
So vloc = [ v.f, v.l, v.u ] for any vector in world space v. I.e. if the local coordinates of the object are [0,1,0], [-1,0,0] and [0,0,1] (in world space), so it is rotated by 90° to the world coordinates, vloc = [ vy, -vx, vz ].
In order for the object to make an intelligent decision it must know the target's position in its local coordinate system, so you need to transform the difference vector d by this mechanism before doing the check on it.
Once you have the difference vector in local coordinates, then what you are trying to do is find out what angle it subtends with the local forward axis. You can find the magnitude of this angle with the dot product (d here is the local difference vector):
cos |a| = norm(d).[1,0,0] = dx÷|d|
But to find the direction of that angle you need the cross product, which gives you the axis about which the angle turns, and also its sign. The vector
q = norm(d)×[1,0,0] = [0, -dz, dy]÷|d|
... has a magnitude equal to sin a, and a is an anticlockwise rotation if viewed along the vector q (i.e. looking from -q to local origin).
For a 2D case, q will always be of the form [0,0,sin a] and therefore sin a = dy = D.l where D is the world-space difference vector and l the object's local Y axis (left vector).
Thus:
float Angle2D(Vector2 deltaPos, float angle){ Vector2 left(-sin(angle), cos(angle)); return asin(b2Dot(deltaPos.normalise(), left));}
... should work if I followed all the logic through correctly.
I think it's helpful to understand the 3D logic behind that though, and possibly even implement it in the general way.
I don't see anywhere in your code where you are finding anything in local coordinates. However, while (imo) inefficient, finding ([d×f]z ÷ d.f) should correctly give you the tangent of the angle, even in world space. I'm not quite sure what your b2Cross is doing (cross product isn't really defined on 2D vectors), but I'm assuming it returns the value that would be the Z component in a 3D cross product. So, I don't really understand why your solution isn't working.
Well let's see, this is all in radians. I noticed that if I keep rotating say clockwise it'll reach 3.14 and go on 3.15, 3.16, 3.17... instead of turning -3.14. That might contribute to it not working but anyway. So far I have.
To start I am just trying to get the difference between the ship's facing and to find out which way I have to rotate to get there withing that threshold. I went back to the original suggestion.
To start I am just trying to get the difference between the ship's facing and to find out which way I have to rotate to get there withing that threshold. I went back to the original suggestion.
bool ObjectShip::FaceTarget(){ const float THRESHOLD = DegToRad(10); b2Vec2 shipToEnemy = mTarget - mBody->GetPosition(); b2Vec2 shipFacingVector = mBody->GetLinearVelocity(); shipToEnemy.Normalize(); shipFacingVector.Normalize(); float angle = acos(b2Dot(shipToEnemy, shipFacingVector)); if(angle > THRESHOLD) Rotate(-ROT_SPEED); if(angle < THRESHOLD) Rotate(ROT_SPEED); return true;}inline float32 b2Dot(const b2Vec2& a, const b2Vec2& b){ return a.x * b.x + a.y * b.y;}
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement