Here is what I have so far:
int vx = (playerx - x);
int vy = (playery - y);
double distance = Math.sqrt((vx * vx) + (vy * vy));
double doublex = ((vx / distance));
double doubley = ((vy / distance));
dx = (int) Math.floor(doublex + 0.5);
dy = (int) Math.floor(doubley + 0.5);
x += dx;
y += dy;
I just want x and y to move straight towards playerx and playery but it moves only at a slope of 0, 1, or undefined.
I suspect it because you x and y are int and you have moving such a short distance that you will only be (1, 1), (1, 0) or (0, 1).
You need to allow it to move further that 1, or use a type which more resolution. e.g. double.
BTW: Instead of using Math.floor I believe a better choice is
dx = (int) Math.round(doublex);
You are dividing horizontal distance and vertical distance by total distance, this will always result in a number between 1 and -1 so each time it moves it will move by either nothing or by 1 in a direction. I guess this is in pixels?
Each time the move happens you should keep track of the actual distance moved and the desired distance moved because, for example, you may be trying to move 0.4 along the y axes in every loop, and it will never move because that will always round down. So if in the second loop you know you should have moved by 0.8 in total, you can round up to one, and leave the desired set to -0.2 and keep looping.
A solution similar to Bresanham's Line Algorithm can be implemented. IE:
function line(x0, y0, x1, y1)
real deltax := x1 - x0
real deltay := y1 - y0
real deltaerr := abs(deltay / deltax) // Assume deltax != 0 (line is not vertical),
// note that this division needs to be done in a way that preserves the fractional part
real error := deltaerr - 0.5
int y := y0
for x from x0 to x1
plot(x,y)
error := error + deltaerr
if error ≥ 0.5 then
y := y + 1
error := error - 1.0
Source: Bresanham's Line Algoithm
Related
public void move(){
double angle;
for(int i = 0; i < planets.size(); i++){
if(Math.abs(Math.sqrt(Math.pow(ship.locX - (planets.get(i).locX + planets.get(i).radi), 2) + Math.pow(ship.locY - (planets.get(i).locY + planets.get(i).radi), 2))) < planets.get(i).gravrange){
//Distance formula between spaceship and planets to determine whether the ship is within the influence of the planet.
angle = ((Math.atan2((planets.get(i).locX + planets.get(i).radi) - ship.locX, (planets.get(i).locY + planets.get(i).radi) - ship.locY)) / Math.PI) + 1;
//The problematic math equation.
Produces a double from 0 to 2, 0 being when ship.locY < planets.get(i).locY && ship.locX == (planets.get(i).locX - planets.get(i).radi). (when relative X = 0 and relative Y < 0.)
if(ship.locX > (planets.get(i).locX + planets.get(i).radi)){xm += Math.cos(angle) * planets.get(i).gravrate;}
else{xm -= Math.cos(angle) * planets.get(i).gravrate;}
if(ship.locY > (planets.get(i).locY + planets.get(i).radi)){ym += Math.sin(angle) * planets.get(i).gravrate;}
else{ym -= Math.sin(angle) * planets.get(i).gravrate;}
}
}
This uses the data to modify the X and Y velocities of the spacecraft.
This equation works for the majority of an orbit, but under certain circumstances has an issue in which the spacecraft undergoes a retrograde force, slowing it. Shortly afterward it begins to be repelled by the planetary body, which after a short period begins attracting it again. When the spacecraft reaches the original position at which this occurred, it begins to move in the opposite direction of its original orbit.
This continues to occur until the spacecraft begins a wavelike motion.
Is there a way to solve this, or am I simply using the wrong equation? I've been attempting to fix this for about two weeks now. I have no education in physics nor calculus at this point in time, so my understanding is limited.
Edit: The comments had questions about my math, so I'll attempt to answer them here. From what I know about atan2, it produces a number from -pi to pi. I divide by pi to produce a number from -1 to 1, then add 1 to produce 0 to 2. I then use this number as a radian measurement. My knowledge of radians (unit circle) is that a circle's radian measure is 0 to 2pi.
Edit 2: The following code has very different math but produces the desired results, save for issues of repelling rather than attracting when approaching the North and South 'poles' of the planet.
public void move(){
double angle;
double x1, x2, y1, y2;
for(int i = 0; i < planets.size(); i++){
x1 = ship.locX;
y1 = ship.locY;
x2 = planets.get(i).locX + planets.get(i).radi;
y2 = planets.get(i).locY + planets.get(i).radi;
if(Math.abs(Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))) < planets.get(i).gravrange){
//Distance formula between spaceship and planets
angle = (y2 - y1)/(x2 - x1); //Gets slope of line between points.
if(angle > 0){
if(y1 > y2){
xm += Math.cos(angle) * planets.get(i).gravrate;
ym += Math.sin(angle) * planets.get(i).gravrate;
}else{
xm -= Math.cos(angle) * planets.get(i).gravrate;
ym -= Math.sin(angle) * planets.get(i).gravrate;
}
}
else{
if(y1 > y2){
xm -= Math.cos(angle) * planets.get(i).gravrate;
ym -= Math.sin(angle) * planets.get(i).gravrate;
}else{
xm += Math.cos(angle) * planets.get(i).gravrate;
ym += Math.sin(angle) * planets.get(i).gravrate;}
}
}
}
I wrote it up very quickly to see if using the slope of the line rather than that strange atan2 equation would help. Apparently it did. I also made the code a bit more readable in this section.
The following code fixed my issue. I was overcomplicating my math equations as I usually do. Took me three weeks of Googling, asking people with degrees in physics and mathematics, and reading Javadocs before I figured that one out. Turns out how atan2 works is simply different from how I thought it worked and I was improperly using it.
The solution was simplifying the atan2 equation beforehand and removing the unnecessary additions.
x1 = ship.locX;
y1 = ship.locY;
x2 = planets.get(i).locX + planets.get(i).radi;
y2 = planets.get(i).locY + planets.get(i).radi;
if(Math.abs(Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))) < planets.get(i).gravrange){
//Distance formula between spaceship and planets
angle = Math.atan2((y2 - y1),(x2 - x1)); //Converts the difference to polar coordinates and returns theta.
xm -= Math.cos(angle) * planets.get(i).gravrate; //Converts theta to X/Y
ym -= Math.sin(angle) * planets.get(i).gravrate; //velocity values.
}
I have the following code to determine the intersection of two 2D lines. It's not working and I'm not sure why; I've been copying code from multiple sources without much change.
The lines extend infinitely from a given midpoint. The "vector()" referenced in the below code is a 2D vector of magnitude 1.0 that indicates the direction in which the line extends (the line also extends in the negative direction, it's not a ray).
Earlier, I was using this method for determining the intersection: Intersection point of two lines (2 dimensions)
After that gave me wrong results, I went with the method on Wikipedia.
float x1 = line1.location().x();
float y1 = line1.location().y();
float x2 = x1 + line1.vector().x();
float y2 = y1 + line1.vector().y();
float x3 = line2.location().x();
float y3 = line2.location().y();
float x4 = x3 + line2.vector().x();
float y4 = y3 + line2.vector().y();
float d = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
if(d == 0) // If parallel, defaults to the average location of the lines.
return new Vector2f((x1 + x3) * 0.5f, (y1 + y3) * 0.5f);
else
{
float a = (x1 * y2) - (y1 * x2);
float b = (x3 * y4) - (y3 * x4);
return new Vector2f(((a * (x3 - x4)) - ((x1 - x2) * b)) / d,
((a * (y3 - y4)) - ((y1 - y2) * b)) / d);
}
Two examples of how it is wrong (The orange dot is the returned point of intersection):q
It returns a point along the primary line, but it doesn't return a point on the second. the same bugs happen in both methods, so I'm really not sure what I'm doing wrong.
How can I fix this?
EDIT: Actually, this code works fine; my visualization code had an error.
I believe you've confused what the location() and vector() methods return. Unless i'm mistaken the location().x() gives you a point on the line created by the vector while vector().x() gives you the magnitude of the x value of the vector. (e.g. a horizontal has a vector().y() of 0)
You are adding the magnitude of the vector to the starting point. Instead try multiplying the starting point by the magnitude
float x2 = x1 * line1.vector().x();
float y2 = y1 * line1.vector().y();
As it turns out, the code was working; my visualization code had a bug in it. My bad.
I am looking for a way to make my Entity(A bullet) move towards a x and y,which is where the player was when the bullet was fired. so In my entity class i have -
float xSpeed = 1.0F;
float ySpeed = 0.0F;
if I wanted to move the entity in a diaganal line I would make xSpeed and ySpeed = 1.0F
How would I make it move in the dir of another x and y?
Thanks for the help
EDIT --
Solved,thanks for the help
To anyone else who finds this question needing help,here is my code-
float xSpeed = 0;
float ySpeed = 0;
then I have some maths to make it so they move at the same speed
ySpeed = ySpeed * (float) (2.5 / Math.sqrt(xSpeed * xSpeed + ySpeed * ySpeed));
xSpeed = xSpeed * (float) (2.5 / Math.sqrt(xSpeed * xSpeed + ySpeed * ySpeed));
and this code to set the xSpeed and ySpeed,based on a x and y you want to move towards
xSpeed = (Game.PlayerX - x) / 3;
ySpeed = (Game.Playery - y) / 3;
and then,finaly,add xSpeed and ySpeed to the x and y of your entity
this.x += xSpeed;
this.y += ySpeed;
The usual way is to set the relative x and y speeds to cover the required x and y distances in the allotted travel time:
float xSpeed = (targetLocation.x - currentLocation.x) / travelTime;
float ySpeed = (targetLocation.y - currentLocation.y) / travelTime;
If you want to travel at a predetermined speed, rather than in a predetermined travel time, do the above computations with travelTime set to some arbitrary value (1.0f would work fine, so you could simply eliminate the division). Then compute this factor:
float factor = desiredSpeed / Math.sqrt(xSpeed * xSpeed + ySpeed * ySpeed);
(Note that you'll be dividing by zero if xSpeed and ySpeed are both 0, but that simply indicates that the current and target locations are identical. If that's a possible condition, you should include an appropriate check.) Finally, readjust xSpeed and ySpeed by the factor:
xSpeed *= factor;
ySpeed *= factor;
Note that by setting travelTime to 1, this is just a sneaky way of multiplying the predetermined speed by the sine and cosine of the angle that the travel direction makes with the x axis without mentioning trigonometry at all. :)
You need to define the speed of the object, and the direction. Then you can use simple trig to determine the x/y displacement per step.
If you use your current algorithm your object will change speeds depending on its direction, which is probably not what you want to do.
Linear direction:
speed x = 1
speed y = 0
Total speed = 1
Diagnol direction:
speed x = 1
speed y = 1
Total speed = 2
See the problem? Your speed should be constant regardless of direction.
Use what's called a Vector to describe your object's direction and speed.
class Vector {
int x; int y; float angle; float magnitude;
}
The x,y, angle and magnitude describe a triangle. The movement you want will be a point on the line of the hypotenuse of the triangle, modified by the time step your engine is using.
edit -
Imagine a Circle that has a radius of 1 unit, with it's origin at 0, 0.
Draw a line from the origin to the left side. You get a line 1 unit long, that intersects the circle at (1,0)
Now, draw a line from the origin at a 45 degree angle to the edge of a circle.
You'll also get a line 1 unit long, however the X/Y coordinate where the line intersects the circle will be (0.7,0.7) NOT (1,1).
I do not have an ide in front of me, so please someone edit if my math is wrong here, but I beilive you'll want something like this:
newPositionX = currentX + ( cos ( angle ) * speed * timeStep )
newPositionY = currentY + ( sin ( angle ) * speed * timeStep )
Based on Wikipedia's article on Bresenham's line algorithm I've implemented the simplified version described there, my Java implementation looks like this:
int dx = Math.abs(x2 - x1);
int dy = Math.abs(y2 - y1);
int sx = (x1 < x2) ? 1 : -1;
int sy = (y1 < y2) ? 1 : -1;
int err = dx - dy;
while (true) {
framebuffer.setPixel(x1, y1, Vec3.one);
if (x1 == x2 && y1 == y2) {
break;
}
int e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
x1 = x1 + sx;
}
if (e2 < dx) {
err = err + dx;
y1 = y1 + sy;
}
}
Now I do understand that err controls the ratio between steps on the x-axis compared to steps on the y-axis - but now that I'm supposed to document what the code is doing I fail to clearly express, what it is for, and why exactly the if-statements are, how they are, and why err is changed in the way as seen in the code.
Wikipedia doesn't point to any more detailled explanations or sources, so I'm wondering:
What precisely does err do and why are dx and dy used in exactly the shown way to maintain the correct ratio between horizontal and vertical steps using this simplified version of Bresenham's line algorithm?
There are various forms of equations for a line, one of the most familiar being y=m*x+b. Now if m=dy/dx and c = dx*b, then dx*y = dy*x + c. Writing f(x) = dy*x - dx*y + c, we have f(x,y) = 0 iff (x,y) is a point on given line.
If you advance x one unit, f(x,y) changes by dy; if you advance y one unit, f(x,y) changes by dx.
In your code, err represents the current value of the linear functional f(x,y), and the statement sequences
err = err - dy;
x1 = x1 + sx;
and
err = err + dx;
y1 = y1 + sy;
represent advancing x or y one unit (in sx or sy direction), with consequent effect on the function value. As noted before, f(x,y) is zero for points on the line; it is positive for points on one side of the line, and negative for those on the other. The if tests determine whether advancing x will stay closer to the desired line than advancing y, or vice versa, or both.
The initialization err = dx - dy; is designed to minimize offset error; if you blow up your plotting scale, you'll see that your computed line may not be centered on the desired line with different initializations.
Just want to add one bit of "why" information to jwpat's excellent answer.
The point of using the f(x) = dy*x - dx*y + c formulation is to speed up the calculation. This formulation uses integer arithmetic (faster), whereas the traditional y = mx + b formulation uses floating point arithmetic (slower).
Hi
this a part of my code :
I have a list which its size is three and I consider that 2 last items are in one line (p and q) I need to get the angle between the first item of this list and these two points (p,q)
private Point partition(List<Point> list, Point p, Point q) {
double x1 = p.getX();
double x2 = q.getX();
double y1 = p.getY();
double y2 = q.getY();
double pQ = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
for (int i = 0; i < list.size()-2; i++) {
double pointX = list.get(i).getX();
double pointY = list.get(i).getY();
double pointQ = Math.sqrt((x2 - pointX) * (x2 - pointX) + (y2 - pointY) * (y2 - pointY));
double pointP = Math.sqrt((pointX - x1) * (pointX - x1) + (pointY - y1) * (pointY - y1));
double angle = Math.acos((pQ * pQ - pointP * pointP - pointQ * pointQ) /(- 2 * pointP * pointQ));
System.out.println(angle);
}
but instead of printing an angle for the first item it will print :(first item is not in the line of two last items).
1.6288442476732894
those points that print this result are :
[X :143.0 Y: 217.0, X :93.0 Y: 163.0, X :193.0 Y: 165.0]
please help me thanks.
EDITED : really it makes me confused .in such a way it will print this value ,sorry all !!!
Your arccos is bad, you need a parenthesis and a - :
arccos((pQ^2 - pointP^2 - pointQ^2)/(-2 * pointP * pointQ))
see How to calculate an angle from three points?
NaN is a special double value meaning "not a number". It is generated as a result of some "bad" calculations:
dividing 0 by 0
dividing infinity by infinity (any combination of positive and negative)
multiplying 0 by infinity, either positive and negative, and vice-versa
adding negative and positive infinity
subtracting negative from positive infinity, and vice-versa
the square root of a negative number
the logarithm of a negative number
the inverse sine or cosine of a number not between -1 and 1
any calculation involving one or more NaN values.
Check the result of the argument to the inverse cosine function (acos). I bet it is off the valid range.