Decelerate while moving in direction - java

Here's a quick description of what some of the methods you'll see do:
this.bounds: Returns the ship's bounds (a rectangle)
this.bounds.getCenter(): Returns a Vector2d representing the center of the ship's bounds.
getVelocity(): A Vector2d which represents the ship's velocity (added to position every frame)
new Vector2d(angle): A new Vector2d, normalized, when given an angle (in radians)
Vector2d#interpolate(Vector2d target, double amount): Not linear interpolation! If you want to see the interpolate code, here it is (in class Vector2d):
public Vector2d interpolate(Vector2d target, double amount) {
if (getDistanceSquared(target) < amount * amount)
return target;
return interpolate(getAngle(target), amount);
}
public Vector2d interpolate(double direction, double amount) {
return this.add(new Vector2d(direction).multiply(amount));
}
When the player is not pressing keys, the ship should just decelerate. Here's what I do for that:
public void drift() {
setVelocity(getVelocity().interpolate(Vector2d.ZERO, this.deceleration));
}
However, now I've realized I want it to drift while going toward a target. I've tried this:
public void drift(Vector2d target) {
double angle = this.bounds.getCenter().getAngle(target);
setVelocity(getVelocity().interpolate(new Vector2d(angle), this.deceleration));
}
This of course won't ever actually reach zero speed (since I'm interpolating toward an angle vector, which has a magnitude of 1). Also, it only really "drifts" in the direction of the target when it gets very slow.
Any ideas on how I can accomplish this? I just can't figure it out. This is a big question so thanks a lot.
This is using a big library I made so if you have any questions as to what some stuff does please ask, I think I've covered most of it though.

Your interpolate function does strange things. Why not use simple physical models? For example:
Body has position (px, py) and velocity (vx, vy), also unit direction vector (dx, dy) is convenient
After (small) time interval dt velocity changes depending on acceleration
vx = vx + ax * dt
vy = vy + ay * dt
Any outer force (motor) causes acceleration and changes velocity
ax = forcex / mass //mass might be 1
ay = forcey / mass
Dry friction force magnitude does not depend on velocity, it's direction is reverse to velocity
ax = c * mass * (-dx)
ay = c * mass * (-dy)
Liquid friction force magnitude depends on velocity (there are many different equations for different situations), it's direction is reverse to velocity
ax = k * Vmagnitude * (-dx)
ay = k * Vmagnitude * (-dy)
So for every time interval add all forces, find resulting acceleration, find resulting velocity, change position accordingly

Related

Trigonometry with Java

I'm trying to do some basic trigonometry with Java and LibGDX on android.
I've spent a long time googling "How to find an angle in right triangles".
I still don't really understand :(
I want to give an Actor subclass a random direction to follow. So what is the angle - and what should I set xSpeed and ySpeed to, in order to move at the correct angle.
I started writing an app to help me see how it works.
There are two objects - An origin point and a touch point. User presses screen, touchPoint moves to where user touched. Methods fire to figure out the appropriate values. I know the XDistance and YDistance between the two points. That means I know the Opposite length and the Adjacent length. So all I need to do is tan-1 of (opposite / adjacent), am I right?
I just don't understand what to do with the numbers my program spits out.
Some code:
In create event of main class:
stage.addListener(new ClickListener() {
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
touchPoint.setX(x);
touchPoint.setY(y);
touchPoint.checkDistance(); // saves x and y distances from origin in private fields
atan2D = getAtan2(touchPoint.getYDistance(), touchPoint.getXDistance());
tanhD = getTanh(touchPoint.getYDistance(), touchPoint.getXDistance());
xDistanceLbl.setText("X Distance: " + touchPoint.getXDistance());
yDistanceLbl.setText("Y Distance: " + touchPoint.getYDistance());
atan2Lbl.setText("Atan2: " + atan2D);
tanhLbl.setText("Tanh: " + tanhD);
angleLbl.setText("Angle: No idea");
}
})
...
private double getAtan2(float adjacent, float opposite) {
return Math.atan2(adjacent, opposite);
}
private double getTanh(float adjacent, float opposite) {
return Math.tanh((adjacent / opposite));
}
These two functions give me numbers between (atan2: -pi to pi) and (tanh: -1.0 to 1.0)
How do I turn these values into angles from which I can then work backwards and get the opposite and adjacent again?
Doing this should allow me to create and object with a random direction, which I can use in 2D games.
atan2 gives you direction in radians. Direction from origin (0,0) to touchPoint. If you need direction from some object to touchPoint, then subtract object coordinates. Perhaps you also want to see direction in degrees (this is only for human eyes)
dx = x - o.x
dy = y - o.y
dir = atan2(dy, dx)
dir_in_degrees = 180 * dir / Pi
I you have direction and want to retrieve coordinate differences, you need to store distance
distance = sqrt(dx*dx + dy*dy)
later
dx = distance * cos(dir)
dy = distance * sin(dir)
But note that often storing dx and dy is better, because some calculations might be performed without trigonometric functions
Just noticed - using tanh is completely wrong, this is hyperbolic tangent function, it has no relation to geometry.
You can use arctan, but it gives angle in half-range only (compared with atan2)

Firing projectiles in a circle

So I can't seem to find an answer to this, but I am trying to fire bullets into a circle. I have a simple class for a circular path that I attach to a bullet and it reads from that class a position when given a time value. The bullet simply increments this time value, constantly updating its position to the next. This can be improved but until I get the logic down this is what I have. I know this method works because I tried it with a linear path. The problem is applying it to a circular path.
I want the bullet to circle around a point (say Point 'Center') with a given radius and speed. I want all bullets to travel at the same speed no matter the radius of the circle so a larger circle will take longer to complete than a shorter one. Currently what is happening is I have the CircularPath object giving saying x = r * cos(t) and y = r * sin (t) where t is in radians, but this is making a circle that increases in speed as the radius increases and the radius and center of this circle is completely off. The bullets are starting in the correct position, except the radius and speeds are off. I hope I am describing this adequately. I will post the code for anyone to inspect.
package io.shparki.tetris.go;
import io.shparki.tetris.util.Point2D;
import java.awt.Color;
import java.awt.Graphics2D;
public class CircularPath extends Path{
private double radius;
// Where Start is the center and end is the location of mouse
// Radius will be distance between the two
public CircularPath(Point2D start, Point2D end) {
super(start, end);
radius = normalToEnd.getLength();
color = Color.YELLOW;
}
public Point2D getPointAtTime(double time){
double px = start.getX() + radius * Math.cos(Math.toRadians(time));
double py = start.getY() - radius * Math.sin(Math.toRadians(time));
return new Point2D(px, py);
}
public double getFinalTime() { return 0; }
public CircularPath getClone() { return new CircularPath(start.getClone(), end.getClone()); }
public void update(){
super.update();
radius = normalToEnd.getLength();
}
public void render(Graphics2D g2d){
super.render(g2d);
g2d.drawLine((int)start.getX(), (int)start.getY(), (int)end.getX(), (int)end.getY());
//g2d.drawOval((int)(start.getX() - radius), (int)(start.getY() - radius), (int)radius * 2, (int)radius * 2);
}
}
x = r * cos(t/r)
y = r * sin(t/r)
The other solution is to model 2d momentum and impose a "gravitational force" toward the center point (or ellipsoidal focus, more generally) that you want the moving object to orbit around.
(The classic Space Wars game was implemented on a machine too slow to handle the trig computations in realtime, so they precomputed a 2d array each for the x and y components of the gravity field; they could then just do a table lookup based on the ship's last position and use that to update its momentum, which was then used to update its position. Slower machines forced more clever solutions.)

Rotate a triangle continuously

I have a float[] newCoords variable that has a size of 9. The first 3 entries represent one vertex, the next 3 represent the second vertex and the last 3 represent the last vertex.
I have some code that is supposed to rotate a triangle anywhere in space when I feed it the coordinates. It looks like this:
float s = (float) Math.sin(0.5);
float c = (float) Math.cos(0.5);
float[] centroid = getCentroid(newCoords);
newCoords[0] -= centroid[0];
newCoords[1] -= centroid[1];
newCoords[3] -= centroid[0];
newCoords[4] -= centroid[1];
newCoords[6] -= centroid[0];
newCoords[7] -= centroid[1];
newCoords[0] = (newCoords[0] * c) - (newCoords[1] * s);
newCoords[1] = (newCoords[0] * s) + (newCoords[1] * c);
newCoords[3] = (newCoords[3] * c) - (newCoords[4] * s);
newCoords[4] = (newCoords[3] * s) + (newCoords[4] * c);
newCoords[6] = (newCoords[6] * c) - (newCoords[7] * s);
newCoords[7] = (newCoords[6] * s) + (newCoords[7] * c);
newCoords[0] += centroid[0];
newCoords[1] += centroid[1];
newCoords[3] += centroid[0];
newCoords[4] += centroid[1];
newCoords[6] += centroid[0];
newCoords[7] += centroid[1];
The problem is, its not rotating it properly, the triangles are spinning and getting smaller and smaller until they disappear for some reason, can anyone see why this is happening?
EDIT: whoops, almost forgot, here is my getCentroid() method.
private float[] getCentroid(float[] p1) {
float[] newCoords = new float[] {(p1[0] + p1[3] + p1[6]) / 3.0f,
(p1[1] + p1[4] + p1[7]) / 3.0f, 0};
return newCoords;
}
I see two problems with your code. Both are fixed with a little change.
You try to apply a rotation operation, taking X and Y coordinates as input and having the new X and Y as output. For every vertex you rotate, you have two lines of code: the first computes the X, the second the Y coordinate. But when computing the Y coordinate, you use the already rotated X coordinate! That's wrong.
There is also a numerical problem. You reuse the old values again and again, resulting in a chain of rotation computations a value makes though, so the numerical errors sum up. Never rely on such computations to work as expected. Instead, you should work with the original values and increase the angle in each frame. This makes sure that each value only participated in a single rotation computation.
For fixing both problems, keep the original coordinates somewhere in your code, I call them coords, and rewrite the code such that you take that array as input (keep newCoords as the output). Increase the rotation angle in each frame to achieve a rotation animation.
This fixes both problems because you get rid of that chain and also you have different arrays for input and output in your rotation function.
Pseudo-code:
// initial:
angle = 0.0;
coords = (initial coordinates)
// per frame:
angle += 0.5;
newCoords = rotate(coords, angle);
draw(newCoords);
Also, please note that 0.5 is a large angle if you want to rotate by that angle frame by frame. The math functions expect angle in radians (not degrees), so you might want to use a lower value depending on what you want to visualize in particular.
You might wonder why I reuse the old angle in each frame, as according to the above mentioned problem 2., it should introduce numerical problems, since it's also a chain of computations. That's not a problem with the rotation angle, as a simple summation doesn't show such bad numerical errors you experience with applying rotations. Yet it has some problems, but they only show up at very long running times when the angle reaches some billions. The reason why such a summation in general is not that bad is because you're changing the variable in the same direction in each frame as well as a slightly off rotation angle isn't noticed very much by the user.

JAVA elastic collision of moving and non moving circles

I'm trying to write a java mobile application (J2ME) and I got stuck with a problem: in my project there are moving circles called shots, and non moving circles called orbs. When a shot hits an orb, it should bounce off by classical physical laws. However I couldn't find any algorithm of this sort.
The movement of a shot is described by velocity on axis x and y (pixels/update). all the information about the circles is known: their location, radius and the speed (on axis x and y) of the shot.
Note: the orb does not start moving after the collision, it stays at its place. The collision is an elastic collision between the two while the orb remains static
here is the collision solution method in class Shot:
public void collision(Orb o)
{
//the orb's center point
Point oc=new Point(o.getTopLeft().x+o.getWidth()/2,o.getTopLeft().y+o.getWidth()/2);
//the shot's center point
Point sc=new Point(topLeft.x+width/2,topLeft.y+width/2);
//variables vx and vy are the shot's velocity on axis x and y
if(oc.x==sc.x)
{
vy=-vy;
return ;
}
if(oc.y==sc.y)
{
vx=-vx;
return ;
}
// o.getWidth() returns the orb's width, width is the shot's width
double angle=0; //here should be some sort of calculation of the shot's angle
setAngle(angle);
}
public void setAngle(double angle)
{
double v=Math.sqrt(vx*vx+vy*vy);
vx=Math.cos(Math.toRadians(angle))*v;
vy=-Math.sin(Math.toRadians(angle))*v;
}
thanks in advance for all helpers
At the point of collision, momentum, angular momentum and energy are preserved. Set m1, m2 the masses of the disks, p1=(p1x,p1y), p2=(p2x,p2y) the positions of the centers of the disks at collition time, u1, u2 the velocities before and v1,v2 the velocities after collision. Then the conservation laws demand that
0 = m1*(u1-v1)+m2*(u2-v2)
0 = m1*cross(p1,u1-v1)+m2*cross(p2,u2-v2)
0 = m1*dot(u1-v1,u1+v1)+m2*dot(u2-v2,u2+v2)
Eliminate u2-v2 using the first equation
0 = m1*cross(p1-p2,u1-v1)
0 = m1*dot(u1-v1,u1+v1-u2-v2)
The first tells us that (u1-v1) and thus (u2-v2) is a multiple of (p1-p2), the impulse exchange is in the normal or radial direction, no tangential interaction. Conservation of impulse and energy now leads to a interaction constant a so that
u1-v1 = m2*a*(p1-p2)
u2-v2 = m1*a*(p2-p1)
0 = dot(m2*a*(p1-p2), 2*u1-m2*a*(p1-p2)-2*u2+m1*a*(p2-p1))
resulting in a condition for the non-zero interaction term a
2 * dot(p1-p2, u1-u2) = (m1+m2) * dot(p1-p2,p1-p2) * a
which can now be solved using the fraction
b = dot(p1-p2, u1-u2) / dot(p1-p2, p1-p2)
as
a = 2/(m1+m2) * b
v1 = u1 - 2 * m2/(m1+m2) * b * (p1-p2)
v2 = u2 - 2 * m1/(m1+m2) * b * (p2-p1)
To get the second disk stationary, set u2=0 and its mass m2 to be very large or infinite, then the second formula says v2=u2=0 and the first
v1 = u1 - 2 * dot(p1-p2, u1) / dot(p1-p2, p1-p2) * (p1-p2)
that is, v1 is the reflection of u1 on the plane that has (p1-p2) as its normal. Note that the point of collision is characterized by norm(p1-p2)=r1+r2 or
dot(p1-p2, p1-p2) = (r1+r2)^2
so that the denominator is already known from collision detection.
Per your code, oc{x,y} contains the center of the fixed disk or orb, sc{x,y} the center and {vx,vy} the velocity of the moving disk.
Compute dc={sc.x-oc.x, sc.y-oc.y} and dist2=dc.x*dc.x+dc.y*dc.y
1.a Check that sqrt(dist2) is sufficiently close to sc.radius+oc.radius. Common lore says that comparing the squares is more efficient. Fine-tune the location of the intersection point if dist2 is too small.
Compute dot = dc.x*vx+dcy*vy and dot = dot/dist2
Update vx = vx - 2*dot*dc.x, vy = vy - 2*dot*dc.y
The special cases are contained inside these formulas, e.g., for dc.y==0, that is, oc.y==sc.y one gets dot=vx/dc.x, so that vx=-vx, vy=vy results.
Considering that one circle is static I would say that including energy and momentum is redundant. The system's momentum will be preserved as long as the moving ball contains the same speed before and after the collision. Thus the only thing you need to change is the angle at which the ball is moving.
I know there's a lot of opinions against using trigonometric functions if you can solve the issue using vector math. However, once you know the contact point between the two circles, the trigonometric way of dealing with the issue is this simple:
dx = -dx; //Reverse direction
dy = -dy;
double speed = Math.sqrt(dx*dx + dy*dy);
double currentAngle = Math.atan2(dy, dx);
//The angle between the ball's center and the orbs center
double reflectionAngle = Math.atan2(oc.y - sc.y, oc.x - sc.x);
//The outcome of this "static" collision is just a angular reflection with preserved speed
double newAngle = 2*reflectionAngle - currentAngle;
dx = speed * Math.cos(newAngle); //Setting new velocity
dy = speed * Math.sin(newAngle);
Using the orb's coordinates in the calculation is an approximation that gains accuracy the closer your shot is to the actual impact point in time when this method is executed. Thus you might want to do one of the following:
Replace the orb's coordinates by the actual point of impact (a tad more accurate)
Replace the shot's coordinates by the position it has exactly when the impact will/did occur. This is the best scenario in respect to the outcome angle, however may lead to slight positional displacements compared to a fully realistic scenario.

2d Ball Collision Problem: no conservation of energy

I am trying to write a simple physics simulation where balls with varying radii and masses bounce around in a perfectly elastic and frictionless environment. I wrote my own code following this resource: http://www.vobarian.com/collisions/2dcollisions2.pdf and I also tested the code from here: Ball to Ball Collision - Detection and Handling
QUESTION EDITED
With the help of Rick Goldstein and Ralph, I have gotten my code to work (there was a typo..). Thanks so much for you help. However I am still confused as to why the other algorithm isn't working for me. The balls bounce off in the correct directions, but the total energy of the system is never conserved. The velocities get faster and faster until the balls just start blinking in static positions on the screen. I actually want to use this code in my program, because it is a lot more concise than the one I wrote.
Here is the functional algorithm that I wrote (although I did take the first bit from that other source). Its in a Bubble class:
public void resolveCollision(Bubble b)
{
// get the minimum translation distance
Vector2 delta = (position.subtract(b.position));
float d = delta.getMagnitude();
// minimum translation distance to push balls apart after intersecting
Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d);
// resolve intersection --
// inverse mass quantities
float im1 = 1 / getMass();
float im2 = 1 / b.getMass();
// push-pull them apart based off their mass
position = position.add(mtd.multiply(im1 / (im1 + im2)));
b.position = b.position.subtract(mtd.multiply(im2 / (im1 + im2)));
//get the unit normal and unit tanget vectors
Vector2 uN = b.position.subtract(this.position).normalize();
Vector2 uT = new Vector2(-uN.Y, uN.X);
//project ball 1 & 2 's velocities onto the collision axis
float v1n = uN.dot(this.velocity);
float v1t = uT.dot(this.velocity);
float v2n = uN.dot(b.velocity);
float v2t = uT.dot(b.velocity);
//calculate the post collision normal velocities (tangent velocities don't change)
float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass+b.mass);
float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);
//convert scalar velocities to vectors
Vector2 postV1N = uN.multiply(v1nPost);
Vector2 postV1T = uT.multiply(v1t);
Vector2 postV2N = uN.multiply(v2nPost);
Vector2 postV2T = uT.multiply(v2t);
//change the balls velocities
this.velocity = postV1N.add(postV1T);
b.velocity = postV2N.add(postV2T);
}
And here is the one that doesn't work
public void resolveCollision(Bubble b)
{
// get the minimum translation distance
Vector2 delta = (position.subtract(b.position));
float d = delta.getMagnitude();
// minimum translation distance to push balls apart after intersecting
Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d);
// resolve intersection --
// inverse mass quantities
float im1 = 1 / getMass();
float im2 = 1 / b.getMass();
// push-pull them apart based off their mass
position = position.add(mtd.multiply(im1 / (im1 + im2)));
b.position = b.position.subtract(mtd.multiply(im2 / (im1 + im2)));
// impact speed
Vector2 v = (this.velocity.subtract(b.velocity));
float vn = v.dot(mtd.normalize());
// sphere intersecting but moving away from each other already
if (vn > 0.0f) return;
// collision impulse (1f is the coefficient of restitution)
float i = (-(1.0f + 1f) * vn) / (im1 + im2);
Vector2 impulse = mtd.multiply(i);
// change in momentum
this.velocity = this.velocity.add(impulse.multiply(im1));
b.velocity = b.velocity.subtract(impulse.multiply(im2));
}
Let me know if you find anything. Thanks
Is there a typo in the line that sets v1nPost? Looks like the denominator should be this.mass + b.mass, not this.mass * b.mass.
Also, because you're computing a collision between this and b, are you checking to make sure you're not also doing the same collision between b and this, thus doubling the delta applied to each participating bubble in the collision?
I do a first guess: getMass() return an integer(or int) (and not a float or double)?
If this is true, than you problem is that 1 / getMass() will result in an integer value (and can be only 1 or most time 0)). To fix this replace 1 by 1.0 or 1.0f
Because the general rule is simple:
If you have a math operation (+,-,*,/) the resulting type will be integer if none of the both operants is a floating point data structure (double or float)
Anyway: there could be a second problem, may your calcualtion is not precise enougth. Then you should use double instead of float.
There is a part that looks strange:
The two calculations:
float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass*b.mass);
float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);
are symmetric, except the last operation, in the first it is * in the second it is +

Categories