140 likes | 294 Views
Animation. Most games have moving components, how do we make things move? We will track the location of an object using an x,y coordinate We will draw the item onto a Graphics object in paintComponent But we need to change x,y from time to time to give it movement How?
E N D
Animation • Most games have moving components, how do we make things move? • We will track the location of an object using an x,y coordinate • We will draw the item onto a Graphics object in paintComponent • But we need to change x,y from time to time to give it movement • How? • Java has a built-in class called Timer • The Timer generates an ActionEvent every few milliseconds • The ActionEvent calls the actionPerformed method • In actionPerformed, we change x,y and then do repaint() • This requires adding to our program • ActionListener and actionPerformed method • Timer object • paintComponent method
Timer class • A Timer object is instantiated by: • Timer t = new Timer(duration, handler); • duration is the time (in milliseconds) that elapses between Timer-generated events, such as 20 or 50 ms • handler is the object that handles the Timer-generated events – we will use this just like we have for our addActionListener statements • The steps are to add • import java.awt.event.*; • “implements ActionListener” to our class header • declare the Timer as a class variable: private Timer t; • instantiate the Timer: t = new Timer(10, this); • but use whatever value you want for the duration, 10 or 20 would be adequate for most applications • start the timer: t.start( ); • if we need to stop the timer: t.stop( ); • That’s about all there is to it!
import javax.swing.*; import java.awt.event.*; public class TimerSkeleton implements ActionListener { private Timer t; // other instance data go here as needed public TimerSkeleton( ) { t = new Timer(10, this); t.start( ); // other initialization operations go here as needed } // other methods go here as needed public void actionPerformed(ActionEvent e) { // whatever you want to happen when the Timer pulses go here } } TimerSkeleton
What Should actionPerformed Do? • This depends on why you are using a Timer • To move an object in a Graphics panel (e.g., a ball) • alter the x and y coordinates of the ball and call repaint( ) • For a Game • calculate where game objects (say a spacecraft or a missile) have moved and redraw them • you might have multiple objects to move, move them all • if you have a variable number of objects, use an array (or two arrays, x[] and y[]) • For Animation • if our item being drawn is represented by an array of different figures • then just increment the array index and repaint( ) • you might pre-load an array of Images for drawImage where each image is slightly different from the previous one • In any case, in actionPerformed, also make sure the object has not been moved off the screen
Moving an Image to Create Motion • Imagine that your class draws on a JPanel an ImageIcon (say of a spaceship) • currently, the spaceship is located at coordinates x, y • the following actionPerformed method will randomly move the spaceship on the screen • x and y should be class variables so that you could do g.drawImage(image, x, y, this); in your paintComponent method public void actionPerformed(ActionEvent e) { int dx = generator.nextInt(2); // generate a # from -1 to +1 int dy = generator.nextInt(2); // generate a # from -1 to +1 x += dx; // move the piece in a random x direction y += dy; // move the piece in a random y direction repaint( ); // assume repaint does drawImage at x, y }
Full Example Code public TimerExample( ) { t = new Timer(10, this); t.start( ); x = 150; y = 150; repaint( ); } public void actionPerformed(ActionEvent ev) { int distanceX = generator.nextInt(2)); int distanceY = generator.nextInt(2)); x += distanceX; y += distanceY; repaint( ); } public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.red); g.fillOval(x, y, 5, 5); } At each Timer Event, randomly move the object (a red circle) up/down/left/right by 0 or 1 unit Note: we could make it more realistic by generating a random int from 1-9 and move it in one of 8 directions or leave it stationary (1 = upper left, or subtract 1 from x and y, 2 = straight up, etc)
Better Motion • The random motion from the previous example would make the object on the screen look like a bee, buzzing around • What if we wanted a more purposeful movement? For instance in a straight line? • Lets add 2 more variables, dx and dy • (delta x means “change in x”, delta y means “change in y”, we use d because we don’t have the Greek letter delta) • For actionPerformed, just do x=x+dx; y=y+dy; • If dx=1 and dy=2, then our object will move in a straight line to the right 1 pixel and down 2 pixels at each movement, or a diagonal downward line • If dx=0 and dy=-1, then the object moves straight up • We do not want dx=0 and dy=0, why not?
Dealing With Edges/Borders • Imagine we start our object at x=0, y=0 and use dx=1, dy=1 • The animation will move the object steadily down to the right but eventually (quickly) the object will move right off the screen • this does not cause an error, but it removes the object from our graphics panel • We want to deal with borders, how? • Wrap-around: if(x>=X_SIZE-20) x=20; if(x<=20) x=X_SIZE-20; • Stop: if(x>=X_SIZE-20||x<=20) dx=0; • Bounce: if(x>=X_SIZE||x<=20) dx*=-1; • Random bounce: if(x>=X_SIZE||x<=20) dx=gen.nextInt(5)-2; • we would similarly handle y and dy • we might change dx and dy whenever the object hits any edge
Handling Multiple ActionEvents private static class ExamplePanel extends JPanel implements ActionListener { private Timer t; private JButton b; public ExamplePanel( ) { t = new Timer(1000, this); t.start(); b = new JButton("Button"); b.addActionListener(this); add(b); } public void actionPerformed(ActionEvent e) { if(e.getSource( )==t) System.out.println("Timer pulsed"); else if(e.getSource( )==b) System.out.println("Button pressed"); } }
Moving Multiple Items • How would a game like Asteroids work? • we need to keep track of the <x, y> coordinates of multiple values, just like we did with our paint program • so we create an array of x, y, dx and dy int values • private int[ ] x, y, dx, dy; // dx, dy – velocity of x, y • private int num; // number of items in the arrays • actionPerformed now manipulates all items in the array and paintComponent draws them all public void actionPerformed(ActionEvent e) { for(int i=0;i<num;i++) { x[i]+=dx[i]; y[i]+=dy[i]; } } If x[i] or y[i] reaches a boundary (0 or max X/Y) then change dx or dy to be the opposite (multiply by -1)
Determining Collisions • For a shooting game, we want to see if we actually successfully shot the object, how? • We will have two x,y values, we will call them mx, my (for me) and ex, ey (for enemy) • We can’t just see if mx==ex&&my==ey because those compare individual pixels and our objects will be larger • Assume that the “enemy” is a circle whose bounding box starts at ex,ey and is 10x10 • the center of the enemy is actually ex+5,ey+5 • our “missile”, mx, my needs to touch some part of that circle, which means that Math.abs(mx-(ex+5))<=5&&Math.abs(my-(ey+5))<=5 – that is, our object is somewhere within the radius of the enemy • If our object is something different from a small missile/bullet or the enemy is different from a round shape, it becomes a bit more challenging
Bouncing Balls • Today we will work with a ball class and make several of them bounce around the screen • A ball will comprise its current location (x, y), its velocity (dx, dy), a size and a color • The ball will be able to move itself • x+=dx; y+=dy; but also check to see if it is at a border • The ball will be able to change direction or rebound • change dx and dy, either multiply them by -1, or change them randomly • The ball will be able to draw itself on a Graphics object • The ball will be able to determine if it has collided with another ball (passed as a parameter) • The ball will be able to change its own size • The ball will have to be able to return its own x value, y value and size (to be used later)
Bouncing Balls Program • We will start with an array of balls • We can either randomly generate their initial locations or we can use the MouseListener so that the user can place them on the Graphics Panel by clicking • The timer starts and in actionPerformed, we use a for loop to iterate through each ball and do b[i].move(); (assuming our balls are stored in an array b) • If we want to handle collisions • In actionPerformed, each time we move a ball, we use another (nested) for loop to iterate through the other balls and text b[i].collide(b[j]) – this is true if b[i] and b[j] overlap at all • If true, we want to change one or both balls’ directions by doing b[i].bounce(); b[j].bounce();
Ball Game • The game for today is similar to the BouncingBalls program • The user, using the mouse, places several balls in the graphics area • As a ball collides with another, one ball disappears and the other grows in size • if(b[i].collide(b[j])) { b[i].grow(b[j].size/2); b[j]=null;) • notice that with each collision, there is 1 fewer balls but the other ball remaining has grown, so what we get as time goes on are fewer and fewer but larger and larger balls • The game goes until there is only 1 ball left on the screen • We can output the amount of time that has elapsed, the idea is to see how long you can go before you are down to 1 ball