Here you will find the source code for the pirate animation, as described in the upcoming book Graphic Guide to Java

Copyright 2025 Antony Lees

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Main Programme

private ArrayList<Ship> ships;
private static final int NUMBER_OF_SHIPS = 6;
private static final int NUMBER_OF_PIRATE_SHIPS = 3;
private Waves waves;

void setup() {
size(600, 400);
ships = new ArrayList();
// civilian ships
for (int i = 0; i < NUMBER_OF_SHIPS; i++) {
ships.add(new Ship());
}
// pirate ships
for (int i = 0; i < NUMBER_OF_PIRATE_SHIPS; i++) {
ships.add(new PirateShip());
}
ships.add(new PirateCaptainShip());
waves = new Waves();
}

void draw() {
// light blue background
background(102, 205, 170);
// waves
waves.drawWaves();
// draw ships
for (Ship ship : ships) {
ship.display();
ship.move();
}
}

Ship class

class Ship {
// ship variables
protected int xPosition;
protected int yPosition;
protected int shipLength;
private int sails;
protected int sailColour;
private int sailSpacing;
private int speed;
protected int flagColour;
protected int flagLogoColour;
private boolean goLeft;

public Ship() {
this.xPosition = (int) random(600, 700);
this.yPosition = (int) random(50, 350);
this.shipLength = (int) random(40, 80);
// sails
this.sails = int(min((this.shipLength/30), 3));
this.sailSpacing = floor(this.shipLength/3);
// speed where smaller = faster
this.speed = 150 / this.shipLength;
this.flagColour = color(255); // white
this.flagLogoColour = color(255, 0, 0); // red
this.sailColour = color(random(255), random(255), random(255));
this.goLeft = true;
}

public void display() {
drawHull();
drawSails();
}

private void drawHull() {
// calculate ship coordinates
int frontOfShipXPosition = this.xPosition - (this.shipLength / 2);
int backOfShipXPosition = this.xPosition + this.shipLength + (this.shipLength / 3);
// ship hull
// brown lines and fill
stroke(139, 71, 38);
fill(139, 71, 38);
// middle rectangle
rect(this.xPosition, this.yPosition, this.shipLength, 20);
// front and back
triangle(frontOfShipXPosition, this.yPosition, this.xPosition, this.yPosition, this.xPosition, this.yPosition + 20);
triangle(this.xPosition + this.shipLength, this.yPosition, backOfShipXPosition, this.yPosition, this.xPosition + this.shipLength, this.yPosition + 20);
}

private void drawSails() {
// draw the sails
int initialSailXPosition = this.xPosition + 15;
for (int i = 0; i < this.sails; i++) {
int spacing = initialSailXPosition + (this.sailSpacing * i);
// black lines
stroke(0);
// sail
fill(this.sailColour);
quad(spacing, this.yPosition - 40, spacing + 10, this.yPosition - 40, spacing + 20, this.yPosition - 10, spacing, this.yPosition - 10);
// mast
fill(0);
line(spacing, this.yPosition - 10, spacing, this.yPosition);
line(spacing, this.yPosition - 40, spacing, this.yPosition - 50);
drawFlag(spacing);
// flag
fill(this.flagColour);
stroke(this.flagColour);
rect(spacing, this.yPosition - 50, 10, 5);
stroke(this.flagLogoColour);
line(spacing + 2, this.yPosition - 49, spacing + 8, this.yPosition - 46);
line(spacing + 8, this.yPosition - 49, spacing + 2, this.yPosition - 46);
}
}

private void drawFlag(int flagXPosition) {
fill(this.flagColour);
stroke(this.flagColour);
rect(flagXPosition, this.yPosition - 50, 10, 5);
stroke(this.flagLogoColour);
line(flagXPosition + 2, this.yPosition - 49, flagXPosition + 8, this.yPosition - 46);
line(flagXPosition + 8, this.yPosition - 49, flagXPosition + 2, this.yPosition - 46);
}

public void move() {
if (this.xPosition <= 0) {
this.goLeft = false;
}
if (this.xPosition >= width) {
this.goLeft = true;
}

if (goLeft) {
this.xPosition -= this.speed;
} else {
this.xPosition += this.speed;
}
}

}

PirateShip class

public class PirateShip extends Ship {

private int portholeCount = 0;

public PirateShip() {
this.portholeCount = int(this.shipLength/12);
// redefine flag colours
this.flagColour = color(0); // black
this.flagLogoColour = color(255); // white
}

@Override
public void display() {
super.display();
// draw the portholes
drawPortholes();
}

private void drawPortholes() {
stroke(0);
for (int i = 0; i < this.portholeCount; i++) {
// portholes
fill(0);
circle(this.xPosition+(15*i),this.yPosition+10,5);
}
}
}

PirateCaptainShip class

class PirateCaptainShip extends PirateShip {

public PirateCaptainShip() {
// override sail colours
this.sailColour = color(0); // black
// override flag colour
this.flagColour = color(255, 0, 0); // red
}
}

Waves class

public class Waves {

// wave height
static final int WAVE_MAX_HEIGHT = 20;
static final int WAVE_MIN_HEIGHT = 5;
int waveHeight = WAVE_MAX_HEIGHT;
// wave movement
static final int WAVE_UP = 1;
static final int WAVE_DOWN = -1;
int waveDirection = WAVE_UP;

public void drawWaves() {
noFill();
stroke(0);
// wave y coordinate
int waveY = WAVE_MAX_HEIGHT/2;
// while the y coordinate is less than the height of the window
while (waveY <= height) {
// wave x coordinate
int waveX = 0;
// while the x coordinate is less than the width of the window
while (waveX <= width) {
// draw a semi=circle arc
arc(waveX, waveY, WAVE_MAX_HEIGHT, this.waveHeight, 0, PI);
// increase the x coordinate by 20
waveX += WAVE_MAX_HEIGHT;
}
// increase the y coordinate by 20
waveY += WAVE_MAX_HEIGHT;
}
determineWaveDirection();
}

private void determineWaveDirection() {
// determine of the waves should be going up or down
if (this.waveHeight <= WAVE_MIN_HEIGHT) {
this.waveDirection = WAVE_UP;
}
if (this.waveHeight >= WAVE_MAX_HEIGHT) {
this.waveDirection = WAVE_DOWN;
}
// change the wave height for next time
this.waveHeight += this.waveDirection;
}
}