Pendulum simulation only performing one period - java

I am making a simulation of a pendulum, but it only performs one swing before sending close to random positions for the bob to be at. Essentially, it does not go backwards.
I have tried to change the direction using the goingForward boolean, but it still doesnt work.
public class AnimationPane extends JPanel {
// START CHANGEABLE VARIABLES
private double startAngle = -60.0; // degrees
private double mass = 1; // kilogrammes
private int radius = 10; // m
private double gravity = 9.80665; // m/s^2 // on earth: 9.80665
// END CHANGEABLE VARIABLEs
private BufferedImage ball;
private BufferedImage rope;
private int pointX = 180;
private int pointY = 50;
private double endAngle = Math.abs(startAngle); // absolute value of startAngle
private double angle = startAngle; // current angle
private double circum = (2 * Math.PI * radius); // m
private double distance = 0; // m
private double velocity = 0; // m/s
private double totalEnergy = ((radius) - (Math.cos(Math.toRadians(angle)) * radius)) * gravity * mass + 0.00001;
private double previousE;
private int xPos = 0; // for program
private int yPos = 0; // for program
private boolean goingForward = true;
private double height = 0;
public AnimationPane() {
try {
ball = ImageIO.read(new File("rsz_black-circle-mask-to-fill-compass-outline.png"));
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
double angleRad = Math.toRadians(Math.abs(angle));
double potentialE = ((radius) - (Math.cos(angleRad) * radius)) * gravity * mass;
Double pE = new Double(potentialE);
height = (radius - (Math.cos(angleRad) * radius));
double kineticE = totalEnergy - pE;
if (kineticE <= 0 || angle >= endAngle) {
if (goingForward == true) {
goingForward = false;
}
else
{
goingForward = true;
}
kineticE = 0.1;
angle = 60;
}
velocity = Math.sqrt(2 * kineticE / mass);
double ratio = distance / circum;
if (goingForward == true) {
distance = distance + (velocity / 10);
angle = startAngle + (360 * ratio);
}
else {
distance = distance - (velocity / 10);
angle = startAngle - (360 * ratio);
}
double angles = Math.toRadians(angle);
double xDouble = Math.sin(angles) * (radius * 10);
Double x = new Double(xDouble);
xPos = x.intValue() + 150;
double yDouble = Math.cos(angles) * (radius * 10);
Double y = new Double(yDouble);
yPos = y.intValue() + 50;
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(xPos + 20, yPos + 20, pointX, pointY);
g.drawImage(ball, xPos, yPos, this);
}
}
I would really appreciate some help getting this to work.
Thank you

I could not debug your code, which was uneasy to work with and sometimes to understand (you use a lot of integer literals in your code, which hides their semantic, I have no idea what was your intention on some statements).
Therefore, I rewrote it using the solution of the differential equation for small oscillations. It works, you can take it as a clean base to implement it again the way you wanted. Note that as Andy Turner pointed it, you should not have to worry about the fact of going forward or backward. You have an equation, you solve it, it gives you the position of the ball at any time. If you want something which is accurate for large angles, I suggest you go on Wikipedia to see the movement equation in this case. Last option, you could numerically solve the differential equation although I would personally don't know how to do it at first glance.
package stackoverflow;
public class AnimationPane extends JPanel {
private static final long serialVersionUID = 1L;
private static final double GRAVITY = 9.80665;
private BufferedImage ball;
private final Point fixedCordPoint;
private final int cordLength;
private final double startAngle;
private double currentAngle;
private final double pulsation;
private final Point ballPos = new Point();
private int time = 1;
public AnimationPane(Point fixedCordPoint, int cordLength, double startAngleRadians) {
this.fixedCordPoint = new Point(fixedCordPoint);
this.cordLength = cordLength;
this.pulsation = Math.sqrt(GRAVITY / cordLength);
this.startAngle = startAngleRadians;
this.currentAngle = startAngleRadians;
this.ball = loadImage(new File("ball.jpg"));
}
private BufferedImage loadImage(File file) {
try {
return ImageIO.read(file);
} catch (IOException e) {
throw new RuntimeException("Could not load file : " + file, e);
}
}
public void start() {
Timer timer = new Timer(100, event -> {
ballPos.x = fixedCordPoint.x + (int) Math.round(Math.sin(currentAngle) * cordLength);
ballPos.y = fixedCordPoint.y + (int) Math.round(Math.cos(currentAngle) * cordLength);
repaint();
currentAngle = startAngle * Math.cos(pulsation * time);
time++;
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(ballPos.x, ballPos.y, fixedCordPoint.x, fixedCordPoint.y);
g.drawImage(ball, ballPos.x - ball.getWidth() / 2, ballPos.y - ball.getHeight() / 2, this);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
AnimationPane pendulumAnimationPane = new AnimationPane(new Point(160, 25), 180, - Math.PI / 10);
frame.setContentPane(pendulumAnimationPane);
frame.setSize(400,400);
frame.setVisible(true);
pendulumAnimationPane.start();
}
}

I really can't follow your code. If I had to say where I thought the problem is, I would say that it is the distance variable, and its consequent use in ratio: this seems to be defined as being zero when the pendulum starts. When you switch the direction of the pendulum, I think that it needs to be rebased to zero... Somehow.
You should also separate out your GUI code from your simulation. You can just make it print out the x and y coordinates while you are debugging. For instance, you can write the values to a CSV file, so you can visualize them in Matlab (or whatever) to ensure that your simulation looks right over time.
I would make two suggestions to help you with the simulation:
Don't model it in terms of energy, which is a scalar quantity, meaning that you have to use additional state like a "direction flag" to model which way it is going;
You are going to need an additional state variable. Storing x and y is redundant, since these can be derived directly from the angle (and R, although that is constant). You are modelling a second-order system, so you need that second state variable which models the movement of the system at an instant, as well as its position. For example, you could model the angle and angular velocity over time.
And, of course, don't mess around with degrees - stick to radians :)
(On the matter of velocity, your variable of that name is actually speed - you don't know which direction it moves in, and that information is highly relevant to the dynamics of the system).
The derivation of the method using angle and angular velocity is quite straightforward, although my maths is rusty enough to caution its use without checking carefully!
The rotational form of Newton's second law of motion is:
Torque = moment of inertia * angular acceleration
The torque on the bob is given by -mgR sin angle (m is mass, g is gravity, R is length of pendulum, angle is the angular displacement of the pendulum from vertical). The minus sign is because the force tends to return the bob to vertical;
The moment of inertia of a point mass is mR^2 - this is a reasonable approximation if the pendulum is very long compared to the radius of the bob.
Therefore the angular acceleration is -g sin theta / R. Hopefully this should look like simple harmonic motion - an acceleration proportional to the distance from an equilibrium (for small theta, sin theta is approximately theta) and directed towards it.
You can now put this together into a numerical scheme to simulate the pendulum. For example, using Euler's method:
New angle = old angle + dt * old angular velocity
New angular velocity = old angular velocity vel - dt * g * sin(angle) / R
where dt is your time step.
You can, of course, use higher-order methods like Runge-Kutta to reduce the numerical errors in the simulation, but it is (slightly) more involved.

Related

Rotating Polygon Objects

Rotating Asteroids ( Polygons )
I am trying to rotate asteroids(polygons) so that they look nice. I am doing this through multiple mathematical equations. To start I give the individual asteroid a rotation velocity:
rotVel = ((Math.random()-0.5)*Math.PI/16);
Then I create the polygon shape,
this.shape = new Polygon();
Followed by generating the points,
for (j = 0; j < s; j++) {
theta = 2 * Math.PI / s * j;
r = MIN_ROCK_SIZE + (int) (Math.random() * (MAX_ROCK_SIZE - MIN_ROCK_SIZE));
x = (int) -Math.round(r * Math.sin(theta)) + asteroidData[0];
y = (int) Math.round(r * Math.cos(theta)) + asteroidData[1];
shape.addPoint(x, y);
}
Finally, in a loop a method is being called in which it attempts to move the polygon and its points down as well as rotating them. (I'm just pasting the rotating part as the other one is working)
for (int i = 0; i < shape.npoints; i++) {
// Subtract asteroid's x and y position
double x = shape.xpoints[i] - asteroidData[0];
double y = shape.ypoints[i] - asteroidData[1];
double temp_x = ((x * Math.cos(rotVel)) - (y * Math.sin(rotVel)));
double temp_y = ((x * Math.sin(rotVel)) + (y * Math.cos(rotVel)));
shape.xpoints[i] = (int) Math.round(temp_x + asteroidData[0]);
shape.ypoints[i] = (int) Math.round(temp_y + asteroidData[1]);
}
now, the problem is that when it prints to the screen the asteroids appear to 'warp' or rather the x and y positions on some of the polygon points 'float' off course.
I've noticed that when I make 'rotVel' be a whole number the problem is solved however the asteroid will rotate at mach speeds. So I've concluded that the problem has to be in the rounding but no matter what I do I can't seem to find a way to get it to work as the Polygon object requires an array of ints.
Does anyone know how to fix this?
Currently your asteroids rotate around (0 , 0) as far as i can see. Correct would be to rotate them around the center of the shape, which would be (n , m), where n is the average of all x-coordinates of the shape, and m is the average of all y-coordinates of the shape.
Your problem is definitely caused by rounding to int! The first improvement is to make all shape coordinates to be of type double. This will solve most of your unwanted 'effects'.
But even with double you might experience nasty rounding errors in case you do a lot of very small updates of the coordinates. The solution is simple: Just avoid iterative updates of the asteroid points. Every time, you update the coordinates based on the previous coordinates, the rounding error will get worse.
Instead, add a field for the rotation angle to the shape and increment it instead of the points themselves. Not until drawing the shape, you compute the final positions by applying the rotation to the points. Note that this will never change the points themselves.
You can extend this concept to other transformations (e.g. translation) too. What you get is some kind of local coordinate system for every shape/object. The points of the shape are defined in the local coordinate system. By moving and rotating this system, you can reposition the entire object anywhere in space.
public class Shape {
// rotation and position of the local coordinate system
private double rot, x, y;
// points of the shape in local coordinate system
private double[] xp, yp;
private int npoints;
// points of the shape in world coordinates
private int[][] wxp, wyp;
private boolean valid;
public void setRotation(double r) { this.rot = r; valid = false; }
public void setPosition(double x, double y) { this.x = x; this.y = y; valid = false; }
public void addPoint(double x, double y) {
// TODO: add point to xp, yp
valid = false;
}
public void draw(...) {
if (!valid) {
computeWorldCoordinates(wxp, wyp);
valid = true;
}
// TODO: draw shape at world coordaintes wxp and wyp
}
protected void computeWorldCoordinates(int[] xcoord, int[] ycoord) {
for (int i = 0; i < npoints; i++) {
double temp_x = xp[i] * Math.cos(rot) - yp[i] * Math.sin(rot);
double temp_y = xp[i] * Math.sin(rot) + yp[i] * Math.cos(rot);
xcoord[i] = (int) Math.round(x + temp_x);
ycoord[i] = (int) Math.round(y + temp_y);
}
}
}

Java 2d game - Issues with delta time and collision

I'm trying to make a java 2d game, and it seems to work out fine in general. The only problem is, that I can't figure out how to place my "delta" time, to make the movements move the same on 30 FPS as on 1000 FPS.
This is my code for the Entity class:
import java.awt.Rectangle;
import map.Tile;
import graphics.Sprite;
public class Entity {
private String name;
private float positionx, positiony; // Current coordinate
private int targetx,targety; // Target coordinate
private double vx, vy; // Current vector
private double lx, ly; // Last vector
private float speed;
private Sprite sprite;
public Entity(String name, int x, int y, Sprite sprite){
this.name = name;
this.speed = 1f;
this.positionx = x;
this.positiony = y;
this.sprite = sprite;
main.Main.e.addEntity(this); // These kind of calls are ugly, and should be fixed.
}
public void remove(){
main.Main.e.removeEntity(this);
sprite.remove();
}
public void setVector(double vx, double vy){
this.vx = vx;
this.vy = vy;
}
public void update(long delta){
//Multiply modifier to make it act the same on 30 fps as 1000 fps.
vx = vx*delta;
vy = vy*delta;
// Calculate vector
double distance = Math.sqrt((vx * vx) + (vy * vy));
if(distance > 0){ // Have we reached the target yet?
vx = ((vx / distance));
vy = ((vy / distance));
}else{
vx = 0;
vy = 0;
}
//Check collision with objects:
Rectangle rx = new Rectangle((int) (vx+positionx), (int)positiony, 32, 32);
Rectangle ry = new Rectangle((int) positionx, (int)(vy+positiony), 32, 32);
for(Entity e : main.Main.e.getEntities()){
if(this != e){
if(isIntersecting(rx, e.getBounds())){
vx = 0; // Disallow x direction.
}
if(isIntersecting(ry, e.getBounds())){
vy = 0; // Disallow y direction.
}
}
}
//Check tiles:
for(Tile t : main.Main.m.getNeighbours(positionx,positiony)){
if(t.isBlocking()){
if(isIntersecting(rx, t.getBounds())){
vx = 0;
}
if(isIntersecting(ry, t.getBounds())){
vy = 0;
}
}
}
//Update the position:
positionx += vx*speed;
positiony += vy*speed;
//Animate:
animate(vx, vy);
}
public boolean isIntersecting(Rectangle r1, Rectangle r2){
return r1.intersects(r2);
}
public Rectangle getBounds(){
return new Rectangle((int) positionx,(int) positiony,32,32);
}
public void setMoveTo(int x, int y){
this.targetx = x;
this.targety = y;
}
//This function is used by the bots, and not on players (they are setting the vector and use update directly):
public void moveTo(long delta){
setVector((targetx-positionx),(targety-positiony));
update(delta);
}
public void animate(double dx, double dy){
sprite.setPosition((int)positionx, (int)positiony);
if(dx > 0){
sprite.setAnimation(0, 7, 100); // Walk right.
}else if(dx < 0){
sprite.setAnimation(1, 7, 100); // Walk left.
}else{
if(lx > 0){
sprite.setAnimation(2, 3, 200); // Stand right.
}else if(lx < 0){
sprite.setAnimation(3, 3, 200); // Stand left.
}
}
lx = dx;
ly = dy;
}
}
The two problems, that I always run into:
1# The game runs differently on 60FPS than on 500FPS.
2# The game runs the same on 60FPS as 500FPS, but my collision screws up, and I can't move closer than 15px from the other object. I think I need to implement something like: If I can't get 10px closer, then move it 10px closer, but I don't know how to implement it.
How can I implement it correctly? That would help a lot!
The easiest way would be to consider that if you want a constant displacement with a constant velocity you can scale all the deltas relative to the delta of 30 FPS like so:
So if you run at 60 FPS but want the same displacement as on 30FPS alpha would be (1/30)/(1/60) = 2
So
Remove vx = vx*delta;
vy = vy*delta;
and change your position update to
alpha = (1.0/30)*delta;
positionx += alpha*vx*speed;
positiony += alpha*vy*speed;
This is only a crude solution, let me know how it works, I will drop in later and update for a more correct solution (taking into account that delta is not always 1/FPS due to rendering and computation taking some time).
Edit: Variable delta will come later. But some optimizations:
You don't need to construct a rectangle in both Y and X for collisiondetection, try drawing two rectangles, if they intersect they do so on both axis. Just create a rectangle from vx + posx, vy+posy and check for intersection with this.
The above should half your collisionchecks. For further optimization consider using a QuadTree an implementation can be found here.
For the problem of "blocky" collision testing (where you stop X pixels from the blocking object). You can do the following, in pseudocode:
if(new position will make this colide)
while(this.position is more than one pixel away from blocking object)
keep moving one pixel
This will make you stop 1px from the target.

final variable keeps changing but shouldnt

I am engineering a program for my bachelor thesis that displays the inner geometry of a steel wire rope depending on the z location (location in the cable).
To test I "walk through" a piece of the cable, with this piece of code (sl is a strand list, initialized already, that works fine):
Cable c = new Cable(sl);
ImageFrame ts = new ImageFrame(c);
try {
while (location <2 ) {
location = location + 0.01;
Thread.sleep(100);
c.update(location);
ts.repaint();
}
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
The cables function update just should recalculate the geometry of the cable. My class cable has a list of strands, and what this update function does, is just calling the update function for all these strands:
public void update(double z) {
for(int i=0;i<strandList.size() ;i++) {
strandList.get(i).updateStrand(z);
}
}
The object Strand looks like this:
public abstract class Strand {
Punt middenCable;
final Punt nulpoint_midden;
Punt midden;
Double angle;
Double d_to_core;
Image img;
int img_size;
BufferedImage bf;
/**
*
* #param c centre of the cable
* #param p middle of the strand
* #param ang angle at this point
* #param dtc distance to core
* #param i image
*/
public Strand(Punt c,Punt p,Image i) {
nulpoint_midden = p; //location of strand at z=0
middenCable =c;
midden = p; // for drawing
img = i;
img_size = 135; //TODO adapt
bf = new BufferedImage(img_size, img_size, BufferedImage.TRANSLUCENT );
bf.getGraphics().drawImage(img, 0, 0, null);
}
/**
* angle goed zetten tov de z
* #param z
*/
abstract public void updateStrand(Double z);
public void paint(Graphics g){
g.setColor(Color.RED); //cirkels rood
//rotate around itself
int x = (int) (this.midden.getX() - img_size/2 );
int y = (int) (this.midden.getY() - img_size/2 );
int st = (int) this.img_size;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
AffineTransform at = new AffineTransform();
at.setToRotation(Math.toRadians(angle), midden.getX(), midden.getY());
g2d.setTransform(at);
g2d.drawImage(bf, x, y, null);
g2d.dispose();
}
In the paint-function it rotate around itself with a certain angle. But the problem is the abstract function "update strand". What this should do is change the coordinates of the punt Midden to the location it actually is at. The parameter nulpoint_midden should not change and be constant, for calculation. For the object type outer strand (extends strand) the updatestrand is like beneath:
import java.awt.Image;
public class OuterStrand extends Strand{
private double rotateconstant1 = 10; //tweaken tot perfecte laylength
private double rotateconstant2 = (2*Math.PI/34); //tweaken tot perfecte laylength
public OuterStrand(Punt c,Punt p, Image i) {
super(c, p,i);
}
#Override
public void updateStrand(Double z) {
// midden updaten
double xpoint = nulpoint_midden.getX(); //original point
double ypoint = nulpoint_midden.getY(); //original point
double cCableX = super.middenCable.getX();
double cCableY = super.middenCable.getY();
double dx = nulpoint_midden.getX() - cCableX;
double dy = ypoint - cCableY;
System.out.println(xpoint+ " " +ypoint) ;
double curangle = Math.atan2(dy, dx);
double dist = Math.sqrt(dx*dx + dy*dy);
double dangle = rotateconstant2 * z;
double x1 = cCableX + dist * Math.cos(dangle + curangle);
double y1 = cCableY + dist * Math.sin(dangle + curangle);
super.midden.setX(x1);
super.midden.setY(y1);
// rotate around itself
super.angle = z * Math.PI * rotateconstant1;
}
}
So the println gives the xpoint and ypoint, and this should be constant. But actually, it changes (so the punt nulpoint_midden changes value). I have no idea why this value changes. here is my print:
> 500.0 200.0
> 500.55439838802135 200.00051226305845
> 501.66318759079536 200.00461035702926
> 503.32632406233347 200.0184412864145
> 505.54367148773895 200.0512248625842
> 508.3149156018249 200.11525184075379
> 511.63945065512087 200.22587972200301
> 515.5162375992163 200.40152475227
> 519.9436341271526 200.66364828540742
> 524.9191967987913 201.03673531535162
> 530.439455611731 201.54826262515832
> 536.4996615512757 202.22865365075 (etcetera)
Because of this changing values, the representation is not correct. Due to the walking through the cable, it should rotate at a constant speed, but is actually accelerating.
I am sorry for the long explanation, but if someone sees my mistake and tells me what i am doing wrong that would be great. Thanks!
I think you are confusing with the meaning of final modifier.
This modifier means that assignment to this variable can be done only once. This is controlled by compiler. You simply cannot compile code that tries to change value of final variable. However if variable is mutable you can change it state by accessing its members (fields or methods).
BTW, could you please next time try to create shorter code snippets? I am sorry to say it but you are asking trivial question and posting tons of absolutely irrelevant code.
The final keyword means the reference may be assigned only once, but it doesn't mean that the object being referenced is somehow prevented from having its values changed.
There's a potential bug in your constructor:
nulpoint_midden = p;
midden = p;
The two instance variables refer to the same object: if you call a setter on midden, nulpoint_midden will appear to change too.
The final variable nulpoint_midden refers to the same object as the variable midden, so changes to the object referred to by midden are also visible via nulpoint_modden.

Simulate the gravitational pull of a star?

I'm making a game where the player will (on release of mouseclick) shoot a "star" in a certain direction at an initial speed determined by how far he dragged the mouse before releasing. I have a "planet" (stationary circle) on the canvas that I want to exert a gravitational pull on the moving planet. I believe I'm using the right formulas for gravitational force and such, and I have it partially working - the planet affects the planet's trajectory up until a certain point, when the star seems to endlessly speed up and stop changing direction based on it's angle to the star. Any advice? (I know that stars aren't supposed to orbit planets, it's the other way around. I coded the whole thing with the names interchanged so forgive that).
main class:
import acm.graphics.GCompound;
import acm.graphics.GImage;
import acm.graphics.GLabel;
import acm.graphics.GLine;
import acm.graphics.GMath;
import acm.graphics.GObject;
import acm.graphics.GPen;
import acm.graphics.GPoint;
import acm.graphics.GRect;
import acm.graphics.GOval;
import acm.graphics.GRectangle;
import acm.program.GraphicsProgram;
import acm.util.RandomGenerator;
import java.awt.Color;
import java.awt.event.MouseEvent;
import java.util.*;
public class Space extends GraphicsProgram {
public static int APPLICATION_WIDTH = 1000;
public static int APPLICATION_HEIGHT = 1000;
private int size = 15;
public static double pMass = 1000;
public static int sMass = 20;
public static double G = 200;
private RandomGenerator rand = new RandomGenerator();
GOval planet, tempstar;
shootingStar star;
GLine line;
double accel, xAccel, yAccel, xspeed, yspeed, angle;
public void init(){
planet = new GOval(APPLICATION_WIDTH/2, APPLICATION_HEIGHT/2, 30, 30);
planet.setFilled(true);
planet.setFillColor(rand.nextColor());
add(planet);
}
public void mousePressed(GPoint point) {
// draw a line
tempstar = new GOval(point.getX() - size/2, point.getY() - size/2, size, size);
tempstar.setFilled(true);
tempstar.setColor(rand.nextColor());
add(tempstar);
line = new GLine(tempstar.getX() + size/2, tempstar.getY() + size/2,
point.getX(), point.getY());
add(line);
line.setVisible(true);
}
public void mouseDragged(GPoint point) {
line.setEndPoint(point.getX(), point.getY());
}
public void mouseReleased(GPoint point){
xspeed =
-.05*GMath.cosDegrees(getAngle(line))*GMath.distance(line.getStartPoint().getX(),
line.getStartPoint().getY(), line.getEndPoint().getX(), line.getEndPoint().getY());
yspeed =
.05*GMath.sinDegrees(getAngle(line))*GMath.distance(line.getStartPoint().getX(),
line.getStartPoint().getY(), line.getEndPoint().getX(), line.getEndPoint().getY());
System.out.println(xspeed + " " + yspeed);
star = new shootingStar(xspeed, yspeed, this);
if(xspeed != 0)
add(star, tempstar.getX(), tempstar.getY());
new Thread(star).start();
remove(tempstar);
remove(line);
}
private double getAngle(GLine line) {
return GMath.angle(line.getStartPoint().getX(), line.getStartPoint().getY(),
line.getEndPoint().getX(), line.getEndPoint().getY());
}
public void checkPlanet(){
accel = .06*GMath.distance(star.getX(), star.getY(), planet.getX(),
planet.getY());
angle = correctedAngle(GMath.angle(planet.getX(), planet.getY(), star.getX(),
star.getY()));
xAccel = accel*GMath.cosDegrees(GMath.angle(planet.getX(), planet.getY(),
star.getX(), star.getY()));
yAccel = accel*GMath.sinDegrees(GMath.angle(planet.getX(), planet.getY(),
star.getX(), star.getY()));
double newX = xspeed - xAccel*.01;
double newY = yspeed + yAccel*.01;
xspeed = newX + xAccel*Math.pow(.01, 2)/2;
yspeed = newY + yAccel*Math.pow(.01, 2)/2;
star.setSpeed(xspeed, yspeed);
}
public double correctedAngle(double x) {
return (x%360.0+360.0+180.0)%360.0-180.0;
}
}
Pertinent parts of shootingStar class:
public void run() {
// move the ball by a small interval
while (alive) {
oneTimeStep();
}
}
// a helper method, move the ball in each time step
private void oneTimeStep() {
game1.checkPlanet();
shootingStar.move(xSpeed, ySpeed);
pause(20);
}
public void setSpeed (double xspeed, double yspeed){
xSpeed = xspeed;;
ySpeed = yspeed;
}
}
EDIT:
Current Main Class Method:
public void checkPlanet(){
double xDistance = star.getX() - planet.getX();
double yDistance = star.getY() - planet.getY();
double distance = Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
accel = G*pMass/Math.pow(distance, 2);
xAccel = accel * xDistance/distance;
yAccel = accel * yDistance/distance;
xspeed += xAccel;
yspeed += yAccel;
star.setSpeed(xspeed, yspeed);
}
Current Star class Method:
public void run() {
while (alive) {
oneTimeStep();
}
}
private void oneTimeStep() {
game1.checkPlanet();
shootingStar.move(xSpeed, ySpeed);
pause(20);
}
public void setSpeed (double xspeed, double yspeed){
xSpeed = xspeed;;
ySpeed = yspeed;
}
}
Wow, that's a lot more effort than what you "HAVE" to do.
If the thing is on the board calculate it's distance from the object. If it's farther away than D do nothing. If it's D away then it's within the objects gravitational pull. Just add a small amount of velocity to it pointing at the object. Let's say it was 1000 X away and 500 z away. Just do something simple like divide by 100, and add that to the objects velocity so it moves 10 x and 5 y towards the object. Each time you update add the velocity again.
You'll probably want a maximum velocity also. This is a LOT easier to calculate works well, and will give you effects like in the game STAR CONTROL where there's a planet, or the ships gravitationally pull towards each other a tiny bit. I did this with 10 planets and a star, and the user could basically do lunar lander with each planet. It was a blast but I never turned it into a real game. This has the advantage of being rockingly fast to calculate. There some edge conditions like if you make the map a torus so they warp through the sides of the map, but basically it's all just simple addition and subtraction.
It's GOOD enough for a game. You're not making a simulator. You're making a game.
I am not sure, but try to change the part where you calculate the xAccel and yAccel values to something like this.
xDistance = XComponentObject1 - XComponentObject2;
yDistance = YComponentObject1 - YComponentObject2;
(xDistance and yDistance can have negative values)
Distance = sqrt( xDistance^2 + yDistance^2 );
gConstant = constant Value for gravitational strenght in your world;
MassObject1 = some Mass;
MassObject2 = some other Mass;
Accel = gConstant*MassObject1*MassObject2 / (Distance^2 );
''NOW COMES THE IMPORTANT PART''
xAccel = Accel * xDistance/Distance;
yAccel = Accel * yDistance/Distance;
I think your whole yadayada with sine and cosine creates a whole bunch of hard-to-trackdown-errors.

Polygon Intersection fails, collision "size" too big

OK, so I'm trying to make a simple asteroids clone. Everything works fine, except for the collision detection.
I have two different versions, the first one uses java.awt.geom.Area:
// polygon is a java.awt.Polygon and p is the other one
final Area intersect = new Area();
intersect.add(new Area(polygon));
intersect.intersect(new Area(p.polygon));
return !intersect.isEmpty();
This works like a charm... if you don't care about 40% CPU for only 120 asteroids :(
So I searched the net for the famous separating axis theorem, since I'm not thaaaaaat good a the math I took the implementation from here and converted it to fit my Java needs:
public double dotProduct(double x, double y, double dx, double dy) {
return x * dx + y * dy;
}
public double IntervalDistance(double minA, double maxA, double minB,
double maxB) {
if (minA < minB) {
return minB - maxA;
} else {
return minA - maxB;
}
}
public double[] ProjectPolygon(double ax, double ay, int p, int[] x, int[] y) {
double dotProduct = dotProduct(ax, ay, x[0], y[0]);
double min = dotProduct;
double max = dotProduct;
for (int i = 0; i < p; i++) {
dotProduct = dotProduct(x[i], y[i], ax, ay);
if (dotProduct < min) {
min = dotProduct;
} else if (dotProduct > max) {
max = dotProduct;
}
}
return new double[] { min, max };
}
public boolean PolygonCollision(Asteroid ast) {
int edgeCountA = points;
int edgeCountB = ast.points;
double edgeX;
double edgeY;
for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) {
if (edgeIndex < edgeCountA) {
edgeX = xp[edgeIndex] * 0.9;
edgeY = yp[edgeIndex] * 0.9;
} else {
edgeX = ast.xp[edgeIndex - edgeCountA] * 0.9;
edgeY = ast.yp[edgeIndex - edgeCountA] * 0.9;
}
final double x = -edgeY;
final double y = edgeX;
final double len = Math.sqrt(x * x + y * y);
final double axisX = x / len;
final double axisY = y / len;
final double[] minMaxA = ProjectPolygon(axisX, axisY, points, xp,
yp);
final double[] minMaxB = ProjectPolygon(axisX, axisY, ast.points,
ast.xp, ast.yp);
if (IntervalDistance(minMaxA[0], minMaxA[1], minMaxB[0], minMaxB[1]) > 0) {
return false;
}
}
return true;
}
It works... kinda. Actually it seems that the "collision hull" of the asteroids is too big when using this code, it's like 1.2 times the size of the asteroid. And I don't have any clue why.
Here are two pictures for comparison:
http://www.spielecast.de/stuff/asteroids1.png
http://www.spielecast.de/stuff/asteroids2.png
As you can hopefully see, the asteroids in picture one are much denser than the ones in picture 2 where is use the SAT code.
So any ideas? Or does anyone knows a Polygon implementation for Java featuring intersection tests that I could use?
It looks like your second result is doing collision detection as if the polygons were circles with their radius set to the most distant point of the polygon from the center. Most collision detection stuff I've seen creates a simple bounding box (either a circle or rectangle) into which the polygon can fit. Only if two bounding boxes intersect (a far simpler calculation) do you continue on to the more detailed detection. Perhaps the appropriated algorithm is only intended as a bounding box calculator?
EDIT:
Also, from wikipedia
The theorem does not apply if one of the bodies is not convex.
Many of the asteroids in your image have concave surfaces.

Categories