working with negative angle in a canvas game - math

I am making a top view medieval game using HTML, JS and canvas.
The problem is that my player is able to hit to others when not looking at them. I made a condition in the third line in the below code to address this problem, but that is not working because the player's look angle limits are max 180 and min -180. The limits between the player's position and victim's position are max 90 min -270. These limits ruin my condition.
I can accept any ideas or readymade condition or code.
if(Math.sqrt(Math.pow((this.X-Victim.X),2),Math.pow((this.Y-Victim.Y),2)) <= 100){//<-here calculates distance between victim and player
var angle = Math.atan2(this.X - Victim.X , this.Y - Victim.Y) * (180 / Math.PI);//<-here calculates angle between victim and player
if((angle < this.LookAngle + 45)&&(angle > this.LookAngle - 45)){//<- i need help on here
console.log(angle,this.LookAngle);
//socket.emit('HitTo',{UserName:Victim.UserName,hitval:20});//<- actually you dont need to know this
}
}

answering my own question
first i will flip my angle vertically because its must be reverse. go to source
180-angle
then i must convert the (look angle) and (angle between 2 player) to 0-360. go to source
(angle + 360) % 360
then i need to check the angle between in other two angle. go to source
var isbetween = (startangle,midangle,endangle) => {
endangle = (endangle- startangle) < 0 ? endangle - startangle + 360 : endangle - startangle;
midangle = (midangle - startangle) < 0 ? midangle - startangle + 360 : midangle- startangle;
return (midangle < endangle);
}
and applying to my code
if(Math.sqrt(Math.pow((this.X-Victim.X),2),Math.pow((this.Y-Victim.Y),2)) <= 100){
var angle = Math.atan2(this.X - Victim.X , this.Y - Victim.Y) * (180 / Math.PI);
var flippedangle = 180 - angle;
var fixedangle = (flippedangle + 360) % 360;
var fixedlook = (this.LookAngle + 360) % 360;
if(((fixedangle - (fixedlook + 80)) < 0 ? fixedangle - (fixedlook + 80) + 360 : fixedangle - (fixedlook + 80)) > (((fixedlook - 80) - (fixedlook + 80)) < 0 ? (fixedlook - 80) - (fixedlook + 80) + 360 : (fixedlook - 80) - (fixedlook + 80))){
socket.emit('HitTo',{UserName:Victim.UserName,hitval:20});
}
}
Thank you
Or if that isn't helping, one of the answers from Stack Overflow found by searching for "javascript check if two angles are close" should give you enough useful information to solve it. – Andrew Morton

Related

Convert standard 360-degree to atan2

I need to reverse calculation of standard 360 angle to atan2.
It's my standard angle calulation
Standard angle = (int) Math.toDegrees(Math.atan2(y,x));
if(angle < 0)
angle += 360;
I need Math.atan2 value from angle how to achieve that.
To translate degrees range 0..360 into radians range -Pi..Pi:
if(angle > 180)
angle -= 360;
angle_radians = Math.toRadians(angle);
If your angle may be beyond standard range (you did not mentioned this), use integer modulo instead of loops
angle = angle % 360
(check how modulo operator works with negative numbers in your language)
Though your question is not very clear, it seems that you want to retrieve the angle before the statement
if (angle < 0)
angle += 360;
As atan2 returns a value in the range [-π,π), i.e. [-180°,180°), the subrange [-180°,0°) is mapped to [180°,360°). Then you invert with
if (angle >= 180°)
angle-= 360;
You also need to convert back to radians.
A general solution with O(1) performance:
double generalModulus(double value, double modulus, double offset) {
return value - Math.floor((value-offset)/modulus) * modulus + offset;
}
double normalizeAngleDegrees(double angleInDegrees, double offset) {
return generalModulus(angleInDegrees, 360.0, offset);
}
double normalizeAngleRadians(double angleInRadians, double offset) {
return generalModulus(angleInRadians, 2.0*Math.PI, offset);
}
To use:
double normDeg = normalizeAngleDegrees(angle, -180); // produces angle in range [-180, 180)
double normRad = normalizeAngleRadians(angle, 0); // produces angle in range [0, 2*Pi)
My Own answer
int normalizeAngle(int angle)
{
int newAngle = angle;
while (newAngle <= -180) newAngle += 360;
while (newAngle > 180) newAngle -= 360;
return newAngle;
}

how to get the end point of a translated QPolygon

I'm trying to draw an arrow so I just referred to the example code where we can draw arrows:
http://doc.qt.io/qt-5/qtwidgets-graphicsview-elasticnodes-edge-cpp.html
I decided to draw using the same formula and tried like:
theCurrentLine->setP1(QPointF(0, 0) );
theCurrentLine->setP2((theLineVector));
p->drawLine(*theCurrentLine);
double angle = ::acos(theCurrentLine->dx() / theCurrentLine->length());
if (theCurrentLine->dy() >= 0)
angle = TwoPi - angle;
QPointF sourcePoint = QPointF(0,0);
QPointF sourceArrowP1 = sourcePoint + QPointF(sin(angle + Pi / 3) * theArrowSize,
cos(angle + Pi / 3) * theArrowSize);
QPointF sourceArrowP2 = sourcePoint + QPointF(sin(angle + Pi - Pi / 3) * theArrowSize,
cos(angle + Pi - Pi / 3) * theArrowSize);
p->drawPolygon(QPolygonF() << theCurrentLine->p1() << sourceArrowP1 << sourceArrowP2);
but now I want to draw the line after the arrow head polygon gets drawn.
How can I change the P1() value of the theCurrentLine which can start after the polygon as currently the polygon(arrowHead) and the line start at the same point? I need to start the line after the arrow head is drawn. The reason is sometimes if the pen width increases the arrow head looks smaller than the line.
You can get the point at index in QPolygon.
QPoint QPolygon::point ( int index ) const
It would be easy when you know how many points there are. And Qt documentation is your friend.
And you could use count(), for example:
QPoint lastPoint = myPolygon.point(myPolygon.count() - 1);
Just give a name to your polygon, and you'll be fine.
Edit: Latest version of these codes should solve your problem. I thought I need to give an example. You need to add the points in this order:
QPolygon myPolygon;
myPolygon.setPoint(0, sourceArrowP1);
myPolygon.setPoint(1, theCurrentLine->p1());
myPolygon.setPoint(2, sourceArrowP2);
p->drawPolygon(myPolygon);
QPoint lastPoint;
lastPoint = myPolygon.point(myPolygon.count() - 1);
You need to draw the line between last and first points. Here:
p->drawLine(myPolygon.point(0, myPolygon.point(myPolygon.count() - 1));
If you want your arrow head to be filled with color, you need to use QPainterPath instead of QPolygon:
QPen pen(Qt::black); //Or whatever color you want
pen.setWidthF(10); //Or whatever size you want your lines to be
QBrush brush(Qt::red); //Or whatever color you want to fill the arrow head
p->setPen(pen);
p->setBrush(brush);
QPainterPath arrow(sourceArrowP1);
arrow.lineTo(theCurrentLine->p1());
arrow.lineTo(sourceArrowP2);
arrow.lineTo(sourceArrowP1); //This returns to the first point. You might eliminate this line if you want to.
p->drawPath(arrow);
My actual implementation:
theCurrentLine->setP1(QPointF(0, 0) ); // arrow line coordinates
theCurrentLine->setP2((theLineVector));
double angle = ::acos(theCurrentLine->dx() / theCurrentLine->length()); // angle of the current Line
if (theCurrentLine->dy() >= 0)
angle = TwoPi - angle;
// getting arrow head points to be drawn
QPointF sourcePoint = QPointF(0,0);
QPointF sourceArrowP1 = sourcePoint + QPointF(sin(angle + Pi / 3) * theArrowSize,
cos(angle + Pi / 3) * theArrowSize);
QPointF sourceArrowP2 = sourcePoint + QPointF(sin(angle + Pi - Pi / 3) * theArrowSize,
cos(angle + Pi - Pi / 3) * theArrowSize);
p->drawPolygon(QPolygonF() << theCurrentLine->p1() << sourceArrowP1 << sourceArrowP2);
// to find the center point in the arrow head right
QLineF perpLine = QLineF(theCurrentLine->p1(), theCurrentLine->p2());
QLineF arrowheadWidth = QLineF(sourceArrowP1, sourceArrowP2);
QPointF originPoint;
QLineF::IntersectType res = perpLine.intersect(arrowheadWidth, &originPoint);
theCurrentLine->setP1(originPoint);
p->drawLine(*theCurrentLine);
If anyone knows a much better way to implement this (I'm sure there will be), please correct me.

Sampling points in a vector

I have a vector _pts that contains values for (x,y,z), i.e. a point in 3D. Starting with _pts[0], I want to select those points whose distance between previously selected points is bigger than sampleRadius.
Here is my code but apparently something's wrong because is selecting a lot of points instead of just selecting a few.
Can anyone see what am I doing wrong? Probably you would need more code to see what can be missed, but I would also appreciate any idea on how can I implement this.
float distance;
bool distanceIsOk;
//PICK A POINT IN VECTOR _pts
for (int cPIdx = 0; cPIdx < _pts.size(); cPIdx++) {
distanceIsOk = true;
//CHECK DISTANCE AGAINST PREVIOUSLY PICKED POINTS
for (int dPIdx = 0; dPIdx < indeces.size(); dPIdx++) {
distance = sqrt(
(_pts[cPIdx].v[0] - _pts[dPIdx].v[0])*(_pts[cPIdx].v[0] - _pts[dPIdx].v[0]) +
(_pts[cPIdx].v[1] - _pts[dPIdx].v[1])*(_pts[cPIdx].v[1] - _pts[dPIdx].v[1]) +
(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])*(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])
);
//IF DISTANCE IS <= SUBSAMPLERADIUS FOR AT LEAST ONE PREVIOUSLY SELECTED POINT
if (distance <= subsampleRadius) {
//DISCARD THE POINT
distanceIsOk = false;
dPIdx += indeces.size();
}
}
//OTHERWISE INCLUDE THAT POINT
if (distanceIsOk == true) {
indeces.push_back(cPIdx);
}
}
Found the error. Instead of
distance = sqrt(
(_pts[cPIdx].v[0] - _pts[dPIdx].v[0])*(_pts[cPIdx].v[0] - _pts[dPIdx].v[0]) +
(_pts[cPIdx].v[1] - _pts[dPIdx].v[1])*(_pts[cPIdx].v[1] - _pts[dPIdx].v[1]) +
(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])*(_pts[cPIdx].v[2] - _pts[dPIdx].v[2])
it should be
distance = sqrt(
(_pts[cPIdx].v[0] - _pts[indeces[dPIdx]].v[0])*(_pts[cPIdx].v[0] - _pts[indeces[dPIdx]].v[0]) +
(_pts[cPIdx].v[1] - _pts[indeces[dPIdx]].v[1])*(_pts[cPIdx].v[1] - _pts[indeces[dPIdx]].v[1]) +
(_pts[cPIdx].v[2] - _pts[indeces[dPIdx]].v[2])*(_pts[cPIdx].v[2] - _pts[indeces[dPIdx]].v[2])

Circle collision response

I'm working on an Android game and I need to bounce 2 circles of each other (like 2 pool balls bouncing off each other). The collision is an elastic collision, and I need to calculate only 1 circles (called a Particle in my code) new velocity after the collision (the other circle, called a Barrier in my code will remain stationary and will not move because of a collision).
I am using a formula that I found on Wikipedia (http://en.wikipedia.org/wiki/Elastic_collision), but my end result for the new velocity of the particle after the collision is exactly the same as the velocity before the collision?
This is def wrong but I cant see where I am going wrong. Can anyone spot where I am going wrong?
I have just used a Java program to simulate my velocities and locations for the 2 circles as I dont wanna try it in my main Android game at the moment for fear of "breaking something"
Here is what I have so far (like I mentioned, this is just a simulation in NetBeans for the moment and I will use a menthod in my Android game to keep things a bit tidier):
double randomNextDouble = (new Random()).nextDouble();
System.out.println("Random.nextDouble: " + randomNextDouble);
double mathPI = Math.PI * 2;
System.out.println("Math PI: " + mathPI);
// get a random direction for the Particle to move towards
double direction = (new Random()).nextDouble() * Math.PI * 2;
System.out.println("Direction: " + direction);
// Then set the Particle's velocity - Increase to make Particles move faster
int velocity = 10;
System.out.println("Velocity: " + velocity);
// Then calculate the xv and the yv
// Velocity value on the x and y axis
double xv = (velocity * Math.cos(direction));
double yv = (velocity * Math.sin(direction));
System.out.println("\nXV: " + xv + "\nYV: " + yv);
// Genareting a random number for the Particle and Barrier's positions on screen
double Xmin = 0;
double Xmax = 300;
double Ymin = 0;
double Ymax = 300;
double randomNumber1 = Xmin + (int)(Math.random() * ((Xmax - Xmin) + 1));
double randomNumber2 = Ymin + (int)(Math.random() * ((Ymax - Ymin) + 1));
double randomNumber3 = Xmin + (int)(Math.random() * ((Xmax - Xmin) + 1));
double randomNumber4 = Ymin + (int)(Math.random() * ((Ymax - Ymin) + 1));
// Setting the Particle and Barrier's radius
double particleRadius = 8;
double barrierRadius = 16;
// Setting up the Particle and Barrier's mass
double particleMass = 100;
double barrierMass = 200;
// Assigning a random number to the Particle to simulate its position on screen
double particleX = randomNumber1;
double particleY = randomNumber2;
System.out.println("\nParticle X: " + particleX + " Particle Y: " + particleY);
// Assigning a random number to the Barrier to simulate its position on screen
double barrierX = randomNumber3;
double barrierY = randomNumber4;
System.out.println("Barrier X: " + barrierX + " Barrier Y: " + barrierY);
double distanceXToBarrier = barrierX - particleX;
System.out.println("\nBarrier X - Particle X: " + distanceXToBarrier);
double distanceYToBarrier = barrierY - particleY;
System.out.println("Barrier Y - Particle Y: " + distanceYToBarrier);
// Get the distance between the Particle and the Barrier
// Used for collision detection
double distance = Math.sqrt((distanceXToBarrier * distanceXToBarrier) + (distanceYToBarrier * distanceYToBarrier));
System.out.println("\nDistance: " + distance);
// Check to see if the Particle and Barrier has collided
if (distance <= particleRadius + barrierRadius)
{
System.out.println("Distance is less than 2 Radii");
}
else
System.out.println("Distance is NOT less than 2 Radii");
// Velx = (v1.u) * u + (v1 - (v1.u) * u)
// Vely = (v1.u) * u + (v1 - (v1.u) * u)
// Where v1 = xv and yv respectively
// Break it into 2 equations
// (v1.u) * u AND
// (v1 - (v1.u) * u)
//
// u = normalised Vector
// To normalize you just devide the x, y, z coords by the length of the vector.
// This then gives you the Unit Vector.
//
//Normalize the vector
double particleXNormalized = particleX * (1.0 / distance);
double particleYNormalized = particleY * (1.0 / distance);
System.out.println("\nParticle X Normalised: " + particleXNormalized +
"\nParticle Y Normalised: " + particleYNormalized);
// Calculating the first part of the eqaution
// (v1.u)
double v1DotUForX = xv * particleXNormalized;
double v1DotUForY = yv * particleYNormalized;
System.out.println("\nv1.u for X: " + v1DotUForX +
"\nv1.u for Y: " + v1DotUForY);
// The first part of the equation
// (v1.u) * u
double part1X = v1DotUForX * particleXNormalized;
double part1Y = v1DotUForY * particleYNormalized;
System.out.println("\nPart 1 for X: " + part1X +
"\nPart 1 for Y: " + part1Y);
// The second part of the equation
// (v1 - (v1.u) * u)
double part2X = (xv - (v1DotUForX) * particleXNormalized);
double part2Y = (yv - (v1DotUForY) * particleYNormalized);
System.out.println("\nPart 2 for X: " + part2X +
"\nPart 2 for Y: " + part2Y);
// Solving for:
// (((mass 1 - mass2) / (mass1 + mass2) * (v1.u) * u + ((2mass2) / (mass1 + mass2) * ((v1.u) * u))) +
// (v1 - (v1.u) * u))
double newXV = ((((particleMass - barrierMass) / (particleMass + barrierMass)) * part1X) + (((2 * barrierMass) / (particleMass + barrierMass)) * part1X) + part2X);
double newYV = ((((particleMass - barrierMass) / (particleMass + barrierMass)) * part1Y) + (((2 * barrierMass) / (particleMass + barrierMass)) * part1Y) + part2Y);
System.out.println("\nNew XV: " + newXV + "\nNew YV: " + newYV);
Looking at your algorithm, you appear to have made errors in the implementation. Why are you normalizing the coordinates of the particle? Shouldn't you be doing that to the velocity? In the usual equations, u is velocity, not position.
And why do you give the particle a random velocity (xv, yv) that has nothing to do with the two random coordinates you set up for the particle and barrier? (Surely the velocity should be some multiple of (barrier - particle) vector?)

Calculating the angle (of the line) between two points on a wrapping plane

OK, so I have a play field of 512x512 which wraps around, -32 becomes 512 for both x and y.
Now I need to calculate the angle between two entities, I have the following code as a kind of workaround, it works most of the time, but sometimes it still fails:
Shooter.getAngle = function(a, b) {
var ax = a.x;
var bx = b.x;
if (a.x < this.width / 4 && b.x > this.width - this.width / 4) {
ax += this.width;
} else if (a.x > this.width - this.width / 4 && b.x < this.width / 4) {
bx += this.width;
}
var ay = a.y;
var by = b.y;
if (a.y < this.height / 4 && b.x > this.height - this.height / 4) {
ay += this.height;
} else if (a.y > this.height - this.height / 4 && b.y < this.height / 4) {
by += this.height;
}
return this.wrapAngle(Math.atan2(ax - bx, ay - by) + Math.PI);
};
I just can't figure out how to project a onto b so that the wrapping is accounted for correctly.
If anyone could help, it would be great, since homing missiles that fly away from their target aren't that fun.
For clarification, if the player is moving right, the missile should follow him over the edge of the field to the left side, just as another player would do.
EDIT:
Here's a test version, the examples show that it wraps correctly but in cases were it doesn't have to wrap it breaks:
function getAngle(a, b) {
var tx = a.x - b.x;
var ty = a.y - b.y;
// actual area goes from -16 to 512 due to the border space that's out of screen
tx = ((tx + 16) % (480 + 32)) - 16;
ty = ((ty + 16) % (480 + 32)) - 16;
var r = Math.atan2(ty, tx) * (180 / Math.PI);
// example
// |> b a >|
// a.x = 460
// b.x = 60
// missile should go over the right border
// tx = 400
// r = 0
// missile goes right, OK
// example2
// |< a b <|
// a.x = 60
// b.x = 460
// missile should go over the left border
// tx = -400
// r = 180
// missile goes left, OK
// example3
// | a >>>> b |
// a.x = 60
// b.x = 280
// missile should go right
// tx = -220
// r = 180
// missile goes left, WRONG
// example4
// | b <<<< a |
// a.x = 280
// b.x = 60
// missile should go left
// tx = 220
// r = 0
// missile goes right, WRONG
console.log(ty, tx);
console.log(r);
}
function Point(x, y) {
this.x = x;
this.y = y;
}
getAngle(new Point(460, 240), new Point(60, 240));
getAngle(new Point(60, 240), new Point(460, 240));
getAngle(new Point(60, 240), new Point(280, 240));
getAngle(new Point(280, 240), new Point(60, 240));
It seems the only way to getting it work is by checking for cases when a.x < width * 0.25 and b.x > width * 0.75 etc. but that's buggy, at least in the version I posted above :/
I'd use this approach: Use the target as the center of your coordinate system (and wrap the missile's coordinates properly), then calculate the angle. So, pseudo-code should be this (EDIT2: Corrected code, a similar problem also led to the problems in your tests):
// missile.x/y = missile's coordinates
// target.x/y = target's coordinates
temp.x = missile.x - target.x;
temp.y = missile.y - target.y;
// The wrapping code - feel free to adjust,
// from the comments it seems you handle this
// a bit differently
while (temp.x < -240)
temp.x += 480
while (temp.y < -240)
temp.y += 480
while (temp.x > 240)
temp.x -= 480
while (temp.y > 240)
temp.y -= 480
// Now you can calculate the angle your missile must go,
// it has to fly across the line from temp.x/y to (0,0)
EDIT: I've seen the video and I think there's some error in your code, but I'm not sure where. Using mathematical functions and angles can be a bit tricky because usually math coordinates have the Y axis going up, screen coordinates have the Y axis going down.
Best way now would perhaps be to start some units test, something like this:
// Create a function that returns the angle a missile must
// fly and cares about all the dirty details with wrapping etc.
MissileAngle(missile.x, missile.y, target.x, target.y)
// Now create some tests for it, f.e.:
MissileAngle(missile top left corner, target bottom right corner)
Expected Result: Northwest
MissileAngle(missile bottom right corner, target top left corner)
Expected Result: Southeast
Note I didn't use coordinates here because it depends heavily on the coordinate system you're using and this might actually help you to get a better overall feeling for the problem (and of course, hopefully helps you to solve the bug, at least it's a fast way to check if it's working correctly without having to play the actual game, which is more time consuming).
Just to make the obvious suggestion: shouldn't it be Math.atan2(ay-by,ax-bx)? Apologies if that was just a typo in your post, but I can certainly see that causing bizarre behaviour in your missiles.
Edited to add: never mind, I just saw your comment.

Resources