Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

grom358

Collision and Painting

This topic is 5221 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am writing a basic demo (Java applet) that has bouncing balls. Currently I only have collision detection against the edges. My game loop is time based which updates the position of the balls and calls repaint(). My problem is say I have a ball that is 3 pixels from the right edge and the ball is moving at 10 pixels per frame to the right. Now on the next loop, the ball will end up 7 pixels from the right edge and moving to the left. So in other words you never see the ball hit the edge. Is this how it should be? Also for ball collision I found this thread http://www.gamedev.net/community/forums/topic.asp?topic_id=176238 that said you handle them as follows: 1. For each ball, you calculate a vector representing a distance travelled in time t. 2. You check each of these for collisions, and determine which collision is earliest. 3. You move everything up to that point, recalculate the vectors, and go back to step 1. How do I integrate this algorithm into the time based game loop? By the way, I''m using a time based game loop so I can create realistic speeds for the balls. That is if 100 pixels represents a meter and the ball moves 100 pixels in a second, then the ball is travelling at 1 meter per second. Is there other approaches to achieve this? P.S. I am working towards writing a simple pool game (no spin or jump shots).
import java.awt.event.*;
import javax.swing.*;

public class Main extends JApplet implements ActionListener {
	Timer gameTimer;
	Table table; // The applet''s content pane


	int loopSpeed = 10; // Loop every 10ms


	private void createGUI() {
		// Custom component to draw the current image

		// at a particular offset.

		table = new Table(getWidth(), getHeight());
		setContentPane(table);
	}

	public void init() {
		// Execute a job on the event-dispatching thread:

		// creating this applet''s GUI.

		try {
			javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
				public void run() {
					createGUI();
				}
			});
		} catch (Exception e) {
			System.err.println("createGUI didn''t successfully complete");
		}

		// Set up the timer that will perform the animation.

		gameTimer = new javax.swing.Timer(loopSpeed, this);
		//timer.setInitialDelay(pause);

		//timer.setCoalesce(false);

		gameTimer.start(); //Start the animation.		

	}

	public void start() {
		gameTimer.restart();
	}

	public void stop() {
		gameTimer.stop();
	}

	public void actionPerformed(ActionEvent evt) {
		table.nextFrame();
		table.repaint();
	}
}
import java.awt.*;
import javax.swing.*;

public class Table extends JComponent {
	private Ball ballA = new Ball(10, 10, 5, 2, Color.BLUE);
	private Ball ballB = new Ball(50, 50, -2, -3, Color.RED);
	private Ball ballC = new Ball(30, 30, 3, 1, Color.GREEN);
	private int width;
	private int height;
	
	public Table(int tableWidth, int tableHeight) {
		setOpaque(true);
		setBackground(Color.WHITE);
		width = tableWidth;
		height = tableHeight;
	}
	
	public void nextFrame() {
		ballA.hitTableEdge(width, height);
		ballB.hitTableEdge(width, height);
		ballC.hitTableEdge(width, height);
		
		ballA.nextFrame();
		ballB.nextFrame();
		ballC.nextFrame();
	}

	protected void paintComponent(Graphics g) {			
		Graphics2D g2d = (Graphics2D) g;

		// clear screen in background 

		g2d.setColor(getBackground());
		g2d.fillRect(0, 0, this.getSize().width, this.getSize().height);

		g2d.setRenderingHint(
			RenderingHints.KEY_ANTIALIASING,
			RenderingHints.VALUE_ANTIALIAS_ON);

		ballA.paint(g2d);
		ballB.paint(g2d);
		ballC.paint(g2d);
	}
}
import java.awt.*;

public class Ball {
	private Color c;
	private int radius;
	private int x_pos;
	private int y_pos;
	private int x_speed;
	private int y_speed;
	
	private int dx;
	private int dy;	
	
	public Ball(int x, int y, int xSpeed, int ySpeed, Color ballColor) {
		radius = 10;
		c = ballColor;
		x_pos = x;
		y_pos = y;
		x_speed = xSpeed;
		y_speed = ySpeed;
	}
	
	public void hitTableEdge(int tableWidth, int tableHeight) {
		// Clip left edge

		if (x_pos - radius + x_speed < 0) {			
			dx = -(x_pos - radius);			
			x_speed = -x_speed;
		}
		// Clip right edge 

		else if (x_pos + radius + x_speed > tableWidth) {
			dx = tableWidth - (x_pos + radius);
			x_speed = -x_speed;
		}
		// Clip top edge

		else if (y_pos - radius + y_speed < 0) {
			dy = -(y_pos - radius);
			y_speed = -y_speed;
		}
		// Clip bottom edge

		else if (y_pos + radius + y_speed > tableHeight) {
			dy = tableHeight - (y_pos + radius);
			y_speed = -y_speed;
		}
	}
	
	public void nextFrame() {
		x_pos += x_speed + dx;
		y_pos += y_speed + dy;
		dx = 0;
		dy = 0;
	}
	
	public void paint(Graphics2D g) {
		g.setColor(c);
		// paint ball

		g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
	}
}

Share this post


Link to post
Share on other sites
Advertisement
You could do something like this:

ball.x+=ball.xSpeed;
if(ball.x+ball.radius>width)
{
ball.xSpeed=-ball.xSpeed;//it must bounce
ball.x=(2*width)-ball.x;
}

If the width is, say, 400 px.
The ball is 3 px width.
The ball moves at 10 pixels per frame.
The ball is at x=397.

The next frame, the ball will be at x=(2*400)-(397+10)=393 px.
You don't see it hitting the edge, but it has bounced correctly.

[edited by - Koroljov on June 5, 2004 11:01:53 AM]

[edited by - koroljov on June 5, 2004 11:03:53 AM]

Share this post


Link to post
Share on other sites
Oops, I didn't notice there was an error in my bounce code. Is it normal to not see the ball colliding (although I never notice with my eyes)?

[edited by - grom358 on June 5, 2004 11:28:46 AM]

Share this post


Link to post
Share on other sites
Below is updated code to fix bounce calculations:


import java.awt.*;

public class Ball {
private Color c;
private int radius;
private int x_pos;
private int y_pos;
private int x_speed;
private int y_speed;

private int tableWidth;
private int tableHeight;

public Ball(Table t, int x, int y, int xSpeed, int ySpeed, Color ballColor) {
tableWidth = t.getTableWidth();
tableHeight = t.getTableHeight();
radius = 10;
c = ballColor;
x_pos = x;
y_pos = y;
x_speed = xSpeed;
y_speed = ySpeed;
}

public void nextFrame() {
int old_x = x_pos;
int old_y = y_pos;
x_pos += x_speed;
y_pos += y_speed;

// Hit left edge

if (x_pos - radius < 0) {
x_speed = -x_speed; // bounce right

x_pos = radius + (x_speed - (old_x - radius));
}
// Hit right edge

else if (x_pos + radius > tableWidth) {
x_speed = -x_speed; // bounce left

x_pos = old_x - ((old_x-tableWidth + radius) - x_speed);
}
// Hit top edge

else if (y_pos - radius < 0) {
y_speed = -y_speed; // bounce down

y_pos = radius + (y_speed - (old_y - radius));
}
// Hit bottom edge

else if (y_pos + radius > tableHeight) {
y_speed = -y_speed; // bounce up

y_pos = old_y - ((old_y-tableHeight + radius) - y_speed);
}
}

public void paint(Graphics2D g) {
g.setColor(c);
// paint ball

g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
}
}

Share this post


Link to post
Share on other sites
i have the exact same thing with bouncing balls and such. The way i do it is i just keep moving until i hit a wall. If a wall is hit, the ball is moved back along it's movement vector just enough so that the ball is making contact with the wall but is not inside of it. Then the ball's movement vector is changed to reflect the bounce and resulting change in direction. The movement and the correction are applied in the same frame, so you always see them "hit" the walls.

[edited by - leiavoia on June 5, 2004 1:52:00 PM]

Share this post


Link to post
Share on other sites
leiavoia, I''m not sure how I can do that with a timeslot game loop. That is if a ball is moving 10 units per timeslot and it is say 3 units from the wall. Then at the end of the timeslot it should be 7 units from the wall.

Are the following step correct:
1) Move the balls
2) If a ball has collide with a wall, move it back so it is making contact with the wall
3) Issue a repaint
4) Move the ball to the end position for the timeslot
5) Repeat 2-4 for all balls
6) Issue a repaint

Where I am getting confused is that I want all the balls to move in sync. That is at the end of the timeslot all the balls have moved by the length of the movement vector. Also I assume from your reply that I should be painting a frame showing the collision? Currently I only paint a frame at the end of the timeslot. Each timeslot is 10 milliseconds and therefore painting 100 frames per second. Although some of the frames will be dropped.

Also is using timeslots even the right approach for this animation?

Share this post


Link to post
Share on other sites
I have gotten some help from the #gamedev and have now changed the game loop to use the time since the last frame was painted (dt). The collision detection determines when the earliest collision occurs (t). If no collision occurs then t = dt. Then the ball positions are updated by calculating the movement vector for t. A repaint() is issued, then the game loop waits for 10 milliseconds (to avoid overloading the paint system) and then repeats. As a result I can now also easily add ball collision.


package net.grom.pool;

import javax.swing.*;

/**
* @author grom
*/

public class Main extends JApplet implements Runnable {
Thread gameLogic; // game logic thread

Table table; // The applet's content pane


private void createGUI() {
// Custom component to draw the current image

// at a particular offset.

table = new Table(getWidth(), getHeight());
setContentPane(table);
}

public void init() {
// Execute a job on the event-dispatching thread:

// creating this applet's GUI.

try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}

public void start() {
gameLogic = new Thread(this);
gameLogic.start(); //Start the game.

}

public void stop() {

}

public void run() {
while (true) {
table.nextFrame();
table.repaint();

try {
Thread.sleep(10); // sleep for 10 milliseconds

} catch (InterruptedException ex) {
// do nothing

}
}
}
}

package net.grom.pool;

import java.awt.*;
import javax.swing.*;

/**
* @author grom
*/

public class Table extends JComponent {
private Ball ballA, ballB, ballC;
private int width;
private int height;

private long prev_t = -1;
private int dt = 0; // Time since last frame


public Table(int tableWidth, int tableHeight) {
setOpaque(true);
setBackground(Color.WHITE);
width = tableWidth;
height = tableHeight;

ballA = new Ball(this, 10, 10, 5, 2, Color.BLUE);
ballB = new Ball(this, 50, 50, -2, -3, Color.RED);
ballC = new Ball(this, 30, 30, 3, 1, Color.GREEN);
}

public int getTableWidth() {
return width;
}

public int getTableHeight() {
return height;
}

/**
* Moves to next frame
*/

public void nextFrame() {
// calculate time since last frame

dt = (int) (System.currentTimeMillis() - prev_t);
if (dt <= 0) {
// do nothing

return;
}
//System.out.println("" + dt + "ms since last frame");


int t = dt;
// if a wall collision occurs

// then we only paint till the collision

t = Math.min(t, ballA.testWallCollision(dt));
t = Math.min(t, ballB.testWallCollision(dt));
t = Math.min(t, ballC.testWallCollision(dt));

ballA.nextFrame(t);
ballB.nextFrame(t);
ballC.nextFrame(t);
}

protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;

// clear screen in background

g2d.setColor(getBackground());
g2d.fillRect(0, 0, this.getSize().width, this.getSize().height);

g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);

ballA.paint(g2d);
ballB.paint(g2d);
ballC.paint(g2d);

// keep track of when frame was painted

prev_t = System.currentTimeMillis();
}
}

package net.grom.pool;

import java.awt.*;

/**
* @author grom
*/

public class Ball {
private Color c;
private int radius;
private int x_pos;
private int y_pos;
private int x_speed; // pixels per 10 millisecond

private int y_speed;

private Table table;
private int tableWidth;
private int tableHeight;

public Ball(Table t, int x, int y, int xSpeed, int ySpeed, Color ballColor) {
table = t;
tableWidth = t.getTableWidth();
tableHeight = t.getTableHeight();
radius = 10;
c = ballColor;
x_pos = x;
y_pos = y;
x_speed = xSpeed;
y_speed = ySpeed;
}

public int testWallCollision(int t) {
// Calculate the movement vector for t

// NOTE: we divide the speed by 10 since we are measuring

// the speed in pixels per 10 milliseconds

// To find the nearest pixel we round to the closest integer

int dx = Math.round((x_speed/10f) * t); // x component of movement vector

int dy = Math.round((y_speed/10f) * t); // y component of movement vector


int collision_t = t;
// time = distance / speed

if (x_pos - radius + dx < 0) {
collision_t = (int) ((x_pos - radius) / (x_speed/10f));
} else if (x_pos + radius + dx > tableWidth) {
collision_t = (int) ((tableWidth - (x_pos + radius)) / (x_speed/10f));
} else if (y_pos - radius + dy < 0) {
collision_t = (int) ((y_pos - radius) / (y_speed/10f));
} else if (y_pos + radius + dy > tableHeight) {
collision_t = (int) ((tableHeight - (y_pos + radius)) / (y_speed/10f));
}
return Math.abs(collision_t); // no collision

}

public void nextFrame(int t) {
// Calculate the movement vector for t

// NOTE: we divide the speed by 10 since we are measuring

// the speed in pixels per 10 milliseconds

// To find the nearest pixel we round to the closest integer

int dx = Math.round((x_speed/10f) * t); // x component of movement vector

int dy = Math.round((y_speed/10f) * t); // y component of movement vector

x_pos += dx;
y_pos += dy;

// Bounce in opposite direction from the wall

if (x_pos - radius <= 0 || x_pos + radius >= tableWidth) {
x_speed = -x_speed;
} else if (y_pos - radius <= 0 || y_pos + radius >= tableHeight) {
y_speed = -y_speed;
}
}

public void paint(Graphics2D g) {
g.setColor(c);
// paint ball

g.fillOval(x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
}
}


[edited by - grom358 on June 6, 2004 10:44:07 PM]

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!