GAM0183 Game Programming

33 downloads 4687 Views 2MB Size Report
Mar 31, 2009 ... Source: Beginning Java Game Programming, 2nd Edition,. Jonathan Harbour, Thomson Course Technology PTR, 2007. 3/31/2009. 3. Dr Andy ...
dissection/krufning

GAM0183 Game Programming Lectures 23, 24, 25, and 26 A basic 2-D bitmap-based Java game … and a few other things an Asteroids-style game

3/31/2009

Dr Andy Brooks

1

a basic 2-D vector-based Java game converted to a a basic 2-D bitmap-based Java game

3/31/2009

Dr Andy Brooks

2

Source: Beginning Java Game Programming, 2nd Edition, Jonathan Harbour, Thomson Course Technology PTR, 2007 http://www.jharbour.com/ http://www.amazon.com/Beginning-Java-Game-Programming-Second/dp/1598634763 http://my.safaribooksonline.com/9781598634761

3/31/2009

Dr Andy Brooks

3

an Asteroids-style game

part of Applet Viewer

B C

frameRate

3/31/2009

“+90” is a bug

Dr Andy Brooks

800 x 600, now larger

4

The bitmap images PNG Portable Network Graphics format

• bluespace.png is the background – artist Reiner Prokein • http://reinerstileset.4players.de/englisch.html

• spaceship.png is the spaceship – based on a design by Reiner Prokein

• plasmashot.png is the bullet • asteroid1.png, asteroid2.png, asteroid3.png, asteroid4.png, asteroid5.png are various images of asteroids – artist Edgar Ibarra 3/31/2009

Dr Andy Brooks

5

PNG http://en.wikipedia.org/wiki/Portable_Network_Graphics

• Transparency of image – “PNG offers a variety of transparency options. With truecolor and greyscale images either a single pixel value can be declared as transparent or an alpha channel can be added (enabling any percentage of partial transparency to be used).”

• Compression – “PNG uses a non-patented lossless data compression method known as DEFLATE, which is the same algorithm used in the zlib compression library.” 3/31/2009

Dr Andy Brooks

6

PNG http://en.wikipedia.org/wiki/Portable_Network_Graphics

• Comparison with JPEG – “JPEG (Joint Photography Experts Group) can produce a smaller file than PNG for photographic (and photolike) images, since JPEG uses a lossy encoding method specifically designed for photographic image data. Using PNG instead of a high-quality JPEG for such images would result in a large increase in filesize (often 5–10 times) with negligible gain in quality.”

3/31/2009

Dr Andy Brooks

7

http://foldoc.org/ • lossless – A term describing a data compression algorithm which retains all the information in the data, allowing it to be recovered perfectly by decompression.

• lossy – A term describing a data compression algorithm which actually reduces the amount of information in the data, rather than just the number of bits used to represent that information. 3/31/2009

Dr Andy Brooks

8

image transparency

• The figures shows asteroid1.png and spaceship.png loaded into GIMP, a graphics editor. – http://gimp-win.sourceforge.net/stable.html (Windows GIMP installer)

• The checkerboard background is a transparent region. – The bitmap images in GalacticWar have transparent regions. 3/31/2009

Dr Andy Brooks

9

package view in the Eclipse IDE

3/31/2009

Dr Andy Brooks

10

Java class: Point2D

helper class

package bitMapGame; // by Jonathan S. Harbour class Point2D extends Object { private double x, y; //int constructor Point2D(int x, int y) { setX(x); setY(y); } //float constructor Point2D(float x, float y) { setX(x); setY(y); } //double constructor Point2D(double x, double y) { setX(x); setY(y); }

3/31/2009

Dr Andy Brooks

4 bytes 8 bytes

11

Java class: Point2D

helper class

//X property double X() { return x; } public void setX(double x) { this.x = x; } public void setX(float x) { this.x = (double) x; } public void setX(int x) { this.x = (double) x; } //Y property double Y() { return y; } public void setY(double y) { this.y = y; } public void setY(float y) { this.y = (double) y; } public void setY(int y) { this.y = (double) y; } }

• Point2D is a helper class for (X,Y) coordinates. • “There is a Java library that also contains a Point2D class, but it was unsuitable for our purposes here.” (Jonathan Harbour) java.awt.geom.Point2D 3/31/2009

Dr Andy Brooks

12

BaseVectorShape->BaseGameEntity

Java class: BaseGameEntity • BasicGameEntity has the basic position, velocity, and rotation variables. facing

(0,0)

270o

3/31/2009

800x 600

-90,270o

0o

X

Y

moving

90o 180o

Dr Andy Brooks

180o

0o 90o

13

Java class: BaseGameEntity package bitMapGame; //Base game entity class public class BaseGameEntity extends Object { //variables protected boolean alive; protected double x,y; protected double velX, velY; protected double moveAngle, faceAngle; //accessor methods public boolean isAlive() { return alive; } public double getX() { return x; } public double getY() { return y; } public double getVelX() { return velX; } public double getVelY() { return velY; } public double getMoveAngle() { return moveAngle; } public double getFaceAngle() { return faceAngle; } 3/31/2009

Dr Andy Brooks

14

Java class: BaseGameEntity //mutator methods public void setAlive(boolean alive) { this.alive = alive; } public public public public

void void void void

setX(double incX(double setY(double incY(double

public public public public

void void void void

setVelX(double incVelX(double setVelY(double incVelY(double

public public public public

void void void void

setFaceAngle(double incFaceAngle(double setMoveAngle(double incMoveAngle(double

3/31/2009

x) i) y) i)

{ { { {

this.x this.x this.y this.y

= x; } += i; } = y; } += i; }

velX) { this.velX i) { this.velX += velY) { this.velY i) { this.velY +=

= velX; } i; } = velY; } i; }

angle){this.faceAngle=angle;} i) { this.faceAngle += i; } angle){this.moveAngle=angle;} i) { this.moveAngle += i; }

Dr Andy Brooks

15

Java class: BaseGameEntity //default constructor BaseGameEntity() { setAlive(false); setX(0.0); setY(0.0); setVelX(0.0); setVelY(0.0); setMoveAngle(0.0); setFaceAngle(0.0); }

} 3/31/2009

Dr Andy Brooks

16

Bitmap-based graphics in Java quotes from Jonathan S Harbour

• “The most amazing thing about the Graphics2D class is how its methods for manipulating 2D graphics work equally well with vectors and bitmaps.” – translating, rotating, scaling

• “The real difference when working with images is that you will need to create a separate AffineTransform object to manipulate the Image object, rather than going directly through Graphics2D.” • “Remember, a transform is a process that manipulates the position, rotation, or scale factor of a shape or image.” 3/31/2009

Dr Andy Brooks

17

Java class: ImageEntity Sprite makes use of ImageEntity.

• ImageEntity inherits from BaseGameEntity. • ImageEntity can load and draw bitmaps. • ImageEntity needs a reference to the applet.

3/31/2009

Dr Andy Brooks

18

The Image class http://java.sun.com/javase/6/docs/api/

• “The abstract class Image is the superclass of all classes that represent graphical images.” • public abstract int getWidth(ImageObserver observer) – “Determines the width of the image.”

• public abstract int getHeight(ImageObserver observer) – “Determines the height of the image.”

3/31/2009

Dr Andy Brooks

19

Java class: ImageEntity package bitMapGame; //Base game image class for bitmapped game entities import import import import

java.awt.*; java.awt.geom.*; java.net.*; java.applet.*;

public class ImageEntity extends BaseGameEntity { //variables protected Image image; protected Applet applet; protected AffineTransform at; protected Graphics2D g2d; //default constructor ImageEntity(Applet a) { applet = a; setImage(null); setAlive(true); } 3/31/2009

Dr Andy Brooks

20

Java class: ImageEntity public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } public int return } public int return }

width() { getImage().getWidth(applet); height() { getImage().getHeight(applet);

public double getCenterX() { return getX() + width() / 2; }

(X,Y)

width

height public double getCenterY() { return getY() + height() / 2; } public void setGraphics(Graphics2D g) { g2d = g; } 3/31/2009 Dr Andy Brooks

21

Java class: ImageEntity, Java method: getURL private URL getURL(String filename) { URL url = null; try { url = this.getClass().getResource(filename); } catch (Exception e) { }

return url; } http://java.sun.com/javase/6/docs/api/ “Class URL represents a Uniform Resource Locator, a pointer to a "resource" on the World Wide Web. A resource can be something as simple as a file or a directory, or it can be a reference to a more complicated object, such as a query to a database or to a search engine.” 3/31/2009

Dr Andy Brooks

22

Java class: ImageEntity, Java method: load(String filename) public void load(String filename) { setImage(applet.getImage(getURL(filename))); while(getImage().getWidth(applet) 360) setFaceAngle(rotRate); }

//generic sprite state variable (alive,dead,collided, etc) public int state() { return currentState; } public void setState(int state) { currentState = state; } 3/31/2009

Dr Andy Brooks

32

Java class: Sprite //returns a bounding rectangle public Rectangle getBounds() { return entity.getBounds(); } //sprite position public Point2D position() { return pos; } public void setPosition(Point2D pos) { this.pos = pos; } //sprite movement velocity public Point2D velocity() { return vel; } public void setVelocity(Point2D vel) { this.vel = vel; } //returns the center of the sprite as a Point2D public Point2D center() { return(new Point2D(entity.getCenterX(),entity.getCenterY())); } //generic variable for selectively using sprites public boolean alive() { return entity.isAlive(); } public void setAlive(boolean alive) {entity.setAlive(alive);} 3/31/2009

Dr Andy Brooks

33

Java class: Sprite //face angle indicates which direction sprite is facing public double faceAngle() { return entity.getFaceAngle(); } public void setFaceAngle(double angle) { entity.setFaceAngle(angle); } public void setFaceAngle(float angle) { entity.setFaceAngle((double) angle); } public void setFaceAngle(int angle) { entity.setFaceAngle((double) angle); }

Overloading of setFaceAngle to account for int, float, and double.

3/31/2009

Dr Andy Brooks

34

Java class: Sprite //move angle indicates direction sprite is moving public double moveAngle() { return entity.getMoveAngle(); } public void setMoveAngle(double angle) { entity.setMoveAngle(angle); } public void setMoveAngle(float angle) { entity.setMoveAngle((double) angle); } public void setMoveAngle(int angle) { entity.setMoveAngle((double) angle); }

Overloading of setMoveAngle to account for int, float, and double.

3/31/2009

Dr Andy Brooks

35

Java class: Sprite //returns the source image width/height public int imageWidth() { return entity.width(); } public int imageHeight() { return entity.height(); } //check for collision with a rectangular shape public boolean collidesWith(Rectangle rect) { return (rect.intersects(getBounds())); } //check for collision with another sprite public boolean collidesWith(Sprite sprite) { return (getBounds().intersects(sprite.getBounds())); } //check for collision with a point public boolean collidesWith(Point2D point) { return (getBounds().contains(point.X(), point.Y())); } } 3/31/2009

Dr Andy Brooks

36

The Rectangle class http://java.sun.com/javase/6/docs/api/

• public boolean intersects(Rectangle r) “Determines whether or not this Rectangle and the specified Rectangle intersect. Two rectangles intersect if their intersection is nonempty.” • public boolean contains(int x, int y) “Checks whether or not this Rectangle contains the point at the specified location (x,y).” 3/31/2009

Dr Andy Brooks

37

Java class: GalacticWar package bitMapGame; //by Jonathan S. Harbour import java.applet.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.util.*; import java.lang.System; //Primary class for the game public class GalacticWar extends Applet implements Runnable, KeyListener { //global constants static int SCREENWIDTH = 800; static int SCREENHEIGHT = 600; static int CENTERX = SCREENWIDTH / 2; static int CENTERY = SCREENHEIGHT / 2; static int ASTEROIDS = 10; static int BULLETS = 10; static int BULLET_SPEED = 4; static double ACCELERATION = 0.05; 3/31/2009

Dr Andy Brooks

38

Java class: GalacticWar //sprite state values static int SPRITE_NORMAL = 0; static int SPRITE_COLLIDED = 1; //the main thread becomes the game loop Thread gameloop; //double buffer objects BufferedImage backbuffer; Graphics2D g2d; //various toggles boolean showBounds = true; boolean collisionTesting = true; //define the game objects ImageEntity background; Sprite ship; Sprite[] ast = new Sprite[ASTEROIDS]; Sprite[] bullet = new Sprite[BULLETS]; int currentBullet = 0;

3/31/2009

Dr Andy Brooks

39

The System class http://java.sun.com/javase/6/docs/api/

• “The System class contains several useful class fields and methods.” – System.out.println(“hello world”);

• public static long currentTimeMillis() – “Returns the current time in milliseconds. Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the underlying operating system and may be larger. For example, many operating systems measure time in units of tens of milliseconds.” – “Returns:the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.” 3/31/2009

Dr Andy Brooks

40

Java class: GalacticWar //create a random number generator Random rand = new Random(); //define sound effects objects SoundClip shoot = new SoundClip(); SoundClip explode; //simple way to handle multiple keypresses boolean keyDown, keyUp, keyLeft, keyRight, keyFire;

//frame rate counter int frameCount = 0, frameRate = 0; long startTime = System.currentTimeMillis();

A long is an integer stored using 8 bytes rather than 4 bytes.

3/31/2009

Dr Andy Brooks

41

Java class: GalacticWar, Java method: init() //applet init event public void init() { shoot.load("shoot.wav"); //create the back buffer for smooth graphics backbuffer = new BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_RGB); g2d = backbuffer.createGraphics(); //load the background image background = new ImageEntity(this); background.load("bluespace.png"); //set up the ship ship = new Sprite(this, g2d); ship.load("spaceship.png"); ship.setPosition(new Point2D(CENTERX, CENTERY)); ship.setAlive(true); //set up the bullets for (int n = 0; n SCREENWIDTH){ bullet[n].setAlive(false); } //update bullet's y position bullet[n].updatePosition(); //bullet disappears at top/bottom edge if (bullet[n].position().Y() < 0 || bullet[n].position().Y() > SCREENHEIGHT){ bullet[n].setAlive(false); } bullet[n].setState(SPRITE_NORMAL); } } }3/31/2009 Dr Andy Brooks

54

Java class: GalacticWar, Java method: updateAsteroids() //Update the asteroids based on velocity public void updateAsteroids() { //move and rotate the asteroids for (int n = 0; n < ASTEROIDS; n++) { if (ast[n].alive()) { //update the asteroid's position and rotation ast[n].updatePosition(); ast[n].updateRotation(); int w = ast[n].imageWidth()-1; int h = ast[n].imageHeight()-1; double newx = ast[n].position().X(); double newy = ast[n].position().Y();

3/31/2009

Dr Andy Brooks

55

Java class: GalacticWar, Java method: updateAsteroids() //wrap the asteroid around the screen edges if (ast[n].position().X() < -w) newx = SCREENWIDTH + w; else if (ast[n].position().X() > SCREENWIDTH+w) newx = -w; if (ast[n].position().Y() < -h) newy = SCREENHEIGHT + h; else if (ast[n].position().Y() > SCREENHEIGHT+h) newy = -h; ast[n].setPosition(new Point2D(newx,newy)); ast[n].setState(SPRITE_NORMAL); }

} }

3/31/2009

Dr Andy Brooks

56

Java class: GalacticWar, Java method: checkCollisions() //Test asteroids for collisions with ship or bullets public void checkCollisions() { //check for collision between asteroids and bullets for (int m = 0; m BULLETS - 1) currentBullet = 0; bullet[currentBullet].setAlive(true); //set bullet's starting point int w = bullet[currentBullet].imageWidth(); int h = bullet[currentBullet].imageHeight(); double x = ship.center().X() - w/2; double y = ship.center().Y() - h/2; bullet[currentBullet].setPosition(new Point2D(x,y));

3/31/2009

Dr Andy Brooks

66

Java class: GalacticWar, Java method: fireBullet() //point bullet in same direction ship is facing bullet[currentBullet].setFaceAngle(ship.faceAngle()); bullet[currentBullet].setMoveAngle(ship.faceAngle()-90); //fire bullet at angle of the ship double angle = bullet[currentBullet].moveAngle(); double svx = calcAngleMoveX(angle) * BULLET_SPEED; double svy = calcAngleMoveY(angle) * BULLET_SPEED; bullet[currentBullet].setVelocity(new Point2D(svx, svy)); //play shoot sound shoot.play(); } static int BULLET_SPEED = 4; Previously in Asteroids: double angle = bullet[currentBullet].getMoveAngle(); double svx = ship.getVelX(); double svy = ship.getVelY(); bullet[currentBullet].setVelX(svx+calcAngleMoveX(angle) * 2); bullet[currentBullet].setVelY(svy+calcAngleMoveY(angle) * 2); 3/31/2009

Dr Andy Brooks

67

Java class: GalacticWar, Java method: calcAngleMoveX(double angle) Java method: calcAngleMoveY(double angle) //Angular motion for X and Y is calculated

public double calcAngleMoveX(double angle) { double movex = Math.cos(angle * Math.PI / 180); return movex; } public double calcAngleMoveY(double angle) { double movey = Math.sin(angle * Math.PI / 180); return movey; }

Similar calculations in Asteroids. 3/31/2009

Dr Andy Brooks

68