I have written code that should form the basis for a simple Pong game. It works, but I don't understand why. It should not work. I'm hoping somebody can give me the insight I'm missing as to why it does work.
I have the following concept in my canvas (which has (0,0) on the top left):
A ball always bounces in an angle between 0 and 180 degrees. I have taken the bottom of my canvas as the basis. Left is 0 degrees, right is 180 degrees. If it bounces on a wall the ball's angle (ball_angle) changes to 180 - ball_angle degrees. A ball's trajectory is defined by 2 more variables (x_traj and y_traj) indicating the direction on each axis.
The part I don't get is the ballHits() method. If the ball is hitting the cealing, coming from the right with a degree of e.g. 100, then it should bounce off at a degree of 80. The ball is coming from the right so the x_traj is negative. We are bouncing on the cealing so the ball drops instead of lifts, ergo we change the y_traj from negative (lifting) to positive (dropping). The ball will still be going to the right, so we leave that direction in tact.
Second scenario is when the ball hits the left wall. The ball is coming from the right again, so we know that traj_x is negative. We bounce off so the ball goes back to the right, ergo traj_x should be multiplied by -1 to make it positive again (move to the right). Wether we hit the wall coming from above or below, we are still going that same direction after bouncing of the wall. We don't change the traj_y variable.
However, below is the working code. I do not have to change any variable when I hit the left or right wall. Could somebody explain to me why?
If needed, the full compiling project can be found on GitHub.
Code to move the ball to new coordinates:
private void updateBall()
{
// http://gamedev.stackexchange.com/questions/73593/calculating-ball-trajectory-in-pong
// If the ball is not hitting anything, we simply move it.
// http://en.wikipedia.org/wiki/Polar_coordinate_system
if (ballHits())
{
// Bounce the ball off the wall.
ball_angle = 180 - ball_angle;
}
// http://en.wikipedia.org/wiki/Polar_coordinate_system
// Convert the angle to radians.
double angle = (ball_angle * Math.PI) / 180;
// Calculate the next point using polar coordinates.
ball_x = ball_x + (int) (x_traj * BALL_STEPSIZE * Math.cos(angle));
ball_y = ball_y + (int) (y_traj * BALL_STEPSIZE * Math.sin(angle));
System.out.printf("Ball: (%d,%d) # %d\n", ball_x, ball_y, ball_angle);
}
Code that determines if we have hit a wall:
private boolean ballHits()
{
// If we came out of bounds just reset it.
ball_y = Math.max(0, ball_y);
ball_x = Math.max(0, ball_x);
// Check to see if it hits any walls.
// Top
if(ball_y <= 0)
{
System.out.println("Collision on top");
y_traj *= -1;
x_traj *= -1;
return true;
}
// Left
if(ball_x <= 0)
{
System.out.println("Collision on left");
//y_traj *= -1;
//x_traj *= -1;
return true;
}
// Right
if(ball_x >= B_WIDTH)
{
System.out.println("Collision on right");
//y_traj *= -1;
//x_traj *= -1;
return true;
}
// Bottom
if(ball_y >= B_HEIGHT)
{
System.out.println("Collision on bottom");
y_traj *= -1;
x_traj *= -1;
return true;
}
return false;
}
Well it's working in a tricky way because cosine goes negative when your angle is > 90°.
Making him start with a different initial trajectory and angle should not work if the ball hits the bottom or top first.
Edit : I thought it would do that but doing it on paper proves me wrong, well it's a weird way to do it but it's working as intended. I'll investigate to find if there's a way it doesn't work.
Edit 2 : Does a start angle in range of [88-92] work ?
Related
I have a move function where I move all the balls that are on the screen. I have also added gravity (probably quite badly) which makes the balls lose height as they bounce (gravity is adjusted using a slider). However, after bouncing a bit new the bottom, they end up falling off the screen even though they shouldn't fall off. Does anyone know how to fix it?
//controls the movement of balls
private void move(JFrame frame) {
loop = 0;
while (loop < balls) {
// Adds Gravity to Y Angle
yAngles.set(loop, yAngles.get(loop) + gravity);
//Wall detection
if (xValues.get(loop) + xAngles.get(loop) < 0) { // Left wall
xAngles.set(loop, xSpeed.get(loop));
} else if (xValues.get(loop) + xAngles.get(loop) > getWidth()-100) { // Right wall
xAngles.set(loop, -xSpeed.get(loop));
} else if (yValues.get(loop) + yAngles.get(loop) < 0) { // Top wall
yAngles.set(loop, ySpeed.get(loop));
} else if (yValues.get(loop) + yAngles.get(loop) > getHeight()-100) { // Bottom Wall
yAngles.set(loop, -ySpeed.get(loop));
bounces.set(loop, bounces.get(loop) + 1);
}
//Changes the x/y values
xValues.set(loop, xValues.get(loop) + xAngles.get(loop));
yValues.set(loop, yValues.get(loop) + yAngles.get(loop) + ((gravity) * bounces.get(loop)));
loop++;
System.out.println(xValues);
System.out.println(yValues);
System.out.println(xAngles);
System.out.println(yAngles);
System.out.println(bounces);
}
}
I have tried implementing an average y Value so I can try and stop the ball once the average height of the ball is the same, it stops the ball. I have also tried to check the the y Value to see if it greater than the border. If it is, then it stops the ball but that also didn't work.
Is there some sort of formula for rotating the arrow in this link right here to make sure it's always pointing toward the red? Each time I've tried, I'd always get a number that's off, and the arrow's rotation is not in sync with the arrow's turning and movement.
A couple of snippets of code to show what I'm working with:
arrow.moveX(true);
arrow.moveY(true);
if(arrow.turning) {
arrow.turn(player.direction / (0.75 * arrow.getSpeed()));
}
The arrow's speed is 12.5 units/time if that's important. As for the movement itself:
public void moveX(boolean turn) {
if(turn) {
x += speed * Math.cos(angle);
} else {
x += speed;
}
}
public void moveY(boolean turn) {
if(turn) {
y += speed * Math.sin(angle);
} else {
y += speed;
}
}
I'm trying to figure out how to render the arrow's sprite make sure that the pointer itself is facing that "forward" direction that it's moving in, no matter how much it rotates. Here is the render method itself if that's necessary:
#Override
public void render(Canvas c, Paint p) {
matrix.setTranslate((float)x, (float)y);
if(alive) {
matrix.postRotate(drawnAngle, (float) (x + width / 2), (float) (y + height / 2));
} else {
matrix.postRotate(angle, (float) (x + width / 2), (float) (y + height / 2));
angle += speed * 2;
}
c.drawBitmap(getSprite(), matrix, p);
}
The variable drawnAngle has a value of 0 right now, it's a placeholder. It was just my attempt of trying to find the right number to rotate the arrow by.
So I've actually spent hours trying to figure this out, and the moment I decide to post for help, I figured it out! It turns out that while I was using radians in the first snippet of code (the actual movement), I was supposed to be using degrees in the actual rotate in that last snippet!
I changed drawnAngle to (float)(angle * (180 / Math.PI)) and this worked as a solution for me!
Hopefully no one else has this problem.
so recently, we've been assigned to code multiple circles that act like robots in a GUI interface. Basically, a robot simulator.
I've got the code to spawn in multiple circles that can act like robots.
This is my current code for detecting wall collision between the robot and the end of the square:
private void checkCollisions(double maxX, double maxY) {
for (ListIterator<Ball> slowIt = balls.listIterator(); slowIt.hasNext();) {
Ball b1 = slowIt.next();
// check wall collisions:
double xVel = b1.getXVelocity();
double yVel = b1.getYVelocity();
if ((b1.getCenterX() - b1.getRadius() <= 0 && xVel < 0)
|| (b1.getCenterX() + b1.getRadius() >= maxX && xVel > 0)) {
b1.setXVelocity(-xVel);
}
if ((b1.getCenterY() - b1.getRadius() <= 0 && yVel < 0)
|| (b1.getCenterY() + b1.getRadius() >= maxY && yVel > 0)) {
b1.setYVelocity(-yVel);
}
for (ListIterator<Ball> fastIt = balls.listIterator(slowIt.nextIndex()); fastIt.hasNext();) {
Ball b2 = fastIt.next();
final double deltaX = b2.getCenterX() - b1.getCenterX() ;
final double deltaY = b2.getCenterY() - b1.getCenterY() ;
if (colliding(b1, b2, deltaX, deltaY)) {
bounce(b1, b2, deltaX, deltaY);
}
}
}
}
The
b1.setXVelocity(-xVel);
b1.setYVelocity(-yVel);
are the main bits that make the circle bounce back from the wall. However, instead of this, I want the ball to detect the wall and rotate 90 degrees rather than bounce back form the wall like a bouncing ball.
Any help will be fully appreciated or a working piece of code that ca do this for me. I have an AraryList of all the balls called 'balls'.
If needed, I can give source code.
This is what I have so far. But I need each ball to have a sensor attached to them detecting if there a wall ahead.
https://i.stack.imgur.com/XsQvX.png
Assuming you have just square walls:
If the ball hits the right wall for example, you want to remove all x velocity, and then add either positive or negative velocity.
The issue with this is that the robot will end up just going around the outside edges of the map.
I'm trying to implement a simple pong game. I want the ball to change X direction or Y direction depending what side of the ball was hit.
The ball moves at 3 pixels per second and has a width of 22 and height of 22. The paddles have a width and height of 32.
For collision detection on the ball, should I just create one rectangle and check for collision with the center point?
Alternatively I could create 4 rectangles around the ball to represent the sides, with an appropriate width, given that the ball moves at 3 pixels per frame.
Another option is to create a method that will check for collision at least 2 pixels in the direction of motion of the ball.
If the ball is moving to the right, and the x-position is 16, the next frame will be 19.
Should I create a method that will check x for 16, 17 and 18, to make sure if there is a collision it will hit the right side and not cause the ball to actually go inside the cube?
#Sig
I now have this as my collision detection
if(Rect.intersects(balls.get(j).ball, levelBlocks.blocks.get(i).rect))
{
//Bottom
if(balls.get(j).yPos <= levelBlocks.blocks.get(i).yPos - (32/2))
{
balls.get(j).ySpeed = balls.get(j).ySpeed * -1;
}
//Top
if(balls.get(j).yPos >= levelBlocks.blocks.get(i).yPos + (32/2))
{
balls.get(j).ySpeed = balls.get(j).ySpeed * -1;
}
//Left
if(balls.get(j).xPos < levelBlocks.blocks.get(i).xPos)
{
balls.get(j).xSpeed = balls.get(j).xSpeed * -1;
}
//Right
if(balls.get(j).xPos > levelBlocks.blocks.get(i).xPos)
{
balls.get(j).xSpeed = balls.get(j).xSpeed * -1;
}
This works, but not 100%, it still seems abit off. if the ball hits two blocks at the same time, it will invert the invert so the direction will go back again.
I then changed it to this
if(Rect.intersects(balls.get(j).ball, levelBlocks.blocks.get(i).rect))
{
//Bottom
if(balls.get(j).yPos <= levelBlocks.blocks.get(i).yPos - (32/2))
{
collision = 2;
}
//Top
if(balls.get(j).yPos >= levelBlocks.blocks.get(i).yPos + (32/2))
{
collision = 2;
}
//Left
if(balls.get(j).xPos <= levelBlocks.blocks.get(i).xPos + (32/2))
{
collision = 1;
}
//Right
if(balls.get(j).xPos >= levelBlocks.blocks.get(i).xPos + (32/2))
{
collision = 1;
}
temp = levelBlocks.blocks.get(i);
levelBlocks.blocks.remove(i);
combo.blocksDestroied += 1;
Assets.score += 10 * combo.comboMultiplier;
}
}
if(collision == 1)
{
balls.get(j).xSpeed = balls.get(j).xSpeed * -1;
collision = 0;
}
if(collision == 2)
{
balls.get(j).ySpeed = balls.get(j).ySpeed * -1;
collision = 0;
}
This does work, but every now and then the ball will just start randomly going through blocks, it is very odd to look at, really confused on why it is doing it, but I do feel it is because of the 3 pixels per frame
Because the shapes you're working with are so simple, why not be perfect? Use a rectangle for the paddles and a circle for the ball when performing hit detection. Start by checking for collisions on a frame-by-frame basis; at the speeds you're working with, you shouldn't need to "look into the future" for future collisions.
This is probably a really basic problem but I can't seem to find any other articles on it.
Anyway, I have written a small bouncing ball program in Java to try and expand my basic skills. The program is just a simple bouncing ball that will drop and hopefully bounce for a while. The original program worked fine but now I have tried to add gravity into the program. The gravity actually works fine for a while but then once the bounces get really small the animation becomes erratic for a very short time then the position of the ball just constantly decreases. I've tried to figure out the problem but I just can't see it. Any help would be most welcome.
public final class Ball extends Rectangle {
float xspeed = 1.0f; float yspeed = 1.0f; float gravity = 0.4f;
public Ball(float x, float y, float width, float height) {
super(x, y, width, height);
}
public void update(){
yspeed += gravity;
move(xspeed, yspeed);
if(getX() < 0){
xspeed = 1;
}
if(getX() + getWidth() > 320){
xspeed = -1;
}
if(getY() < 0){
yspeed = 1;
}
if(getY() + getHeight() > 200 && yspeed > 0){
yspeed *= -0.98f;
}
if(getY() + getHeight() > 200 && yspeed < 0){
yspeed *= 0.98f;
}
}
public void move(float x, float y){
this.setX(getX()+x);
this.setY(getY()+y);
}
}
EDIT: Thanks that seems to have sorted the erratic movement. I'm still struggling to see how I can stop my ball moving down when it has stopped bouncing. Right now it will move stop bouncing then continue moving down passed the "floor". I think it's to do with my yspeed += gravity line. I just can't see how I'd go about stopping the movement down.
When you do
yspeed += gravity;
you are assuming that the ball has space move through a distance dx = v_i * t + 1/2 (-g) t^2. When you are very near the floor this may not be true. It fail if:
You are near enough the the floor and moving down
You are very near the floor and have low velocity (like when the ball has lost most of it's energy)
This bug causes your simulation to stop conserving energy, resulting in the erratic behavior you see at low amplitude.
You can reduce the problem by using smaller time steps, and you can get rid of it outright if you do test computation to notice when you're out of room and to select a safe time step for that iteration (i.e. always use your default unless there is a problem, then calculate the best time step).
However, the basic approximation you're using here has other problems as well. Look in any numeric analysis text for a discussion of solving differential equations numerically.
First things first: setting y-speed to 1 when you bounce on the top of the window is not correct, you should set yspeed to -yspeed (but if you start within the borders, it should never bounce up to the top anyway).
Secondly, your multiply by -0.981 when bouncing on the bottom is okay but I'm concerned with the constant 0.4 gravity being added to yspeed every iteration. I think that's what is causing you wiggles at the bottom since you do the move before checking which can result in the ball dropping below ground level.
I would try ensuring the the y value can never go below ground level by replacing the move with:
if (getY() + getHeight() + yspeed > 200) {
move(xspeed, 200 - getY() - getHeight());
} else {
move(xspeed, yspeed);
}
The problem is that when the bounces get really small, the
yspeed *= -0.981;
line will get called in short succession. The ball will go below the bottom, start coming back up, but still be below the bottom (because 0.981 < 1.0) eventually, and it will behave eradically. Here's how you fix it:
if(getY() + getHeight() > 200){
yspeed *= -0.981;
setY(400 - getY() - getHeight()); // I believe this is right.
}
By fixing the position, you won't alternate between decreasing and increasing as quickly and won't get stuck in the situation where it is always decreasing because it is always below the bounds.
[EDIT: I think I misunderstood, so this probably isn't much use :) ]
if(getY() + getHeight() > 200){
yspeed *= -0.981;
}
You're negating the vertical velocity on every update. I'd probably try handling gravity in update-sized slices. Assuming you're doing 30 updates per second (for 30fps), maybe something like
// Define some constants
SecondsPerUpdate = (1.0f / 30);
AccelDueToGravity = 0.981;
if(getY() + getHeight() > 200){
yspeed -= (AccelDueToGravity * SecondsPerUpdate);
}
I suspect it's because when the ball bounces, it will actually be slightly below the "ground", and at low speeds, it won't move back above the ground in one tick - so the next update() will see it still below the ground, and bounce again - but downwards this time, so the cycle continues.
You need to move the ball back up to ground level when it bounces, something like this:
if(getY() + getHeight() > 200){
yspeed *= -0.981;
setY(200 - getHeight());
}