final int HALF_WIDTH = WIDTH / 2;
final int HALF_HEIGHT = HEIGHT / 2;
final int BALL_RADIUS = 20;
final int PAD_WIDTH = 10;
final int PAD_HEIGHT = 100;
final int HALF_PAD_WIDTH = PAD_WIDTH / 2;
final int HALF_PAD_HEIGHT = PAD_HEIGHT / 2;
final float PAD_VEL_HUM = 5;
final float PAD_VEL_CPU = 4.5;
final float BALL_ACC_X = 1.08;
final float BALL_ACC_Y = 1.04;
final int MODE_SELECT = 1;
PFont fontCaption, fontButton, fontNote, fontScore;
color colorBackground, buttonColor, buttonHColor;
Button(String label_, int x_, int y_, int w_, int h_)
x = x_; y = y_; w = w_; h = h_;
return mouseX >= x && mouseX <= x + w &&
mouseY >= y && mouseY <= y + h;
fill(isMouseOver() ? buttonHColor : buttonColor);
text(label, x + w / 2, y + h / 2);
velx = random(120, 240) / 60;
vely = random(60, 180) / 60;
if (random(2) == 0) vely = -vely;
velx = -velx * BALL_ACC_X;
vely = vely * BALL_ACC_Y;
if (posy <= BALL_RADIUS) {
if (posy >= HEIGHT - BALL_RADIUS) {
posy = HEIGHT - BALL_RADIUS;
ellipse(posx, posy, 2 * BALL_RADIUS, 2 * BALL_RADIUS);
void reset(boolean isHuman_)
if (ball.posy >= posy - HALF_PAD_HEIGHT && ball.posy <= posy + HALF_PAD_HEIGHT) {
float direct = (posx - ball.posx > 0) ? 1 : -1;
if ((direct > 0 && ball.velx > 0) || (direct < 0 && ball.velx < 0)) {
float h = HEIGHT - 2 * BALL_RADIUS;
float dist = abs(posx - ball.posx) - BALL_RADIUS - HALF_PAD_WIDTH;
float approx = abs(ball.posy - BALL_RADIUS + direct * dist * ball.vely / ball.velx);
if (int(approx / h) % 2 == 1) {
vel = getAdjustedVel(posy, pos, HALF_PAD_HEIGHT / 2, PAD_VEL_CPU);
float getAdjustedVel(float cur, float dest, float adj, float vel)
if (cur <= dest - adj) return vel;
else if (cur >= dest + adj) return -vel;
if (posy < HALF_PAD_HEIGHT) posy = HALF_PAD_HEIGHT;
if (posy > HEIGHT - HALF_PAD_HEIGHT) posy = HEIGHT - HALF_PAD_HEIGHT;
line(posx, posy - HALF_PAD_HEIGHT, posx, posy + HALF_PAD_HEIGHT);
PImage [] loadImages(String stub, String extension, int numImages)
PImage [] images = new PImage[0];
for(int i = 0; i < numImages; ++i) {
PImage img = loadImage(stub + i + extension);
images = (PImage [])append(images,img);
colorBackground = color(0, 128, 64);
background(colorBackground);
textAlign(CENTER, CENTER);
images = loadImages("kittens", ".jpg", 11);
fontButton = createFont("Arial", 28, true);
fontNote = createFont("Arial", 22, true);
fontScore = createFont("Arial", 64, true);
buttonColor = color(196, 128, 0);
buttonHColor = color(255, 164, 0);
int x = HALF_WIDTH - w / 2;
new Button("human vs human", x, 2 * y, w, h),
new Button("human vs cpu", x, 3 * y, w, h),
new Button("cpu vs human", x, 4 * y, w, h),
new Button("cpu vs cpu", x, 5 * y, w, h),
new Button("sound: off", x, 6 * y, w, h)
paddle1 = new Paddle(HALF_PAD_WIDTH);
paddle2 = new Paddle(WIDTH - HALF_PAD_WIDTH);
if (ball.posx - BALL_RADIUS <= PAD_WIDTH) {
if (!paddle1.isHit(ball)) {
ball.posx = PAD_WIDTH + BALL_RADIUS;
if (ball.posx + BALL_RADIUS >= WIDTH - PAD_WIDTH) {
if (!paddle2.isHit(ball)) {
ball.posx = WIDTH - PAD_WIDTH - BALL_RADIUS;
fill(colorBackground, 80);
rect(0, 0, width, height);
image(images[num], HALF_WIDTH, HALF_HEIGHT);
if (time == 0) num = (num + 1) % 11;
text("click to play", HALF_WIDTH, HEIGHT - 40);
text("Ping-Pong", HALF_WIDTH, 50);
for (int i = 0; i < buttons.length; ++i) {
text("player1 constrols: W S, player2 controls: Up Down, Space - exit", HALF_WIDTH, HEIGHT - 40);
line(HALF_WIDTH, 0, HALF_WIDTH, HEIGHT);
line(PAD_WIDTH, 0, PAD_WIDTH, HEIGHT);
line(WIDTH - PAD_WIDTH, 0, WIDTH - PAD_WIDTH, HEIGHT);
text(score1, width / 4, height / 4);
text(score2, 3 * width / 4, height / 4);
text(paddle1.isHuman ? "player1: W S" : "cpu", width / 4, height - 50);
text(paddle2.isHuman ? "player2: Up Down" : "cpu", 3 * width / 4, height - 50);
for (k = 0; k < buttons.length; ++k) {
if (buttons[k].isMouseOver()) break;
boolean player1, player2;
case 0: player1 = true; player2 = true; break;
case 1: player1 = true; player2 = false; break;
case 2: player1 = false; player2 = true; break;
case 3: player1 = false; player2 = false; break;
case 4: soundOn = !soundOn; buttons[k].label = "sound: " + (soundOn ? "broken" : "off"); return;
ball.init(random(2) == 0);
if (key == 'W' || key == 'w') paddle1.setVel(-PAD_VEL_HUM);
else if (key == 'S' || key == 's') paddle1.setVel(PAD_VEL_HUM);
else if (keyCode == UP) paddle2.setVel(-PAD_VEL_HUM);
else if (keyCode == DOWN) paddle2.setVel(PAD_VEL_HUM);
if (key == 'W' || key == 'w' || key == 'S' || key == 's') paddle1.setVel(0);
else if (keyCode == UP || keyCode == DOWN) paddle2.setVel(0);
else if (key == ' ') mode = MODE_SELECT;