I'm new to game physics and I am trying to adapt a simple 2D ball simulation for a 3D simulation with the Java3D library. I have this problem:
Two things:
1) I noted down the values generated by the engine: X/Y are too high and minX/minY/maxY/maxX values are causing trouble.
Sometimes the balls are drawing but not moving
Sometimes they are going out of the panel
Sometimes they're moving on little area
Sometimes they just stick at one place...
2) I'm unable to select/define/set the default correct/suitable values considering the 3D graphics scaling/resolution while they are set with respect to 2D screen coordinates, that is my only problem.
Please help.
This is the code:
public class Ball extends GameObject {
private float x, y; // Ball's center (x, y)
private float speedX, speedY; // Ball's speed per step in x and y
private float radius; // Ball's radius
// Collision detected by collision detection and response algorithm?
boolean collisionDetected = false;
// If collision detected, the next state of the ball.
// Otherwise, meaningless.
private float nextX, nextY;
private float nextSpeedX, nextSpeedY;
private static final float BOX_WIDTH = 640;
private static final float BOX_HEIGHT = 480;
/**
* Constructor The velocity is specified in polar coordinates of speed and
* moveAngle (for user friendliness), in Graphics coordinates with an
* inverted y-axis.
*/
public Ball(String name1,float x, float y, float radius, float speed,
float angleInDegree, Color color) {
this.x = x;
this.y = y;
// Convert velocity from polar to rectangular x and y.
this.speedX = speed * (float) Math.cos(Math.toRadians(angleInDegree));
this.speedY = speed * (float) Math.sin(Math.toRadians(angleInDegree));
this.radius = radius;
}
public void move() {
if (collisionDetected) {
// Collision detected, use the values computed.
x = nextX;
y = nextY;
speedX = nextSpeedX;
speedY = nextSpeedY;
} else {
// No collision, move one step and no change in speed.
x += speedX;
y += speedY;
}
collisionDetected = false; // Clear the flag for the next step
}
public void collideWith() {
// Get the ball's bounds, offset by the radius of the ball
float minX = 0.0f + radius;
float minY = 0.0f + radius;
float maxX = 0.0f + BOX_WIDTH - 1.0f - radius;
float maxY = 0.0f + BOX_HEIGHT - 1.0f - radius;
double gravAmount = 0.9811111f;
double gravDir = (90 / 57.2960285258);
// Try moving one full step
nextX = x + speedX;
nextY = y + speedY;
System.out.println("In serializedBall in collision.");
// If collision detected. Reflect on the x or/and y axis
// and place the ball at the point of impact.
if (speedX != 0) {
if (nextX > maxX) { // Check maximum-X bound
collisionDetected = true;
nextSpeedX = -speedX; // Reflect
nextSpeedY = speedY; // Same
nextX = maxX;
nextY = (maxX - x) * speedY / speedX + y; // speedX non-zero
} else if (nextX < minX) { // Check minimum-X bound
collisionDetected = true;
nextSpeedX = -speedX; // Reflect
nextSpeedY = speedY; // Same
nextX = minX;
nextY = (minX - x) * speedY / speedX + y; // speedX non-zero
}
}
// In case the ball runs over both the borders.
if (speedY != 0) {
if (nextY > maxY) { // Check maximum-Y bound
collisionDetected = true;
nextSpeedX = speedX; // Same
nextSpeedY = -speedY; // Reflect
nextY = maxY;
nextX = (maxY - y) * speedX / speedY + x; // speedY non-zero
} else if (nextY < minY) { // Check minimum-Y bound
collisionDetected = true;
nextSpeedX = speedX; // Same
nextSpeedY = -speedY; // Reflect
nextY = minY;
nextX = (minY - y) * speedX / speedY + x; // speedY non-zero
}
}
speedX += Math.cos(gravDir) * gravAmount;
speedY += Math.sin(gravDir) * gravAmount;
}
public float getSpeed() {
return (float) Math.sqrt(speedX * speedX + speedY * speedY);
}
public float getMoveAngle() {
return (float) Math.toDegrees(Math.atan2(speedY, speedX));
}
public float getRadius() {
return radius;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public void setX(float f) {
x = f;
}
public void setY(float f) {
y = f;
}
}
Here's how I'm drawing the balls:
public class 3DMovingBodies extends Applet implements Runnable {
private static final int BOX_WIDTH = 800;
private static final int BOX_HEIGHT = 600;
private int currentNumBalls = 1; // number currently active
private volatile boolean playing;
private long mFrameDelay;
private JFrame frame;
private int currentFrameRate;
private Ball[] ball = new Ball[currentNumBalls];
private Random rand;
private Sphere[] sphere = new Sphere[currentNumBalls];
private Transform3D[] trans = new Transform3D[currentNumBalls];
private TransformGroup[] objTrans = new TransformGroup[currentNumBalls];
public 3DMovingBodies() {
rand = new Random();
float angleInDegree = rand.nextInt(360);
setLayout(new BorderLayout());
GraphicsConfiguration config = SimpleUniverse
.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
ball[0] = new Ball(0.5f, 0.0f, 0.5f, 0.4f, angleInDegree, Color.yellow);
// ball[1] = new Ball(1.0f, 0.0f, 0.25f, 0.8f, angleInDegree,
// Color.yellow);
// ball[2] = new Ball(0.0f, 1.0f, 0.15f, 0.11f, angleInDegree,
// Color.yellow);
trans[0] = new Transform3D();
// trans[1] = new Transform3D();
// trans[2] = new Transform3D();
sphere[0] = new Sphere(0.5f);
// sphere[1] = new Sphere(0.25f);
// sphere[2] = new Sphere(0.15f);
// Create a simple scene and attach it to the virtual universe
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
startSimulation();
}
public BranchGroup createSceneGraph() {
// Create the root of the branch graph
BranchGroup objRoot = new BranchGroup();
for (int i = 0; i < currentNumBalls; i++) {
// Create a simple shape leaf node, add it to the scene graph.
objTrans[i] = new TransformGroup();
objTrans[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
Transform3D pos1 = new Transform3D();
pos1.setTranslation(randomPos());
objTrans[i].setTransform(pos1);
objTrans[i].addChild(sphere[i]);
objRoot.addChild(objTrans[i]);
}
BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
100.0);
Color3f light1Color = new Color3f(1.0f, 0.0f, 0.2f);
Vector3f light1Direction = new Vector3f(4.0f, -7.0f, -12.0f);
DirectionalLight light1 = new DirectionalLight(light1Color,
light1Direction);
light1.setInfluencingBounds(bounds);
objRoot.addChild(light1);
// Set up the ambient light
Color3f ambientColor = new Color3f(1.0f, 1.0f, 1.0f);
AmbientLight ambientLightNode = new AmbientLight(ambientColor);
ambientLightNode.setInfluencingBounds(bounds);
objRoot.addChild(ambientLightNode);
return objRoot;
}
public void startSimulation() {
playing = true;
Thread t = new Thread(this);
t.start();
}
public void stop() {
playing = false;
}
public void run() {
long previousTime = System.currentTimeMillis();
long currentTime = previousTime;
long elapsedTime;
long totalElapsedTime = 0;
int frameCount = 0;
while (true) {
currentTime = System.currentTimeMillis();
elapsedTime = (currentTime - previousTime); // elapsed time in
// seconds
totalElapsedTime += elapsedTime;
if (totalElapsedTime > 1000) {
currentFrameRate = frameCount;
frameCount = 0;
totalElapsedTime = 0;
}
for (int i = 0; i < currentNumBalls; i++) {
ball[i].move();
ball[i].collideWith();
drawworld();
}
try {
Thread.sleep(88);
} catch (Exception e) {
e.printStackTrace();
}
previousTime = currentTime;
frameCount++;
}
}
public void drawworld() {
for (int i = 0; i < currentNumBalls; i++) {
printTG(objTrans[i], "SteerTG");
trans[i].setTranslation(new Vector3f(ball[i].getX(),
ball[i].getY(), 0.0f));
objTrans[i].setTransform(trans[i]);
}
}
private Vector3f randomPos()
/*
* Return a random position vector. The numbers are hardwired to be within
* the confines of the box.
*/
{
Vector3f pos = new Vector3f();
pos.x = rand.nextFloat() * 5.0f - 2.5f; // -2.5 to 2.5
pos.y = rand.nextFloat() * 2.0f + 0.5f; // 0.5 to 2.5
pos.z = rand.nextFloat() * 5.0f - 2.5f; // -2.5 to 2.5
return pos;
} // end of randomPos()
public static void main(String[] args) {
System.out.println("Program Started");
3DMovingBodiesbb = new 3DMovingBodies();
bb.addKeyListener(bb);
MainFrame mf = new MainFrame(bb, 600, 400);
}
}