I'm just trying to code a nice looking physics game.
The ball collision looks nice but if the balls are colliding too slow, they "stick" in each other. I have no clue why they do.
Here's my collision function:
private void checkForCollision(ArrayList<Ball> balls) {
for (int i = 0; i < balls.size(); i++) {
Ball ball = balls.get(i);
if (ball != this && ball.intersects(this)) {
this.collide(ball, false);
}
}
}
public boolean intersects(Ball b) {
double dx = Math.abs(b.posX - posX);
double dy = Math.abs(b.posY - posY);
double d = Math.sqrt(dx * dx + dy * dy);
return d <= (radius + b.radius);
}
private void collide(Ball ball, boolean b) {
double m1 = this.radius;
double m2 = ball.radius;
double v1 = this.motionX;
double v2 = ball.motionX;
double vx = (m1 - m2) * v1 / (m1 + m2) + 2 * m2 * v2 / (m1 + m2);
v1 = this.motionY;
v2 = ball.motionY;
double vy = (m1 - m2) * v1 / (m1 + m2) + 2 * m2 * v2 / (m1 + m2);
if (!b)
ball.collide(this, true);
System.out.println(vx + " " + vy);
motionX = vx * BOUNCEOBJECT;
motionY = vy * BOUNCEOBJECT;
}
But this is what happens when they collide with a low speed:
So do you have an idea?
EDIT:
The update of Alnitak works very nice... but one problem is still there... if i add gravity like this:
public void physic() {
motionY += GRAVITY; // <= this part (GRAVITY is set to 0.3D)
checkForCollision(screen.balls);
keyMove();
bounceWalls();
posX += motionX;
posY += motionY;
}
They still move into each other. I think this is the wrong way to add gravity, or isn't it?
And I think I did something wrong with the collision formula, because they don't fall right:
!
and then they slowly sink into the ground.
EDIT:
found an AMAZING tutorial: http://www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html
This is a common problem that happens because sometimes the delta-v of the bouncing ball is insufficient to take it back out of the collision zone.
So the collision routine reverses the direction again, taking it back inside the other ball, ad-infinitum.
You should add a sufficient offset (in the direction of the collision force) to the position of the ball to ensure that the newly calculated positions are no longer colliding.
Alternatively, check whether the balls would collide once you add the new motion values:
public boolean intersects(Ball b) {
double dx = b.posX - (posX + motionX); // no need for Math.abs()
double dy = b.posY - (posY - motionY);
double d = dx * dx + dy * dy; // no need for Math.sqrt()
return d < (radius + b.radius) * (radius + b.radius);
}
but you should also change ball.intersects(this) to intersects(ball).
They may appear to collide slightly too early, but on a fast moving ball it probably won't be visible.
(m1 - m2) * v1 / (m1 + m2) + 2 * m2 * v2 / (m1 + m2);
This has an integer value 2. Please make it 2.0f or 2.0d then check it out. It must be the problem for small speeds. Becuse integer constant autocasts multiplied doubles.
If this does not work, then Alnitak 's answer would be helpful.
If you need real nice physics, you should use the force then convert it to velocity then convert it to displacement . Look at integrator techniques like Runge Kutta and Euler Integration
Force-->acceleration-->velocity-->displacement
if collision occurs, just update the force then the rest will be flowing.
----> http://codeflow.org/entries/2010/aug/28/integration-by-example-euler-vs-verlet-vs-runge-kutta/ <-----
http://www.forums.evilmana.com/game-programming-theory/euler-vs-verlet-vs-rk4-physics/
http://www.newagepublishers.com/samplechapter/001579.pdf
http://cwx.prenhall.com/bookbind/pubbooks/walker2/
Verlet integration is a point between Runge-Kutta-4 and Euler Integration preferably for molecular dynamics (a good example for bouncing balls if you ommit the electrical fields and bonds)
Just found an AMAZING tutorial:
http://www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html
Related
The issue I have is that I'm attempting to add drag to an object in this basic physics simulation (Java [Processing]), but once I add the appropriate formula, it causes the objects velocity to increase drastically in the opposite direction. Of course the problem is that drag for some reason is being calculated too high but I'm not sure why thats happening as I'm using the real world equation.
void setup(){size(1280,720);}
class Circle{
float x,y,r,m,dx,dy,ax,ay,fx,fy;
Circle(float xPos, float yPos, float Radius, float Mass){
x = xPos;
y = yPos;
r = Radius;
m = Mass;
}
void ADD_DRAG(){
fx -= 0.5 * 1.225 * dx * dx * 0.5 * r * PI;
fy -= 0.5 * 1.225 * dy * dy * 0.5 * r * PI;
}
void update(){
ADD_DRAG();
ax = fx / m;
ay = fy / m;
dx += ax / frameRate;
dy += ay / frameRate;
x += dx / frameRate;
y += dy / frameRate;
}
}
Circle[] SceneObjects = {new Circle(50,50,20,20000),new Circle(50,50,2,20)};
void draw(){
background(51);
for (Circle c : SceneObjects){
c.update();
circle(c.x * 3,c.y * 3,c.r * 3);
}
}
void mouseClicked(){
if(SceneObjects[1].fx != 2000)
SceneObjects[1].fx = 2000;
else
SceneObjects[1].fx = 0;
}
This is the code, essentially there is a Circle class which stores the objects properties and then the forces applies are updated each draw loop. The mouseClicked void is just for testing by adding a force to the objects. All and any help is appreciated, thanks!
Maths I am Using:
Rearranged F=ma for ax = fx / m;
Acceleration * time = Speed for dx += ax / frameRate; (frameRate is 1/time)
Distance = Speed * time = for x += dx / frameRate; (same as above)
For drag im using this equation https://www.grc.nasa.gov/WWW/K-12/rocket/drageq.html with the constants eg air density etc added as seen.
There are a couple of things wrong here.
You haven't given us numbers (or a minimal complete example), but the vector algebra is off.
Yes, the acceleration is f = -kv2, and |v|2 = vx2 + vy2, but that doesn't mean that you can decompose f into fx=kvx2 and fy=kvy2. Not only is your magnitude off, but your acceleration is now not (in general) aligned with the motion; the path of your projectile will tend to curve toward a diagonal between the axes (e.g. x=y).
Also, your code always gives acceleration in the negative x and negative y directions. If your projectile happens to start out going that way, your version of air resistance will speed it up.
Finally, your time interval may simply be too large.
There is a better way. The differential equation is v' = -k v|v|, and the exact solution is v = (1/kt) z, (with appropriate choice of the starting time) where z is the unit direction vector. (I don't know how to put a caret over a letter.) This leads to v(t) = (1/t)v(t=1.0)
So you can either work out a fictional time t0 and calculate each new velocity using 1/(kt), or you can calculate the new velocity from the previous velocity: vn+1 =vn/(kd vn + 1), where d is the time interval. (And then of course you have to decompose v into vx and vy properly.)
If you're not familiar with vector algebra, this may seem confusing, but you can't get an air-resistance sim to work without learning the basics.
I wrote a code that should turn a point around another point counterclockwise. But it does not work correctly.
public boolean contains(double x, double y) {
double ox = this.x.get() + (this.width.get()/2);
double oy = this.y.get() + (this.height.get()/2);
double theta = rotate.get() - (rotate.get() * 2);
double px1 = Math.cos(theta) * (x-ox) - Math.sin(theta) * (y-oy) + ox;
double py1 = Math.sin(theta) * (x-ox) + Math.cos(theta) * (y-oy) + oy;
return shape.contains(px1, py1);
}
x, y - are the coordinates of the point to be rotated.
ox,oy - is the coordinates of the point around which you want to rotate.
rotate.get() - angle to rotate
Update: Changes in the code that solved the problem, who can come in handy:
double px1 = Math.cos(Math.toRadians(theta)) * (x-ox) - Math.sin(Math.toRadians(theta)) * (y-oy) + ox;
double py1 = Math.sin(Math.toRadians(theta)) * (x-ox) + Math.cos(Math.toRadians(theta)) * (y-oy) + oy;
Please check, if your rotate.get() will give you a degrees value (e.g. 45°) or a radians value (e.g. 0.5*pi). Math.sin() and Math.cos() will only accept radians.
To convert them you could use something like angle = Math.toRadians(45)
Although this is answered, another simple way to get this done is using the built-in method of Rotate class. This way you dont need to worry about the Math stuff ;)
Rotate r = new Rotate();
r.setPivotX(ox);
r.setPivotY(oy);
r.setAngle(angleInDegrees);
Point2D point = r.transform(new Point2D(x, y));
I'm programming a software renderer in Java, and am trying to use Z-buffering for the depth calculation of each pixel. However, it appears to work inconsistently. For example, with the Utah teapot example model, the handle will draw perhaps half depending on how I rotate it.
My z-buffer algorithm:
for(int i = 0; i < m_triangles.size(); i++)
{
if(triangleIsBackfacing(m_triangles.get(i))) continue; //Backface culling
for(int y = minY(m_triangles.get(i)); y < maxY(m_triangles.get(i)); y++)
{
if((y + getHeight()/2 < 0) || (y + getHeight()/2 >= getHeight())) continue; //getHeight/2 and getWidth/2 is for moving the model to the centre of the screen
for(int x = minX(m_triangles.get(i)); x < maxX(m_triangles.get(i)); x++)
{
if((x + getWidth()/2 < 0) || (x + getWidth()/2 >= getWidth())) continue;
rayOrigin = new Point2D(x, y);
if(pointWithinTriangle(m_triangles.get(i), rayOrigin))
{
zDepth = zValueOfPoint(m_triangles.get(i), rayOrigin);
if(zDepth > zbuffer[x + getWidth()/2][y + getHeight()/2])
{
zbuffer[x + getWidth()/2][y + getHeight()/2] = zDepth;
colour[x + getWidth()/2][y + getHeight()/2] = m_triangles.get(i).getColour();
g2.setColor(m_triangles.get(i).getColour());
drawDot(g2, rayOrigin);
}
}
}
}
}
Method for calculating the z value of a point, given a triangle and the ray origin:
private double zValueOfPoint(Triangle triangle, Point2D rayOrigin)
{
Vector3D surfaceNormal = getNormal(triangle);
double A = surfaceNormal.x;
double B = surfaceNormal.y;
double C = surfaceNormal.z;
double d = -(A * triangle.getV1().x + B * triangle.getV1().y + C * triangle.getV1().z);
double rayZ = -(A * rayOrigin.x + B * rayOrigin.y + d) / C;
return rayZ;
}
Method for calculating if the ray origin is within a projected triangle:
private boolean pointWithinTriangle(Triangle triangle, Point2D rayOrigin)
{
Vector2D v0 = new Vector2D(triangle.getV3().projectPoint(modelViewer), triangle.getV1().projectPoint(modelViewer));
Vector2D v1 = new Vector2D(triangle.getV2().projectPoint(modelViewer), triangle.getV1().projectPoint(modelViewer));
Vector2D v2 = new Vector2D(rayOrigin, triangle.getV1().projectPoint(modelViewer));
double d00 = v0.dotProduct(v0);
double d01 = v0.dotProduct(v1);
double d02 = v0.dotProduct(v2);
double d11 = v1.dotProduct(v1);
double d12 = v1.dotProduct(v2);
double invDenom = 1.0 / (d00 * d11 - d01 * d01);
double u = (d11 * d02 - d01 * d12) * invDenom;
double v = (d00 * d12 - d01 * d02) * invDenom;
// Check if point is in triangle
if((u >= 0) && (v >= 0) && ((u + v) <= 1))
{
return true;
}
return false;
}
Method for calculating surface normal of a triangle:
private Vector3D getNormal(Triangle triangle)
{
Vector3D v1 = new Vector3D(triangle.getV1(), triangle.getV2());
Vector3D v2 = new Vector3D(triangle.getV3(), triangle.getV2());
return v1.crossProduct(v2);
}
Example of the incorrectly drawn teapot:
What am I doing wrong? I feel like it must be some small thing. Given that the triangles draw at all, I doubt it's the pointWithinTriangle method. Backface culling also appears to work correctly, so I doubt it's that. The most likely culprit to me is the zValueOfPoint method, but I don't know enough to know what's wrong with it.
My zValueOfPoint method was not working correctly. I'm unsure why :( however, I changed to a slightly different method of calculating the value of a point in a plane, found here: http://forum.devmaster.net/t/interpolation-on-a-3d-triangle-using-normals/20610/5
To make the answer here complete, we have the equation of a plane:
A * x + B * y + C * z + D = 0
Where A, B, and C are the surface normal x/y/z values, and D is -(Ax0 + By0 + Cz0).
x0, y0, and z0 are taken from one of the vertices of the triangle. x, y, and z are the coordinates of the point where the ray intersects the plane. x and y are known values (rayOrigin.x, rayOrigin.y) but z is the depth which we need to calculate. From the above equation we derive:
z = -A / C * x - B / C * y - D
Then, copied from the above link, we do:
"Note that for every step in the x-direction, z increments by -A / C, and likewise it increments by -B / C for every step in the y-direction.
So these are the gradients we're looking for to perform linear interpolation. In the plane equation (A, B, C) is the normal vector of the plane.
It can easily be computed with a cross product.
Now that we have the gradients, let's call them dz/dx (which is -A / C) and dz/dy (which is -B / C), we can easily compute z everywhere on the triangle.
We know the z value in all three vertex positions.
Let's call the one of the first vertex z0, and it's position coordinates (x0, y0). Then a generic z value of a point (x, y) can be computed as:"
z = z0 + dz/dx * (x - x0) + dz/dy * (y - y0)
This found the Z value correctly and fixed my code. The new zValueOfPoint method is:
private double zValueOfPoint(Triangle triangle, Point2D rayOrigin)
{
Vector3D surfaceNormal = getNormal(triangle);
double A = surfaceNormal.x;
double B = surfaceNormal.y;
double C = surfaceNormal.z;
double dzdx = -A / C;
double dzdy = -B / C;
double rayZ = triangle.getV1().z * modelViewer.getModelScale() + dzdx * (rayOrigin.x - triangle.getV1().projectPoint(modelViewer).x) + dzdy * (rayOrigin.y - triangle.getV1().projectPoint(modelViewer).y);
return rayZ;
}
We can optimize this by only calculating most of it once, and then adding dz/dx to get the z value for the next pixel, or dz/dy for the pixel below (with the y-axis going down). This means that we cut down on calculations per polygon significantly.
this must be really slow
so much redundant computations per iteration/pixel just to iterate its coordinates. You should compute the 3 projected vertexes and iterate between them instead look here:
triangle/convex polygon rasterization
I dislike your zValueOfPoint function
can not find any use of x,y coordinates from the main loops in it so how it can compute the Z value correctly ?
Or it just computes the average Z value per whole triangle ? or am I missing something? (not a JAVA coder myself) in anyway it seems that this is your main problem.
if you Z-value is wrongly computed then Z-Buffer can not work properly. To test that look at the depth buffer as image after rendering if it is not shaded teapot but some incoherent or constant mess instead then it is clear ...
Z buffer implementation
That looks OK
[Hints]
You have too much times terms like x + getWidth()/2 why not compute them just once to some variable? I know modern compilers should do it anyway but the code would be also more readable and shorter... at least for me
Alright, I'm trying to do some simple object moving in the direction of where you touched the screen.
If I touch directly northwest of the object, it'll kind of move into the direction of the touch position. If I touch directly southeast of the object, it will kind of move into the direction of the touch position as well. However, if I touch directly northeast of the object, it'll move into the opposite direction towards the southwest. If I touch directly southwest of the object, it'll also move to the opposite direction towards northeast.
Also, if I touch north of the object, but just a little to the west, it will go straight west with a little to the north. Same with touching west of the object with a little bit to the north, it'll go straight north with a little bit to the west. Same thing for other directions.
Really, all the directions are from somewhat to obviously incorrect. I've been doing some paper calculations as well and I've seemed to be getting some correct angles, but at this point I'm completely stumped.
Does anyone know what the problem may be?
package com.badlogic.androidgames.texasholdem;
import java.util.List;
import android.util.FloatMath;
import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Input.TouchEvent;
import com.badlogic.androidgames.framework.Screen;
public class MainMenuScreen extends Screen {
public static float TO_RADIANS = (1 / 180.0f) * (float) Math.PI;
public static float TO_DEGREES = (1 / (float) Math.PI) * 180;
float num_x = 0; // Position of object on X axis
float num_y = 0; // Position of object on Y axis
float angle = 0;
public MainMenuScreen(Game game) {
super(game);
}
public void update(float deltaTime) {
Graphics g = game.getGraphics();
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
game.getInput().getKeyEvents();
int len = touchEvents.size();
for(int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if(event.type == TouchEvent.TOUCH_UP) {
if(inBounds(event, 0, 0, g.getWidth(), g.getHeight()) ) {
// Calculate the angle of the direction between two points
angle = (float) Math.atan2(event.x - num_x, event.y - num_y) * TO_DEGREES;
if (angle < 0)
angle += 360;
// This is just to give me numbers on the Math.atan2 result, angle, to/from X position, and to/from Y position
System.out.println("Pressed! - ATAN: " + Math.atan2(event.x - num_x, event.y - num_y)
+ " - ANGLE:" + angle + " - POS: " + event.x + "tx/"
+ (int)num_x + "fx " + event.y + "ty/" + (int)num_y + "fy");
}
}
}
// Moving object in direction at 1f speed
num_x += (1f * (float) Math.cos(angle * TO_RADIANS));
num_y += (1f * (float) Math.sin(angle * TO_RADIANS));
}
private boolean inBounds(TouchEvent event, int x, int y, int width, int height) {
if(event.x > x && event.x < x + width - 1 &&
event.y > y && event.y < y + height - 1)
return true;
else
return false;
}
public void present(float deltaTime) {
Graphics g = game.getGraphics();
g.drawPixmap(Assets.background, 0, 0);
g.drawPixmap(Assets.backcard, (int)num_x, (int)num_y);
}
public void pause() {
Settings.save(game.getFileIO());
}
public void resume() {
}
public void dispose() {
}
}
if event x> x then x must be positive to move toward event.x
the problem here is that when event.x< x then your moving x must be negative
int dx,dy;
dx = (1f * (float) Math.cos(angle * TO_RADIANS));
dy = (1f * (float) Math.sin(angle * TO_RADIANS));
if(event.x<x){
dx=-dx;}
if(event.y<y){
dy=-dy;}
num_x+=dx;
num_y+=dy;
this way is simpler but less precise....
public void update(){
//(find dif between item x, and touch x)
float xdif=destx-x;
float ydif=desty-y;
if(x<destx){
dx=xdif/8;
}
else if(x>destx){
//we devide both x and y differences by the same number
dx=xdif/8;
}
else if(x==destx){
dx=0;
}
if(y<desty){
dy=ydif/5;
}
else if(y>desty){
dy=ydif/5;
}
else if(y==desty){
dy=0;
}
x+=dx;
y+=dy;
there u go, pathing in a straight line between two points, item.x and touch x.
Firstly, the math - I think the problem is that, for example, tan(135deg) = tan (-45deg) = -1. Therefore, atan has return values ranging between -90deg and 90deg as a resolution to ambiguity (look at its graph here). I think La5t5tarfighter's solution - negating the x movement in some cases - is on the right track, but you need to negate the y component in those cases as well. You could try that, but it would be much simpler if you used libGDX's Vector2 class. This is how I'd do it:
move.set(touchX, touchY); // y should be through flipping or unproject() before this
move.sub(objectPos); // move now points from object to where you touched
move.nor(); // now 1 unit long
move.scl(SPEED*deltaTime); // multiplied by a constant and delta - framerate-independent
objectPos.add(move);
You could even chain it into just one line if you want:
objectPos.add(move.set(x,y).sub(objectPos).nor().scl(SPEED*deltaTime));
Secondly, you're not using a Camera. I'm not completely sure what the default coordinate system is, but I believe the y axis points up which is not the same as the one used for inputs - Input.getY() is given with an y axis pointing down from the top left corner. If you had a Camera, you'd do this:
cam.unproject(someVector.set(Gdx.input.getX(), Gdx.input.getY(), 0));
Lacking that, you might need to flip the y axis:
event.y = Gdx.graphics.getHeight() - event.y;
Still, this could be wrong. Try drawing the object right at the touch position - if I'm right in this, it'll seem mirrored vertically. If it draws correctly where you touch, ignore this part.
Alright, so I got a bit of movement code and I'm thinking I'm going to need to manually input when to go up/down a slope. All I got to work with is the slope's normal, and vector, and My current and previous position, and my yaw.
Is there a better way to rotate whether I go up or down the slope based on my yaw?
Vector3f move = new Vector3f(0,0,0);
move.x = (float)-Math.cos(Math.toRadians(yaw));
move.z = (float)-Math.sin(Math.toRadians(yaw));
System.out.println("slopeNormal.z: " + slopeNormal.z + "move.z: " + move.z);
move.normalise();
float vx = (float) (Math.sqrt(Math.pow(move.y, 2) + Math.pow(move.z, 2)) * move.x);
float vy = (float) (Math.sqrt(Math.pow(move.x, 2) + Math.pow(move.z, 2)) * move.y);
float vz = - vx * slopeNormal.x - vy * slopeNormal.y;
move.scale(movementSpeed * delta);
if(vz < 0)
move.y -= slopeVec.y * 1.5f;
if(vz > 0)
move.y += slopeVec.y * 1.5f;
Vector3f.add(pos, move, pos);
Edit: updated code.
First off, the following is incorrect:
move.x = (float)-Math.toDegrees(Math.cos(Math.toRadians(yaw)));
move.z = (float)-Math.toDegrees(Math.sin(Math.toRadians(yaw)));
Math.toDegrees converts an angle in radians to one in degrees, but the results of Math.cos and Math.sin are not angles.
Assume zero yaw is in the positive x-direction... and define vx, vy, vz = rate of motion along 3 axes, s = speed, and slope normal = nx, ny, nz where nx^2 + ny^2 + nz^2 = 1. So nx = ny = 0, nz = 1 would be flat.
First, I define x', y' = axes relative to the flat ground (motion is constrained to ground). Then (the following is not valid Java, but I'm enclosing it in code format anyway):
vx' = cos(yaw) * s
vy' = sin(yaw) * s
Then I need to rotate from x', y' coordinates to real-world coordinates. That is done using the slope normal:
vx = sqrt(vy^2 + vz^2) vx'
vy = sqrt(vx^2 + vz^2) vy'
vz = - vx' nx - vy' ny
A check on this transformation: vx^2 + vy^2 + vz^2 must equal vx'^2 + vy'^2 = s^2. I think this works out.
So to answer your question: up or down? vz > 0 is up, vz < 0 is down.