Game Development 20 min read

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.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Create a Java Shooting Game from Scratch – Full OOP Source Code Explained

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

Game screenshot
Game screenshot
Game screenshot
Game screenshot

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();
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Game Developmentobject‑oriented programming
Java Backend Technology
Written by

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!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.