Define a Game Object with a Position and Collision Box
public class GameObj {
protected int x = 0;
protected int y = 0;
protected int width = 2; // Collision box width
protected int height = 2; // Collision box height
public GameObj (int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public int getX() { return this.x; }
public int getY() { return this.y; }
public int getWidth() { return this.width; }
public int getHeight() { return this.height; }
}
- The collision box information will later be accessed to check if this object has collided with another one
Make a Sprite Class to use with animated game objects
public class Sprite {
private BufferedImage spriteSheetImage;
private int frameWidth, frameHeight, frameCount, frameIndex, x, y;
public Sprite (BufferedImage spriteSheetImage, int frameWidth, int frameHeight, int frameCount) {
try {
// A sprite sheet image should contain evenly spaced areas containing different frames of the animation)
this.spriteSheetImage = spriteSheetImage;
this.frameWidth = frameWidth; // Width of each sprite frame/image in the sprite sheet
this.frameHeight = frameHeight; // Height of each sprite frame/image in the sprite sheet
this.frameCount = frameCount; // The number of frames/images in the sprite sheet
this.frameIndex = 0; // To keep track of which frame of the animation is in focus
}
catch (Exception e) {
e.printStackTrace();
}
}
public void update() {
frameIndex += 1;
if (frameIndex == frameCount) frameIndex = 0;
}
public BufferedImage getFrame() {
return spriteSheetImage.getSubimage( frameWidth*frameIndex, 0, frameWidth, frameHeight);
}
}
- See the animations lesson for more information about this.
- Import java.awt.image.BufferedImage
Define a Movable Object that can be Updated and Detect Collisions
public class Movable extends GameObj {
protected Sprite sprite;
protected float xVelocity = 0;
protected float yVelocity = 0;
protected float xAccel = 0;
protected float yAccel = 1;
protected boolean grounded = false;
public Movable(Sprite sprite, int x, int y, int width, int height) {
super(x, y, width, height);
this.sprite = sprite;
}
public void update (ArrayList<GameObj> solids ) {
this.sprite.update();
this.xVelocity += xAccel;
this.yVelocity += yAccel;
int collisionCheck = 0;
this.grounded = false;
if (xVelocity != 0 || yVelocity != 0) {
for (GameObj other : solids) {
if (this != other) {
collisionCheck = this.willCollide(other);
// If collision above
if (collisionCheck == 1) {
this.yVelocity = 0;
this.y = other.y + this.height/2 + other.height/2;
}
// If collision below
else if (collisionCheck == 3) {
this.yVelocity = 0;
this.y = other.y - this.height/2 - other.height/2;
this.grounded = true;
}
// If collision on right
if (collisionCheck == 2) {
this.xVelocity = 0;
this.x = other.x - this.width/2 - other.width/2;
}
// If collision on left
else if (collisionCheck == 4) {
this.xVelocity = 0;
this.x = other.x + this.width/2 + other.width/2;
}
// If collision is diagonal
else if (collisionCheck == 5) {
this.xVelocity = 0;
this.yVelocity = 0;
break;
}
}
}
this.x += xVelocity;
this.y += yVelocity;
}
}
public boolean checkCollision(GameObj other) {
return Math.abs(this.x - other.x) < (this.width/2 + other.width/2) &&
Math.abs(this.y - other.y) < (this.height/2 + other.height/2);
}
public int willCollide(GameObj other) {
if (Math.abs(this.x + this.xVelocity - other.x) < (this.width/2 + other.width/2) &&
Math.abs(this.y + this.yVelocity - other.y) < (this.height/2 + other.height/2))
{
if (Math.abs(this.y - other.y) < (this.height/2 + other.height/2)) {
if (this.xVelocity > 0) return 2; // Collision on right
else return 4; // Collision on left
}
else if (Math.abs(this.x - other.x) < (this.width/2 + other.width/2)) {
if (this.yVelocity < 0) return 1; // Collision above
else return 3; // Collision below
}
else return 5; // Diagonal collision
}
return 0; // No collision
}
public void setXVel(int setTo) { this.xVelocity = setTo; }
public void setYVel(int setTo) { this.yVelocity = setTo; }
public Sprite getSprite() { return this.sprite; }
}
- Import java.util.ArrayList
Define a Barrier Class to be Used as Solid Walls and Platforms
public class Barrier extends GameObj {
public Barrier (int tlX, int tlY, int width, int height) {
super(tlX + width/2, tlY + height/2, width, height);
}
}
- This allows us to make a collidable object with an easy way to specify the dimensions, it takes the top left corner coordiantes as parameters
Define a Nonplayer character to add to the game
public class PhysGameNPC extends Movable {
public PhysGameNPC(Sprite sprite, int x, int y, int width, int height) {
super(sprite, x, y, width, height);
changeDirection();
}
public void update (ArrayList<GameObj> solids) {
super.update(solids);
if (Math.random() < .1) changeDirection();
}
public void changeDirection() {
this.xVelocity = -2 + (int)(Math.random()*5);
}
}
- This can just move around, but you can program it do other things to make the game more interesting
Define a Hero to be Controlled by the User
public class PhysGameHero extends Movable{
public PhysGameHero(Sprite sprite, int x, int y, int width, int height) {
super(sprite, x, y, width, height);
}
}
- The game code will add controls to it
Define the Game Code to Put All of the Parts Together
public class PhysicsGame {
private PhysGameHero player1;
private ArrayList<PhysGameNPC> npcs;
private ArrayList<Barrier> barriers;
private ArrayList<GameObj> solids;
public PhysicsGame () {
try {
npcs = new ArrayList<PhysGameNPC>();
barriers = new ArrayList<Barrier>();
solids = new ArrayList<GameObj> ();
// Create player 1
BufferedImage heroImage = ImageIO.read(new File("demoImage.png"));
Sprite heroSprite = new Sprite(heroImage,64,64,1);
player1 = new PhysGameHero(heroSprite,200,300,64,64);
player1.setXVel(0);
player1.setYVel(0);
solids.add(player1);
// Create NPCs
BufferedImage spriteSheetImage = ImageIO.read(new File("demoSprite.png"));
Sprite sprite = new Sprite(spriteSheetImage,64,64,4);
PhysGameNPC npc1 = new PhysGameNPC(sprite,80,330,64,64);
npcs.add(npc1);
solids.add(npc1);
// Create walls and platforms
addBarrier(0,0,30,400); // Left Wall
addBarrier(30,370,370,30); // Floor
addBarrier(370,0,400,400); // Right Wall
addBarrier(250,270,150,30); // Platform 1
addBarrier(0,190,150,30); // Platform 2
}
catch (Exception e) {
e.printStackTrace();
}
}
public void update () {
// Update the NPCs (position and sprite frame) and check for collisions
for (PhysGameNPC obj : npcs) {
obj.update(solids);
}
// Update player 1 and check for collisions
player1.update(solids);
}
public void display(Graphics2D g) {
// Display player 1
g.drawImage(player1.getSprite().getFrame(),
player1.getX() - player1.getWidth()/2,
player1.getY() - player1.getHeight()/2,
null);
// Display NPCs
for (PhysGameNPC obj : npcs) {
g.drawImage(obj.getSprite().getFrame(), obj.getX() - obj.getWidth()/2, obj.getY() - obj.getHeight()/2, null);
}
// Display barriers
g.setPaint(Color.black);
for (Barrier obj : barriers) {
g.fillRect( obj.getX() - obj.getWidth()/2, obj.getY() - obj.getHeight()/2, obj.getWidth(),obj.getHeight());
}
}
public void onLeftKeyPress() {
player1.setXVel(-6);
}
public void onRightKeyPress() {
player1.setXVel(6);
}
public void onSpaceKeyPress() {
if (player1.grounded) {
player1.setYVel(-16);
}
}
public void addBarrier(int topLeftX, int topLeftY, int width, int height) {
Barrier toAdd = new Barrier(topLeftX, topLeftY, width, height);
barriers.add(toAdd);
solids.add(toAdd);
}
public PhysGameHero getPlayer1() {return this.player1;}
public ArrayList<PhysGameNPC> getNPCs() {return this.npcs;}
public ArrayList<Barrier> getBarriers() {return this.barriers;}
public ArrayList<GameObj> getSolids() {return this.solids;}
}
- Import these: java.awt.Color, java.awt.Graphics2D, java.awt.image.BufferedImage, java.io.File, java.util.ArrayList, javax.imageio.ImageIO
Define a Panel to Connect the Controls and Display to the Game
public class PhysGamePanel extends JPanel implements ActionListener {
PhysicsGame game;
public PhysGamePanel(PhysicsGame game) {
this.game = game;
try {
this.setBackground(Color.blue);
Timer timer = new Timer (50, this);
timer.start();
}
catch (Exception e) {
e.printStackTrace();
}
PressRightAction pressRightAction = new PressRightAction();
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0, false), "pressRight");
this.getActionMap().put("pressRight", pressRightAction );
PressLeftAction pressLeftAction = new PressLeftAction();
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0, false), "pressLeft");
this.getActionMap().put("pressLeft", pressLeftAction );
PressSpaceAction pressSpaceAction = new PressSpaceAction();
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0, false), "pressSpace");
this.getActionMap().put("pressSpace", pressSpaceAction );
}
@Override
public void actionPerformed (ActionEvent event) {
game.update();
repaint();
}
public void paint (Graphics gra) {
Graphics2D g = (Graphics2D) gra;
super.paint(g);
game.display(g);
}
public class PressRightAction extends AbstractAction {
@Override
public void actionPerformed (ActionEvent ae) {
game.onRightKeyPress();
}
}
public class PressLeftAction extends AbstractAction {
@Override
public void actionPerformed (ActionEvent ae) {
game.onLeftKeyPress();
}
}
public class PressSpaceAction extends AbstractAction {
@Override
public void actionPerformed (ActionEvent ae) {
game.onSpaceKeyPress();
}
}
}
- Import these from java.awt: Color, Graphics, Graphics2D, event.ActionEvent, event.ActionListener, event.KeyEvent
- Import these from javax.swing: AbstractAction, JPanel, KeyStroke, Timer