Moving a ball over a rotateable line.
Hello ppl....am trying to make a small 2D java game.....just a rookie in Java so really need ur help guys....in my game i ll be moving a ball over a rotateable line.....in the program i calculate the slope of the line n then move the ball accordingly with a velocity dependent on the angle of the line...the problem is i need the ball to remain on top of the line as i change the inclination of the line....be it a negative or a positive slope.....i tried using the intersects() method but its not precise enough....so the ball just sinks in.........i also tried extracting the pixel color using a Robot object n then tried comparing it with tat of the ball....still no luck....wud be relly grateful if u guys could help me out.....S.O.S
ppl = people
ur = your
wud = would
You *are* using a regular size keyboard, right?
To your question - this has nothing to do with Java, you just need the right physics simulation. Draw a diagram on a piece of paper, brush up on a little trigonometry from school and that's it. Nothing to do with Robot or intersects().
hello kirllig...
Thanks for "ur" reply and i believe i have given the right simulation but the Java methods have constraints due to which am not getting precision simulation....."n" "abt" the keyboard thing....man "u" were right...i have this funky keyboard which lets u type in these slangs easily....Have a nice day! :)
Message was edited by:
MyJavaStory
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
public class LocationTest extends JPanel implements ChangeListener
{
Ellipse2D.Double ball;
Line2D.Doubleline;
JSlider translate;
doublelastTheta = 0;
public void stateChanged(ChangeEvent e)
{
JSlider slider = (JSlider)e.getSource();
String name = slider.getName();
int value = slider.getValue();
if(name.equals("rotation"))
tilt(Math.toRadians(value));
else if(name.equals("translate"))
moveBall(value);
repaint();
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(ball == null)
initGeom();
g2.setPaint(Color.green.darker());
g2.draw(line);
g2.setPaint(Color.red);
g2.fill(ball);
}
private void initGeom()
{
int w = getWidth();
int h = getHeight();
int DIA = 30;
int padFromEnd = 5;
line = new Line2D.Double(w/4, h*15/16, w*3/4, h*15/16);
double x = line.x2 - padFromEnd - DIA;
double y = line.y2 - DIA;
ball = new Ellipse2D.Double(x, y, DIA, DIA);
// update translate slider values
int max = (int)line.getP1().distance(line.getP2());
translate.setMaximum(max);
translate.setValue(max-padFromEnd);
}
private void tilt(double theta)
{
// rotate line from left end
Point2D pivot = line.getP1();
double lineLength = pivot.distance(line.getP2());
Point2D.Double p2 = new Point2D.Double();
p2.x = pivot.getX() + lineLength*Math.cos(theta);
p2.y = pivot.getY() + lineLength*Math.sin(theta);
line.setLine(pivot, p2);
// find angle from pivot to ball center relative to line
// ie, ball center -> pivot -> line end
double cx = ball.getCenterX();
double cy = ball.getCenterY();
double pivotToCenter = pivot.distance(cx, cy);
// angle of ball to horizon
double dy = cy - pivot.getY();
double dx = cx - pivot.getX();
// relative angle phi = ball_to_horizon - last line_to_horizon
double phi = Math.atan2(dy, dx) - lastTheta;
// rotate ball from pivot
double x = pivot.getX() + pivotToCenter*Math.cos(theta+phi);
double y = pivot.getY() + pivotToCenter*Math.sin(theta+phi);
ball.setFrameFromCenter(x, y, x+ball.width/2, y+ball.height/2);
lastTheta = theta; // save theta for next time
}
private void moveBall(int distance)
{
Point2D pivot = line.getP1();
// ball touches line at distance from pivot
double contactX = pivot.getX() + distance*Math.cos(lastTheta);
double contactY = pivot.getY() + distance*Math.sin(lastTheta);
// find new center location of ball
// angle lambda = lastTheta - 90 degrees (anti-clockwise)
double lambda = lastTheta - Math.PI/2;
double x = contactX + (ball.width/2)*Math.cos(lambda);
double y = contactY + (ball.height/2)*Math.sin(lambda);
ball.setFrameFromCenter(x, y, x+ball.width/2, y+ball.height/2);
}
private JPanel getControls()
{
JSlider rotate = getSlider("rotation angle", "rotation", -90, 0, 0, 5, 15);
translate = getSlider("distance from end", "translate", 0, 100, 100,25, 50);
JPanel panel = new JPanel(new GridLayout(0,1));
panel.add(rotate);
panel.add(translate);
return panel;
}
private JSlider getSlider(String title, String name, int min, int max,
int value, int minorSpace, int majorSpace)
{
JSlider slider = new JSlider(JSlider.HORIZONTAL, min, max, value);
slider.setBorder(BorderFactory.createTitledBorder(title));
slider.setName(name);
slider.setPaintTicks(true);
slider.setMinorTickSpacing(minorSpace);
slider.setMajorTickSpacing(majorSpace);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args)
{
LocationTest test = new LocationTest();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(test);
f.getContentPane().add(test.getControls(), "Last");
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
}
}
Hey Philip! Thanks a lot of for all that code. Really appreciate it....however my scenario is slightly different... iam doing an animation...so a thread is involved. In my program, holding left click on the mouse gives a clockwise rotation n holding right click gives an anti-clockwise rotation. The repaint is called in the thread. Now the problem is I dont know how to get the coordinates of the line after i have rotated it because Iam using a rotate instance and then applying it to the transform method of Graphics2D.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.text.MessageFormat;
import javax.swing.*;
public class LocationTest2 extends JPanel
{
JLabel lineAngle;
Ellipse2D.Double ball;
Line2D.Double line;
Point origin = new Point();
double theta = 0;
double thetaInc = Math.toRadians(1);
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(ball == null)
initGeom();
AffineTransform at =
AffineTransform.getRotateInstance(theta, origin.x, origin.y);
g2.setPaint(Color.green.darker());
g2.draw(at.createTransformedShape(line));
g2.setPaint(Color.red);
g2.draw(at.createTransformedShape(ball));
showLineAngle(at);
}
private void showLineAngle(AffineTransform at)
{
Point2D p1 = line.getP1();
Point2D p2 = line.getP2();
Point2D xfp1 = new Point2D.Double();
Point2D xfp2 = new Point2D.Double();
at.transform(p1, xfp1);
at.transform(p2, xfp2);
double dy = xfp2.getY() - xfp1.getY();
double dx = xfp2.getX() - xfp1.getX();
double degrees = Math.toDegrees(Math.atan2(dy, dx));
if(degrees < 0)
degrees += 360;
String s = MessageFormat.format("{0,number,0.#}", degrees);
lineAngle.setText(s);
}
public void spin(Point origin)
{
this.origin = origin;
theta += thetaInc;
repaint();
}
private void initGeom()
{
int w = getWidth();
int h = getHeight();
int DIA = 30;
int padFromEnd = 5;
line = new Line2D.Double(w/4, h/2, w*3/4, h/2);
double x = line.x2 - padFromEnd - DIA;
double y = line.y2 - DIA;
ball = new Ellipse2D.Double(x, y, DIA, DIA);
}
private JPanel getLabel()
{
lineAngle = new JLabel(" ");
Dimension d = lineAngle.getPreferredSize();
d.width = 35;
lineAngle.setPreferredSize(d);
JPanel panel = new JPanel();
panel.add(new JLabel("line angle "));
panel.add(lineAngle);
return panel;
}
public static void main(String[] args)
{
LocationTest2 test = new LocationTest2();
test.addMouseListener(new SpinController());
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(test);
f.getContentPane().add(test.getLabel(), "Last");
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
}
}
class SpinController extends MouseAdapter implements Runnable
{
LocationTest2 panel;
Point spinOrigin;
Thread thread;
boolean spinning = false;
public void mousePressed(MouseEvent e)
{
if(panel == null)
panel = (LocationTest2)e.getSource();
spinOrigin = e.getPoint();
if(!spinning)
{
spinning = true;
thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
}
public void mouseReleased(MouseEvent e)
{
spinning = false;
thread = null;
}
public void run()
{
while(spinning)
{
try
{
Thread.sleep(40);
}
catch(InterruptedException ie)
{
System.out.println("interrupt");
spinning = false;
}
panel.spin(spinOrigin);
}
}
}
Good morning Philip.....Thanks again man..:-). Again there is a small probs....guess i didnt communicate everything to u...sorry abt tat....u see the ball dynamics is similar to a free falling body.....in the program i have written.....the ball falls under an approx of g=9.8 m/s2.....i am trying to give the ball an approx simulation of a free falling body. Now when the ball hits the line its does a bounce and then depending on the angle it has to move along the line and then fall off it.....just like how a ball would behave in the real world. Now am not lookin for a precision simulation.....just an approximation. my problem is that when i rotate the line the ball moves pretty ok but when i change the slope of the line when the ball is in motion, the y-directional motion is more than required n so its sinkin in....i guess it because of the approximations i have taken....not sure....thx in advance.....:)
Hello guys....need ur help...plss...cant move ahead without a solution for this problem of mine...calling out to all u java2D PROs.....thank you.:)
"HELP, HELP, HELP, HELP, HELP.............." guys is it somethin so complicated am askin? if u didnt get my problem do post ur queries n i shall answer them...:-)...had put in a lotta hopes for this...hey Philip....u there?
Some comments:
1 — Here's an attempt at showing how to accurately locate a falling ball as it
contacts a plane that can have an arbitrary rotation (about the view axis).
2 — I drew the floor in blue to help see the placement. The green line is the
rotating/tilted line. To keep things simple and restrict the focus to the ball–line
contact I did not implement any lateral displacement.
3 — There may be a lot of ways of doing this placement and you may find one that
suits you better. I have not included any construction lines or details to show how I did
the figuring in the tilt method.
4 — When the animation is not running the slider shows the ball tangent to both the
(green) line and (blue) floor through a range of rotation. Reset will get it ready for a
drop event.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
public class TrueContact extends JPanel implements ActionListener, ChangeListener
{
DropController dropController = new DropController(this);
Ellipse2D.Double ball;
Line2D.Doubleline;
doubleinitialX;
doublefloor;
doublelastTheta = 0;
doubleg = 3.0;
doubledLast;
doublevLast;
doublerestitution = 0.75;
public void actionPerformed(ActionEvent e)
{
String ac = e.getActionCommand();
if(ac.equals("drop"))
dropController.start();
else if(ac.equals("reset"))
{
dropController.stop();
ball.setFrame(initialX, 0, ball.width, ball.height);
vLast = 0;
dLast = 0;
repaint();
}
}
public void stateChanged(ChangeEvent e)
{
JSlider slider = (JSlider)e.getSource();
int value = slider.getValue();
tilt(Math.toRadians(value));
repaint();
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(ball == null)
initGeom();
g2.setPaint(Color.green.darker());
g2.draw(line);
g2.setPaint(Color.red);
g2.fill(ball);
g2.setPaint(Color.blue);
int w = getWidth();
g2.draw(new Line2D.Double(w/4, floor, w*3/4, floor));
}
private void initGeom()
{
int w = getWidth();
int h = getHeight();
int DIA = 30;
line = new Line2D.Double(w/4, h*7/8, w*3/4, h*7/8);
floor = line.getY1();
// Near the center is best for initial x value.
initialX = w*7/12.0;
double y = 0;
ball = new Ellipse2D.Double(initialX, y, DIA, DIA);
vLast = 0;
dLast = 0;
}
private void tilt(double theta)
{
// Rotate line at its center.
Point2D p1 = line.getP1();
Point2D p2 = line.getP2();
double length = p1.distance(p2);
Point2D.Double pivot = new Point2D.Double();
pivot.x = p1.getX() + (p2.getX() - p1.getX())/2;
pivot.y = p1.getY() + (p2.getY() - p1.getY())/2;
double x = pivot.getX() + (length/2)*Math.cos(theta);
double y = pivot.getY() + (length/2)*Math.sin(theta);
p2.setLocation(x, y);
x = pivot.x + (length/2)*Math.cos(theta+Math.PI);
y = pivot.y + (length/2)*Math.sin(theta+Math.PI);
p1.setLocation(x, y);
line.setLine(p1, p2);
// Adjust floor/ball height on tilted line in two parts:
// Locate ball center above the intersection of the tilted line
// and the vertical line that passes through the ball center.
double verticalOffset = -(ball.height/2)/Math.cos(theta) + ball.height/2;
// Find vertical displacement of intersection of tilted line
// and vertical line caused by rotation angle of tilted line.
double dx = pivot.x - ball.getCenterX();
double rotationOffset = dx*Math.tan(theta);
// Find floor == point of contact between ball and the horizontal.
floor = pivot.y + verticalOffset - rotationOffset;
// If the animation is not running we can adjust the
// vertical position of ball to rest on floor.
if(!dropController.isRunning())
ball.y = floor - ball.height;
lastTheta = theta;
}
public void dropBall()
{
// v = v0t + gt; t = 1
// d = d0 + v0t + (v - v0)t/2
double v = vLast + g;
double d = dLast + (v + vLast)/2.0;
double x = ball.getX();
double y = checkForContact(d);
ball.setFrameFromDiagonal(x, y, x + ball.width, y + ball.height);
repaint();
// If there was no contact with line, update state variables.
if(y == d)
{
dLast = d;
vLast = v;
}
}
private double checkForContact(double y)
{
if(y + ball.height >= floor) // contact
{
// Find velocity at contact.
// v^2 = v0^2 + 2gd
// v = sqrt(v0*v0 + 2gd)
double distanceToContact = floor - ball.height - dLast;
double contactVelocity = Math.sqrt(vLast*vLast + 2.0*g*distanceToContact);
// Apply coefficient of restitution to kinetic energy.
// ke = (mv^2)/2
double ke = (contactVelocity*contactVelocity)/2.0;
double keAfter = restitution*ke;
// Calculate new rebound velocity.
double reboundVelocity = -Math.sqrt(2.0*keAfter);
// Reset variables to reflect the contact with line.
y = floor - ball.height;
vLast = reboundVelocity;
dLast = y;
// Determine the rebound height after contact.
// v2^2 = v1^2 + 2gd
// d = (v1^2)/2g
double reboundHeight = (reboundVelocity*reboundVelocity)/(2.0*g);
// Stop animation if rebound height is small.
if(reboundHeight < 1.0)
{
dropController.stop();
y = floor - ball.height;
}
}
return y;
}
private JPanel getControls()
{
JSlider slider = new JSlider(JSlider.HORIZONTAL, -45, 45, 0);
slider.setPaintTicks(true);
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(15);
slider.setPaintLabels(true);
slider.addChangeListener(this);
JButton drop = new JButton("drop");
drop.setActionCommand("drop");
drop.addActionListener(this);
JButton reset = new JButton("reset");
reset.setActionCommand("reset");
reset.addActionListener(this);
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(1,0,1,0);
gbc.weightx = 1.0;
panel.add(drop, gbc);
gbc.gridwidth = gbc.REMAINDER;
panel.add(reset, gbc);
gbc.gridwidth = 2;
gbc.fill = gbc.HORIZONTAL;
panel.add(slider, gbc);
return panel;
}
public static void main(String[] args)
{
TrueContact test = new TrueContact();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(test);
f.getContentPane().add(test.getControls(), "Last");
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
}
}
class DropController implements Runnable
{
TrueContact trueContact;
Thread thread;
boolean animating;
public DropController(TrueContact tc)
{
trueContact = tc;
animating = false;
}
public void run()
{
while(animating)
{
try
{
Thread.sleep(100);
}
catch(InterruptedException ie)
{
System.out.printf("sleep interrupted%n");
animating = false;
}
trueContact.dropBall();
}
}
public void start()
{
if(!animating)
{
animating = true;
thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
}
public void stop()
{
if(animating)
{
animating = false;
thread.interrupt();
thread = null;
System.out.printf("thread stopped%n");
}
}
public boolean isRunning() { return animating; }
}
