I'm trying to create a bouncing ball that can move in the x, y, and z coordinates. The ball originally starts a couple units in the y-axis above the origin. I've managed to figure out how to get my ball to bounce up and down in the y plane, but am having troubles figuring out what I am doing wrong whenever I try adding the x and z trajectories.
double initialVelocity = userInputY;
double initVelX = userInputX;
double initVelZ = userInputZ;
double speed = 1/500.0;
double time = 0;
double x, y, z =0;
if (time == 0){
velocity+= initialVelocity;
}
time += speed
velocity = velocity - 9.8 * speed;
if (y+velocity < 0.1){ //collision detection against the floor
velocity *= -1;
}
if (z + initVelZ < 100){ //Collision detection for ceiling of 100 units
initVelZ *= -1;
}
if (x + initVelX < 50){ //Collision detection for if ball moves 50 units away from origin in x
initVelX *= -1;
}
else{
y += velocity;
x += initVelX;
z += initVelZ;
}
gl.gltranslated(x, y, z);
glu.gluSphere() //not really that important to specify
When I only use my y variable my ball has a bouncing animation but only moves up and down.
The comparisons for the z and x coordinates look backwards:
if (z + initVelZ < 100){ //Collision detection for ceiling of 100 units
initVelZ *= -1;
}
if (x + initVelX < 50){ //Collision detection for if ball moves 50 units away from origin in x
initVelX *= -1;
}
This inverts the velocity every time the new position is found to be inside the bounds. But they need to be inverted when the new position would end up being outside the bounds.
Also, you probably need to test for collision with the walls at both ends of each coordinate direction, so that the ball can bounce off all 6 walls.
For example for the z coordinate, the logic could look like this:
if (z + initVelZ < 0.0 || z + initVelZ > 100.0) {
initVelZ *= -1.0;
}
Same thing for the other coordinates.
Related
I was working on an animation on processing.
But, I have a question about the code below:
So, you can see on the output, the ball is going everywhere on the window. I want to make a barrier for the ball. The ball can pass from the middle but if it hit the line it goes somewhere with the speed of (int)random(1, 3).
How to make that?
Here is a picture of what I would like to achieve:
One way to do this is to check for collision when the ball passes through the middle, specifically when the ball's velocity would make y pass from < height/2 to > height/2.
Something like this:
if (((y < height/2 && y+dirY > height/2) || (y > height/2 && y+dirY < height/2)) && (x < width/2-middle || x > width/2+middle)){
dirY = (int) random (1,3);
dirX = (int) random (1,3);
}
(A more appropriate solution would be to create the collision when the edge of the ball passes the barrier, but I'll leave that up to you)
I'm not going to write code, but I'll tell you how to calculate it, it's not that hard.
Taken a circle, we know the center and the radius.
Taken a line we know the startpoint and endpoint.
The line intersects the circle if the distance from them center to startpoint or endpoint is smaller than radius (e.g. point is inside circle)
A line intersects circle if startpoint X is on the left of center (sX < cX), endpoint X on the right (eX > cX), and the distance between their Y and the center Y is less than radius (cY - sY < r)
Note that this really applies only in your simple case with a horizontal line. Normally is better to really work with vectors calculations to determine distance from center to line for example. That would allow you to work with diagonal lines etc.
For your toy model, the easiest way to handle collisions is considering a collision zone. So,
Establish the collision zone limits x1, x2, y1, y2. When the center of the ball enters this zone, collision rules apply.
Consider elastic collisions: your ball only collides with an horizontal line, so only the vertical component of the velocity can be affected, and it never looses speed.
Consider to test your toy model with a good bunch of initial conditions, so you can discover any artifacts due to the smallness of your model, the values you are using, and so on. (I have modified the initial position of the ball for a random initial position, run the model several times to see how it behaves under different initial conditions).
int dirX = (int)random(1, 3);
int dirY = (int)random(1, 3);
//int x = 20, y = 20;
int middle = 20;
int x = (int)random(20, 180);
int y = (int)random(20, 80);
int x1, x2, y1, y2;
void setup() {
size(200,200);
// collision zone limits
x1 = (width/2 - middle) + 20/2 - 1; // 89
x2 = (width/2 + middle) - 20/2 + 1; // 111
y1 = height/2 - 20/2 + 1; // 91
y2 = height/2 + 20/2 - 1; // 109
}
void draw() {
background(255);
ellipse(x, y, 20, 20);
line(0, height/ 2, width/2 - middle, height/2); // (0, 100, 80, 100)
line(width/2 + middle, height/2, width, height/2); // (120, 100, 200, 100)
x += dirX;
y += dirY;
if (x > width || x < 0)
dirX *= -1;
if (y > height || y < 0)
dirY *= -1;
if ((x < x1 && y > y1 && y < height/2 && dirY > 0) || // upper left and going down
(x < x1 && y > height/2 && y < y2 && dirY < 0) || // lower left and going up
(x > x2 && y > y1 && y < height/2 && dirY > 0) || // upper right and going down
(x > x2 && y > height/2 && y < y2 && dirY < 0)) // lower right and going up
dirY *= -1;
}
For a more complex model, you could consider more sophisticated situations as, for example, the backwards collision due to the ends of the lines, the collision of the contour of the ball with the lines, etc.
I would like help in smoothing my random terrain out to make it look more realistic. At the moment I'm just assigning random values to an array and setting tiles to the values stored in the array.
This gives this effect:
protected void generateLevel() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rand = random.nextInt(1000); //height
if(rand <= 300){ //water level
tiles[x + y * width] = 6;//set water
}else{
tiles[x + y * width] = 1; //set grass
}
}
}
}
Please help me in making my terrain look more realistic, I would like someones opinion on how they might implement this into their game or mine. How do I smooth my terrain for a realistic effect?
Instead of using random-for-each-point terrain, you may consider using perlin or simplex noise instead. This has the benefit of being smooth, replicable no matter in what order you read your map, and replicable with a given starting "shuffled array". A very good description and implementation is available at this post.
Use OpenSimplex noise: https://gist.github.com/KdotJPG/b1270127455a94ac5d19
Do sommething like:
protected void generateLevel() {
OpenSimplexNoise noise = new OpenSimplexNoise(); //optionally pass in a seed.
for (int y = 0 y < height; y++) {
for (int x = 0; x < width; x++) {
double value = noise.eval(x / 24.0, y / 24.0, 0.5);
if (value < 0) {
tiles[x + y * width] = 6; //set water
} else {
tiles[x + y * width] = 1; //set grass
}
}
}
}
There are also alternatives such as Perlin noise and Simplex noise. I'd skip Perlin noise entirely because it tends to exhibit visually significant grid artifacts (it lines all of its features up with its underlying square grid). Simplex noise in 2D is okay, though most implementations of 2D Simplex noise on the internet use a weird gradient set that results in artifacts down the negative diagonal. And 3D simplex noise is patent-saddled.
Perlin noise vs OpenSimplex noise, 2D slices of 3D:
Left is noise(x, y, 0) grayscale
Next is noise(x, y, 0) > 0 ? white : black
Next is |noise(x, y, 0)| > 0.1 ? white : black
Next is noise(x, y, 0.5) grayscale
EDIT: Here's the code from the comment
Fractal noise:
OpenSimplexNoise n1 = new OpenSimplexNoise(seed1);
OpenSimplexNoise n2 = new OpenSimplexNoise(seed2);
OpenSimplexNoise n3 = new OpenSimplexNoise(seed3);
for each x,y {
double value = (n1.eval(x / 48.0, y / 48.0, 0.5) + n2.eval(x / 24.0, y / 24.0, 0.5) * .5 + n3.eval(x / 12.0, y / 12.0, 0.5) * .25) / (1 + .5 + .25);
//Do whatever you need to with that value
}
Rivers:
if (value > -0.1 || value < 0.1)
water
else
land
Biomes:
OpenSimplexNoise n1 = new OpenSimplexNoise(seed1);
OpenSimplexNoise n2 = new OpenSimplexNoise(seed2);
OpenSimplexNoise n3 = new OpenSimplexNoise(seed3);
for each x,y {
double elevation = n1.eval(x / 24.0, y / 24.0, 0.5);
double temperature = n2.eval(x / 24.0, y / 24.0, 0.5);
double precipitation = n3.eval(x / 24.0, y / 24.0, 0.5);
if (elevation < 0)
water
else {
//decide biome based on temperature, precipitation, and elevation
}
}
In this program I simulate the gravity. All works but when there is no more movement for the ball it keep bouncing 1-5 pixels depending on the gravity value I set. How could i stop the ball when the energy is lost? I want the xSpeed to become 0 and the ball to stay on a fix position.
Edit: The gravity variate from 1 to 100. The user can change the gravity.
energyLoss = 0.9 and dt = 0.2
// right and left wall collision
if (x + xSpeed > this.getWidth() - radius - 1) {
x = this.getWidth() - radius - 1;
xSpeed = -xSpeed;
} else if (x + xSpeed < 0 + radius) {
x = 0 + radius;
xSpeed = -xSpeed;
} else
x += xSpeed;
if (y == this.getHeight() - radius - 1) {
}
if (y > this.getHeight() - radius - 1) {
y = this.getHeight() - radius - 1;
ySpeed *= energyLoss;
ySpeed = -ySpeed;
// friction with the ground
xSpeed *= xFriction;
if (Math.abs(xSpeed) < .4)
xSpeed = 0;
} else {
ySpeed += gravity * dt; // velocity formula
y += ySpeed * dt + .5 * gravity * dt * dt; // position formula
}
repaint();
Simplistic Answer
After this line:
ySpeed *= energyLoss;
Change this line:
ySpeed = -ySpeed;
To something like this:
if (ySpeed < SomeMinimumValue)
{
ySpeed = 0;
}
else // invert speed, i.e. change direction.
{
ySpeed = -ySpeed;
}
Edit; Second try:
if (Math.abs(ySpeed) < SomeMinimumValue)
... as above.
Less code, more talk
It appears that the problem stems from the ball impacting the "ground" with a low y and or x speed. If this is the case, you need to zero the speed when impacting the "ground" with a sufficiently low y or x speed; zero y speed when y speed is low enough and zero x speed when x speed is low enough, not necessarily both at the same time. You also need to make sure you are not zeroing the x speed on a y impact and not zeroing the y speed on an x impact.
The simplest way to fix this problem is to detect a special case where the ball is on the wall with 0 (or very small) Y velocity. This is basically what DwB is suggesting.
However, you then need to go further and make sure that you stop applying gravity when you're in that situation.
Something like this:
// right and left wall collision
if (x + xSpeed > this.getWidth() - radius - 1) {
x = this.getWidth() - radius - 1;
xSpeed = -xSpeed;
} else if (x + xSpeed < 0 + radius) {
x = 0 + radius;
xSpeed = -xSpeed;
} else
x += xSpeed;
if (y == this.getHeight() - radius - 1
&& ySpeed == 0) { // Check speed too here!
// Do nothing for Y.
// friction with the ground
xSpeed *= xFriction;
if (Math.abs(xSpeed) < .4)
xSpeed = 0
}
else if (y > this.getHeight() - radius - 1) {
y = this.getHeight() - radius - 1;
ySpeed *= energyLoss;
if (Math.abs(ySpeed) < SomeMinimumValue)
ySpeed = 0;
else // invert speed, i.e. change direction.
ySpeed = -ySpeed;
// friction with the ground
xSpeed *= xFriction;
if (Math.abs(xSpeed) < .4)
xSpeed = 0;
} else {
ySpeed += gravity * dt; // velocity formula
y += ySpeed * dt + .5 * gravity * dt * dt; // position formula
}
repaint();
Maybe a beginner question, but thank you for reading. I'm working on a 2d Java game in which I have missiles that track the position of an object using the following code. I'd like the missiles to stop homing when they reach a certain minimum displacement from their target, at which point they should fall straight down. Right now, the homing behavior turns off whenever the missile is within the minimum displacement, and turns on again if the displacement increases. I'd like the behavior to turn off only once, staying off for the remainder of the missile's lifespan. How can I accomplish this?
public void home(int targetX, int targetY, int scale){
int dy = targetY - y;
int dx = targetX - x;
double speed, sep;
sep = Math.sqrt(dx * dx + dy * dy);
speed = scale/sep;
if(dy > 50 || dx > 50){
x += dx * speed;
y += dy * speed;
}
else{
x += 0;
y += scale;
}
}
Add a member variable, such as boolean homing = true. Then, change your conditional to something like the following:
if (dy < 50 && dx < 50)
homing = false;
if(homing){
x += dx * speed;
y += dy * speed;
}
else{
x += 0;
y += scale;
}
You basically just need to break the mutual dependence between you missile's behaviour and its state.
I have a simple Java applet that has two user-controlled balls, drawn using java.awt. I need a way to detect a collision with between them. I have an algorithm for detecting collision with the walls:
if (xPosition > (300 - radius)){
xSpeed = -xSpeed;
}
else if (xPosition < radius){
xSpeed = -xSpeed;
}
else if (yPosition > (300 - radius)) {
ySpeed = -ySpeed;
}
else if (yPosition < radius){
ySpeed = -ySpeed;
}
xPosition += xSpeed;
yPosition += ySpeed;
and for the second ball:
if (xPosition2 > (300 - radius)){
xSpeed2 = -xSpeed2;
}
else if (xPosition2 < radius){
xSpeed2 = -xSpeed2;
}
else if (yPosition2 > (300 - radius)) {
ySpeed2 = -ySpeed2;
}
else if (yPosition2 < radius){
ySpeed2 = -ySpeed2;
}
xPosition2 += xSpeed2;
yPosition2 += ySpeed2;
The applet is 300 pixels by 300 pixels.
radius stores the radius of the circles.
xPosition and xPosition2 store the x coordinates for the two balls.
yPosition and yPosition store the y coordinates for the two balls,
xSpeed and xSpeed2 store the x velocities for the two balls.
ySpeed and ySpeed2 store the y velocities for the two balls.
Use http://java.sun.com/j2se/1.5.0/docs/api/java/awt/geom/Point2D.html, there's a distance method there, if it's less than the radius they're colliding.
EDIT:
Err, less than the radius * 2 , sorry
There's Point2D in Java or you can do it yourself, it is trivially easy for circle/circle collisions or sphere/sphere collisions.
int distXX = (xPosition1 - xPosition2) * (xPosition1 - xPosition2);
int distYY = (yPosition1 - yPosition2) * (yPosition1 - yPosition2);
if ( radius*radius > distXX * distYY ) {
... // There's a collision
}
public boolean colliding(Ball anotherBall) {
double xDelta = (this.x + this.ballSize/2 + this.dx) - (anotherBall.x + anotherBall.ballSize/2 + anotherBall.dx);
double YDelta = (this.y + this.ballSize/2 + this.dy) - (anotherBall.y + anotherBall.ballSize/2 + anotherBall.dy);
double distance = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(YDelta, 2));
return (distance <= this.ballSize/2 + anotherBall.ballSize/2);
}
This Link is pretty useful!
Circle-Circle Collisions
It's very detailed and didatic
At the bottom of that page there are another links, to even more detailed stuff!
I used the Distance Between Centers method ---
Circles
By measuring the distance between each center you can say if they are colliding.
The distance should never be more then the sum of the 2 radius.
Here's what I did:
private boolean checkDrawContains(ShapeDrawable newHole)
{
long newCenterX = newHole.getBounds().left + (newHole.getBounds().width()/2); //Get the center of my shapes
long newCenterY = newHole.getBounds().top + (newHole.getBounds().height()/2);
for(ShapeDrawable hole: mHoles) // I was storing the circles in an ArrayList
{
long centerX = hole.getBounds().left + (hole.getBounds().width()/2); //Get the center of my shapes
long centerY = hole.getBounds().top + (hole.getBounds().height()/2);
long x = centerX - newCenterX;
long y = centerY - newCenterY;
long aux = (long) ((Math.pow(Math.abs(x),2)) + (Math.pow(Math.abs(y),2))); //Pythagoras the hard way :P
long distance = (long) Math.sqrt(aux);
long sRads = (newHole.getBounds().width()/2) + (hole.getBounds().width()/2);
if(distance <= sRads ) {
return true; //Is Colliding!
}
}
return false; // Is not Colliding!
}