Create a Java Shooting Game from Scratch – Full OOP Source Code Explained
This article shares a complete Java shooting game example, demonstrating object‑oriented programming concepts such as inheritance, interfaces, and event handling, and provides clear, commented source code for classes like Airplane, Bee, Bullet, Hero, and the main game loop.
Introduction
Technology originates from sharing, so I took some time to organize a simple shooting game I previously built with Java for reference and learning. Java is not ideal for desktop applications, but this game serves to illustrate the OOP (object‑oriented programming) process in an entertaining way. The code is straightforward, well‑commented, and easy to understand; any remaining questions can be explored through further study.
Full Code
Enemy Airplane
import java.util.Random;
// Enemy airplane: a flying object and also an enemy
public class Airplane extends FlyingObject implements Enemy {
private int speed = 3; // movement step
/** Initialize data */
public Airplane() {
this.image = ShootGame.airplane;
width = image.getWidth();
height = image.getHeight();
y = -height;
Random rand = new Random();
x = rand.nextInt(ShootGame.WIDTH - width);
}
/** Get score */
@Override
public int getScore() {
return 5;
}
/** Out‑of‑bounds handling */
@Override
public boolean outOfBounds() {
return y > ShootGame.HEIGHT;
}
/** Move */
@Override
public void step() {
y += speed;
}
}Score Reward Interface
/**
* Reward
*/
public interface Award {
int DOUBLE_FIRE = 0; // double firepower
int LIFE = 1; // extra life
/** Get reward type (0 or 1) */
int getType();
}Bee (Reward Object)
import java.util.Random;
/** Bee */
public class Bee extends FlyingObject implements Award {
private int xSpeed = 1; // horizontal speed
private int ySpeed = 2; // vertical speed
private int awardType; // reward type
/** Initialize data */
public Bee() {
this.image = ShootGame.bee;
width = image.getWidth();
height = image.getHeight();
y = -height;
Random rand = new Random();
x = rand.nextInt(ShootGame.WIDTH - width);
awardType = rand.nextInt(2); // random reward
}
/** Get reward type */
public int getType() {
return awardType;
}
/** Out‑of‑bounds handling */
@Override
public boolean outOfBounds() {
return y > ShootGame.HEIGHT;
}
/** Move (can fly diagonally) */
@Override
public void step() {
x += xSpeed;
y += ySpeed;
if (x > ShootGame.WIDTH - width) {
xSpeed = -1;
}
if (x < 0) {
xSpeed = 1;
}
}
}Bullet (Flying Object)
/**
* Bullet: a flying object
*/
public class Bullet extends FlyingObject {
private int speed = 3; // movement speed
/** Initialize data */
public Bullet(int x, int y) {
this.x = x;
this.y = y;
this.image = ShootGame.bullet;
}
/** Move */
@Override
public void step() {
y -= speed;
}
/** Out‑of‑bounds handling */
@Override
public boolean outOfBounds() {
return y < -height;
}
}Enemy Interface (Score)
/**
* Enemy can have a score
*/
public interface Enemy {
/** Enemy score */
int getScore();
}FlyingObject (Base Class)
import java.awt.image.BufferedImage;
/**
* FlyingObject (enemy plane, bee, bullet, hero)
*/
public abstract class FlyingObject {
protected int x; // x coordinate
protected int y; // y coordinate
protected int width; // width
protected int height; // height
protected BufferedImage image; // image
public int getX() { return x; }
public void setX(int x) { this.x = x; }
public int getY() { return y; }
public void setY(int y) { this.y = y; }
public int getWidth() { return width; }
public void setWidth(int width) { this.width = width; }
public int getHeight() { return height; }
public void setHeight(int height) { this.height = height; }
public BufferedImage getImage() { return image; }
public void setImage(BufferedImage image) { this.image = image; }
/** Check if out of bounds */
public abstract boolean outOfBounds();
/** Move one step */
public abstract void step();
/** Determine if this object is hit by a bullet */
public boolean shootBy(Bullet bullet) {
int bx = bullet.x;
int by = bullet.y;
return this.x < bx && bx < this.x + width && this.y < by && by < this.y + height;
}
}Hero (Player Plane)
import java.awt.image.BufferedImage;
/**
* Hero plane: a flying object
*/
public class Hero extends FlyingObject {
private BufferedImage[] images = {};
private int index = 0; // image switch index
private int doubleFire; // double firepower
private int life; // lives
/** Initialize data */
public Hero() {
life = 3; // initial lives
doubleFire = 0; // initial firepower
images = new BufferedImage[]{ShootGame.hero0, ShootGame.hero1};
image = ShootGame.hero0;
width = image.getWidth();
height = image.getHeight();
x = 150;
y = 400;
}
public int isDoubleFire() { return doubleFire; }
public void setDoubleFire(int doubleFire) { this.doubleFire = doubleFire; }
public void addDoubleFire() { doubleFire = 40; }
public void addLife() { life++; }
public void subtractLife() { life--; }
public int getLife() { return life; }
/** Move to mouse position */
public void moveTo(int x, int y) {
this.x = x - width / 2;
this.y = y - height / 2;
}
@Override
public boolean outOfBounds() { return false; }
/** Shoot bullets */
public Bullet[] shoot() {
int xStep = width / 4;
int yStep = 20;
if (doubleFire > 0) {
Bullet[] bullets = new Bullet[2];
bullets[0] = new Bullet(x + xStep, y - yStep);
bullets[1] = new Bullet(x + 3 * xStep, y - yStep);
return bullets;
} else {
Bullet[] bullets = new Bullet[1];
bullets[0] = new Bullet(x + 2 * xStep, y - yStep);
return bullets;
}
}
@Override
public void step() {
if (images.length > 0) {
image = images[index++ / 10 % images.length]; // switch hero image
}
}
/** Collision detection with another flying object */
public boolean hit(FlyingObject other) {
int x1 = other.x - this.width / 2;
int x2 = other.x + this.width / 2 + other.width;
int y1 = other.y - this.height / 2;
int y2 = other.y + this.height / 2 + other.height;
int heroX = this.x + this.width / 2;
int heroY = this.y + this.height / 2;
return heroX > x1 && heroX < x2 && heroY > y1 && heroY < y2;
}
}Game Main Class (ShootGame)
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ShootGame extends JPanel {
public static final int WIDTH = 400;
public static final int HEIGHT = 654;
private int state;
private static final int START = 0;
private static final int RUNNING = 1;
private static final int PAUSE = 2;
private static final int GAME_OVER = 3;
private int score = 0;
private Timer timer;
private int interval = 1000 / 100;
public static BufferedImage background, start, airplane, bee, bullet, hero0, hero1, pause, gameover;
private FlyingObject[] flyings = {};
private Bullet[] bullets = {};
private Hero hero = new Hero();
static {
try {
background = ImageIO.read(ShootGame.class.getResource("background.png"));
start = ImageIO.read(ShootGame.class.getResource("start.png"));
airplane = ImageIO.read(ShootGame.class.getResource("airplane.png"));
bee = ImageIO.read(ShootGame.class.getResource("bee.png"));
bullet = ImageIO.read(ShootGame.class.getResource("bullet.png"));
hero0 = ImageIO.read(ShootGame.class.getResource("hero0.png"));
hero1 = ImageIO.read(ShootGame.class.getResource("hero1.png"));
pause = ImageIO.read(ShootGame.class.getResource("pause.png"));
gameover = ImageIO.read(ShootGame.class.getResource("gameover.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void paint(Graphics g) {
g.drawImage(background, 0, 0, null);
paintHero(g);
paintBullets(g);
paintFlyingObjects(g);
paintScore(g);
paintState(g);
}
public void paintHero(Graphics g) { g.drawImage(hero.getImage(), hero.getX(), hero.getY(), null); }
public void paintBullets(Graphics g) { for (Bullet b : bullets) g.drawImage(b.getImage(), b.getX() - b.getWidth() / 2, b.getY(), null); }
public void paintFlyingObjects(Graphics g) { for (FlyingObject f : flyings) g.drawImage(f.getImage(), f.getX(), f.getY(), null); }
public void paintScore(Graphics g) {
g.setColor(new Color(0xFF0000));
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 22));
g.drawString("SCORE:" + score, 10, 25);
g.drawString("LIFE:" + hero.getLife(), 10, 45);
}
public void paintState(Graphics g) {
switch (state) {
case START: g.drawImage(start, 0, 0, null); break;
case PAUSE: g.drawImage(pause, 0, 0, null); break;
case GAME_OVER: g.drawImage(gameover, 0, 0, null); break;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Fly");
ShootGame game = new ShootGame();
frame.add(game);
frame.setSize(WIDTH, HEIGHT);
frame.setAlwaysOnTop(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setIconImage(new ImageIcon("images/icon.jpg").getImage());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.action();
}
public void action() {
MouseAdapter mouseAdapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
if (state == RUNNING) hero.moveTo(e.getX(), e.getY());
}
@Override
public void mouseEntered(MouseEvent e) { if (state == PAUSE) state = RUNNING; }
@Override
public void mouseExited(MouseEvent e) { if (state == RUNNING) state = PAUSE; }
@Override
public void mouseClicked(MouseEvent e) {
switch (state) {
case START: state = RUNNING; break;
case GAME_OVER:
flyings = new FlyingObject[0];
bullets = new Bullet[0];
hero = new Hero();
score = 0;
state = START;
break;
}
}
};
this.addMouseListener(mouseAdapter);
this.addMouseMotionListener(mouseAdapter);
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (state == RUNNING) {
enterAction();
stepAction();
shootAction();
bangAction();
outOfBoundsAction();
checkGameOverAction();
}
repaint();
}
}, interval, interval);
}
private int flyEnteredIndex = 0;
public void enterAction() {
flyEnteredIndex++;
if (flyEnteredIndex % 40 == 0) {
FlyingObject obj = nextOne();
flyings = Arrays.copyOf(flyings, flyings.length + 1);
flyings[flyings.length - 1] = obj;
}
}
public void stepAction() {
for (FlyingObject f : flyings) f.step();
for (Bullet b : bullets) b.step();
hero.step();
}
private int shootIndex = 0;
public void shootAction() {
shootIndex++;
if (shootIndex % 30 == 0) {
Bullet[] bs = hero.shoot();
bullets = Arrays.copyOf(bullets, bullets.length + bs.length);
System.arraycopy(bs, 0, bullets, bullets.length - bs.length, bs.length);
}
}
public void bangAction() {
for (Bullet b : bullets) bang(b);
}
public void outOfBoundsAction() {
int idx = 0;
FlyingObject[] aliveFlyings = new FlyingObject[flyings.length];
for (FlyingObject f : flyings) if (!f.outOfBounds()) aliveFlyings[idx++] = f;
flyings = Arrays.copyOf(aliveFlyings, idx);
idx = 0;
Bullet[] aliveBullets = new Bullet[bullets.length];
for (Bullet b : bullets) if (!b.outOfBounds()) aliveBullets[idx++] = b;
bullets = Arrays.copyOf(aliveBullets, idx);
}
public void checkGameOverAction() { if (isGameOver()) state = GAME_OVER; }
public boolean isGameOver() {
for (FlyingObject f : flyings) {
if (hero.hit(f)) {
hero.subtractLife();
hero.setDoubleFire(0);
// remove collided flying object
// (simplified removal for brevity)
return hero.getLife() <= 0;
}
}
return false;
}
public void bang(Bullet bullet) {
int hitIdx = -1;
for (int i = 0; i < flyings.length; i++) {
if (flyings[i].shootBy(bullet)) { hitIdx = i; break; }
}
if (hitIdx != -1) {
FlyingObject hitObj = flyings[hitIdx];
// swap with last and shrink array
FlyingObject temp = flyings[hitIdx];
flyings[hitIdx] = flyings[flyings.length - 1];
flyings[flyings.length - 1] = temp;
flyings = Arrays.copyOf(flyings, flyings.length - 1);
if (hitObj instanceof Enemy) {
Enemy e = (Enemy) hitObj;
score += e.getScore();
} else if (hitObj instanceof Award) {
Award a = (Award) hitObj;
switch (a.getType()) {
case Award.DOUBLE_FIRE: hero.addDoubleFire(); break;
case Award.LIFE: hero.addLife(); break;
}
}
}
}
public static FlyingObject nextOne() {
Random random = new Random();
int type = random.nextInt(20);
if (type < 4) return new Bee();
else return new Airplane();
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
