for Proce55ing
PongPod - Source code

PongPod.java

/**
 * PongPod.<br>
 * Play a pong with a wheel.<br><br>
 *
 * Copyright (C) 2005 Kenta Cho. Some rights reserved.
 * 
 * @p5 description
 * - 画面下の灰色の丸(ホイール)にカーソルを合わせてください
 * - クリックするとゲームが始まります
 * - ホイールに沿って丸くカーソルを動かすとラケットが動きます
 * - ラケットでボールをはじき返してください
 * - ボールが中央の黒丸に入るとミスです
 * - ミス3つでゲームオーバー
 */
//import processing.core.*;

//public class PongPod extends PApplet {
public class PongPod extends BApplet {
  Field field;
  Wheel wheel;
  Racket racket;
  Ball[] ball = new Ball[25];
  //PFont font;
  BFont font;
  int TITLE = 0;
  int IN_GAME = 1;
  int GAMEOVER = 2;
  int state;
  Title title;
  Gameover gameover;
  int score;
  Se se;

  public void setup() {
    size(340, 550);
    framerate(30);
    cursor(ARROW);
    noStroke();
    //
    ellipseMode(CENTER_DIAMETER);
    //font = loadFont("Verdana-48.vlw");
    font = loadFont("Futura-BoldCon.vlw.gz");
    textFont(font);
    field = new Field();
    field.init();
    wheel = new Wheel();
    wheel.init();
    racket = new Racket();
    racket.init();
    for (int i = 0; i < ball.length; i++)
      ball[i] = new Ball();
    title = new Title();
    gameover = new Gameover();
    se = new Se();
    se.load();
    startTitle();
  }

  void startTitle() {
    state = TITLE;
    title.start();
  }

  void startInGame() {
    state = IN_GAME;
    score = 0;
    se.init();
    initBalls();
    field.start();
    racket.start();
  }

  void startGameover() {
    state = GAMEOVER;
    gameover.start();
  }

  //public void draw() {
  public void loop() {
    background(250, 250, 250);
    smooth();
    switch (state) {
      case 0 : // TITLE
        field.draw();
        wheel.draw();
        wheel.move();
        title.draw();
        break;
      case 1 : // IN_GAME
        field.draw();
        wheel.draw();
        field.move();
        wheel.move();
        moveBalls();
        racket.move();
        racket.draw();
        drawBalls();
        drawStatus();
        se.playAllSes();
        break;
      case 2 : // GAMEOVER
        field.draw();
        wheel.draw();
        wheel.move();
        gameover.draw();
        drawStatus();
        break;
    }
  }

  void drawStatus() {
    textSize(26);
    //textAlign(RIGHT);
    textMode(ALIGN_RIGHT);
    fill(255, 255, 255);
    text(score, width - 14, 35);
  }

  float adjustDeg(float d) {
    if (d > PI)
      return d - PI * 2;
    else if (d < -PI)
      return d + PI * 2;
    else
      return d;
  }

  /**
   * 2D vector.
   */
  class Vector {
    float x, y;
  }

  /**
   * My racket.
   */
  class Racket {
    float SENSITIVITY = 0.7f;
    float radius;
    float deg;
    Vector[] pos;
    Vector p1, p2;
    float rWidth, rHeight;

    void init() {
      radius = field.bwRadius * 1.5f;
      pos = new Vector[2];
      for (int i = 0; i < pos.length; i++)
        pos[i] = new Vector();
      p1 = new Vector();
      p2 = new Vector();
      rWidth = 70;
      rHeight = 7;
    }

    void start() {
      deg = 0;
    }

    void move() {
      deg -= wheel.angVel * SENSITIVITY;
      deg = adjustDeg(deg);
      float d = deg;
      for (int i = 0; i < pos.length; i++) {
        pos[i].x = field.centerPos.x - sin(d) * radius;
        pos[i].y = field.centerPos.y + cos(d) * radius;
        p1.x = pos[i].x - sin(d + HALF_PI) * rWidth / 2;
        p1.y = pos[i].y + cos(d + HALF_PI) * rWidth / 2;
        p2.x = pos[i].x - sin(d - HALF_PI) * rWidth / 2;
        p2.y = pos[i].y + cos(d - HALF_PI) * rWidth / 2;
        for (int j = 0; j < ball.length; j++)
          if (ball[j].isExist)
            if (ball[j].checkHit(p1, p2))
              ball[j].reflectWithRacket(d + HALF_PI);
        d += PI;
      }
    }

    void draw() {
      fill(0, 0, 0);
      //rectMode(CENTER);
      rectMode(CENTER_DIAMETER);
      float d = deg;
      for (int i = 0; i < pos.length; i++) {
        //pushMatrix();
        push();
        translate(pos[i].x + field.pos.x, pos[i].y + field.pos.y);
        rotate(d);
        rect(0, 0, rWidth, rHeight);
        //popMatrix();
        pop();
        d += PI;
      }
      rectMode(CORNER);
    }
  }

  /**
   * Balls.
   */
  class Ball {
    Vector pos = new Vector();
    Vector ppos = new Vector();
    float deg;
    float speed;
    float size, trgSize;
    boolean isExist = false;
    int reflectCnt;
    int idx;

    void set(float x, float y, float d, float sp, float sz) {
      pos.x = x;
      pos.y = y;
      deg = adjustDeg(d);
      speed = sp;
      trgSize = sz;
      size = 0.1f;
      reflectCnt = 0;
      isExist = true;
    }

    void move() {
      ppos.x = pos.x;
      ppos.y = pos.y;
      pos.x -= sin(deg) * speed;
      pos.y += cos(deg) * speed;
      size += (trgSize - size) * 0.1f;
      if (pos.x < size || pos.x >= field.size.x - size)
        reflect(0);
      if (pos.y < size || pos.y >= field.size.y - size)
        reflect(HALF_PI);
      if (reflectCnt > 0)
        reflectCnt--;
      if (dist(pos.x, pos.y, field.centerPos.x, field.centerPos.y)
        < field.bwRadius * 0.75f + size) {
        field.incOutCnt();
        isExist = false;
        return;
      }
    }

    void reflect(float d) {
      float aod1 = abs(adjustDeg(deg - d));
      float aod2 = abs(adjustDeg(deg - (d + PI)));
      float od, aod;
      if (aod1 < aod2) {
        od = adjustDeg(deg - d);
        aod = aod1;
      } else {
        od = adjustDeg(deg - (d + PI));
        aod = aod2;
      }
      if (od < 0)
        deg += aod * 2;
      else
        deg -= aod * 2;
      pos.x = ppos.x;
      pos.y = ppos.y;
      pos.x -= sin(deg) * speed;
      pos.y += cos(deg) * speed;
      se.playSe(idx % 7);
    }

    void reflectWithRacket(float d) {
      if (reflectCnt <= 0) {
        reflect(d);
        se.playSe(7);
        score++;
      }
      reflectCnt = 3;
    }

    boolean checkHit(Vector p, Vector pp) {
      float bmvx, bmvy, inaa;
      bmvx = pp.x;
      bmvy = pp.y;
      bmvx -= p.x;
      bmvy -= p.y;
      inaa = bmvx * bmvx + bmvy * bmvy;
      if (inaa > 0.00001) {
        float sofsx, sofsy, inab, hd;
        sofsx = pos.x;
        sofsy = pos.y;
        sofsx -= p.x;
        sofsy -= p.y;
        inab = bmvx * sofsx + bmvy * sofsy;
        if (inab >= 0 && inab <= inaa) {
          hd = sofsx * sofsx + sofsy * sofsy - inab * inab / inaa;
          if (hd >= 0 && hd <= size * 0.7f + 7.0f) {
            return true;
          }
        }
      }
      return false;
    }

    void draw() {
      fill(255, 255, 255);
      ellipse(
        field.pos.x + pos.x,
        field.pos.y + pos.y,
        size * 2,
        size * 2);
    }
  }

  void initBalls() {
    for (int i = 0; i < ball.length; i++) {
      ball[i].idx = i;
      ball[i].isExist = false;
    }
    ballIdx = 0;
  }
  void moveBalls() {
    for (int i = 0; i < ball.length; i++)
      if (ball[i].isExist)
        ball[i].move();
  }
  void drawBalls() {
    for (int i = 0; i < ball.length; i++)
      if (ball[i].isExist)
        ball[i].draw();
  }
  int ballIdx = 0;
  Ball getBallInstance() {
    for (int i = 0; i < ball.length; i++) {
      if (!ball[ballIdx].isExist)
        return ball[ballIdx];
      ballIdx++;
      if (ballIdx >= ball.length)
        ballIdx = 0;
    }
    return null;
  }

  /**
   * Game field (main screen).
   */
  class Field {
    Vector pos = new Vector();
    Vector size = new Vector();
    float bwRadius;
    Vector centerPos = new Vector();
    float brightness;
    float bwSize;
    int cnt;
    int outCnt;

    void init() {
      size.x = 320;
      size.y = 240;
      pos.x = pos.y = (width - size.x) / 2;
      bwRadius = size.y * 0.1f;
      centerPos.x = size.x / 2;
      centerPos.y = size.y / 2;
      brightness = 0;
      bwSize = 1.0f;
    }

    void start() {
      bwSize = 1;
      cnt = 0;
      outCnt = 0;
      for (int i = 0; i < 3; i++)
        addBall();
    }

    void addBall() {
      Ball b = getBallInstance();
      if (b == null)
        return;
      float x, y;
      if (random(2) >= 1) {
        x = random(field.size.x - 40) + 20;
        if (random(2) >= 1)
          y = 20;
        else
          y = field.size.y - 20;
      } else {
        y = random(field.size.y - 40) + 20;
        if (random(2) >= 1)
          x = 20;
        else
          x = field.size.x - 20;
      }
      b.set(x, y, random(TWO_PI), 1.5f + random(1.0f), 4 + random(3.0f));
    }

    void incOutCnt() {
      se.playSe(8);
      outCnt++;
      if (outCnt >= 3)
        startGameover();
    }

    void move() {
      cnt++;
      if (cnt % 300 == 0)
        addBall();
    }

    void draw() {
      fill(80 * brightness, 200 * brightness, 80 * brightness);
      rect(pos.x, pos.y, size.x, size.y);
      fill(0, 0, 0);
      ellipse(
        pos.x + size.x / 2,
        pos.y + size.y / 2,
        bwRadius * 2 * bwSize,
        bwRadius * 2 * bwSize);
      if (state == IN_GAME) {
        fill(255, 255, 255);
        for (int i = 0; i < outCnt; i++)
          ellipse(
            pos.x + size.x / 2 - 10 + i * 10,
            pos.y + size.y / 2,
            10,
            10);
      }
    }
  }

  /**
   * Wheel that controls a racket.
   */
  class Wheel {
    Vector centerPos;
    float radFrom, radTo;
    boolean cursorOn;
    float angVel;
    float pdeg;

    void init() {
      centerPos = new Vector();
      centerPos.x = width / 2;
      centerPos.y = height * 0.75f;
      radFrom = width * 0.35f / 2;
      radTo = width * 0.7f / 2;
      cursorOn = false;
      angVel = 0;
      pdeg = 0;
    }

    void move() {
      float d = dist(mouseX, mouseY, centerPos.x, centerPos.y);
      if (d >= radFrom * 0.05f && d <= radTo * 1.4f) {
        if (!cursorOn) {
          cursorOn = true;
          cursor(HAND);
          pdeg = atan2(mouseX - centerPos.x, mouseY - centerPos.y);
        }
      } else {
        if (cursorOn) {
          cursorOn = false;
          cursor(ARROW);
        }
      }
      if (cursorOn) {
        float deg = atan2(mouseX - centerPos.x, mouseY - centerPos.y);
        angVel = adjustDeg(deg - pdeg);
        pdeg = deg;
      } else {
        angVel = 0;
      }
    }

    void draw() {
      fill(210, 210, 210);
      ellipse(centerPos.x, centerPos.y, radTo * 2, radTo * 2);
      fill(255, 255, 255);
      ellipse(centerPos.x, centerPos.y, radFrom * 2, radFrom * 2);
    }
  }

  /**
   * Title screen.
   */
  class Title {
    int cnt;

    void start() {
      cnt = 0;
      field.brightness = 0;
      field.bwSize = 0;
    }

    void draw() {
      cnt++;
      if (cnt > 30 && mousePressed && wheel.cursorOn) {
        startInGame();
        return;
      }
      if (cnt <= 30)
        field.brightness = cnt * (1.0f / 30);
      if (cnt > 45) {
        textSize(42);
        //textAlign(CENTER);
        textMode(ALIGN_CENTER);
        fill(255, 255, 255);
        text(
          "PongPod",
          field.pos.x + field.size.x / 2,
          field.pos.y + field.size.y / 2);
      }
    }
  }

  /**
   * Gameover screen.
   */
  class Gameover {
    int cnt;

    void start() {
      cnt = 0;
    }

    void draw() {
      cnt++;
      if (cnt > 300 || (cnt > 30 && mousePressed && wheel.cursorOn)) {
        startTitle();
        return;
      }
      if (cnt <= 30) {
        field.bwSize = 1.0f + cnt * 0.1f;
        field.brightness = 1.0f - cnt * (1.0f / 30);
      }
      if (cnt > 45) {
        textSize(32);
        //textAlign(CENTER);
        textMode(ALIGN_CENTER);
        fill(255, 255, 255);
        text(
          "GameOver",
          field.pos.x + field.size.x / 2,
          field.pos.y + field.size.y / 2);
      }
    }
  }

  /**
   * Sound effect.
   */
  class Se {
    int SE_NUM = 9;
    //PSound[] se;
    BSound[] se;
    boolean[] play;
    int cnt;
    int interval = 7;

    void load() {
      //se = new PSound[SE_NUM];
      se = new BSound[SE_NUM];
      play = new boolean[SE_NUM];
      for (int i = 0; i < SE_NUM; i++)
        se[i] = loadSound("se0" + (i + 1) + ".wav");
    }

    void init() {
      for (int i = 0; i < SE_NUM; i++)
        play[i] = false;
      cnt = 0;
    }

    void playSe(int n) {
      play[n] = true;
    }

    void playAllSes() {
      cnt++;
      int ci = cnt % interval;
      if (ci == interval - 1) {
        for (int i = 0; i < SE_NUM; i++) {
          if (play[i])
            //se[i].stop();
            stop(se[i]);
        }
      }
      if (ci == 0) {
        for (int i = 0; i < SE_NUM; i++) {
          if (play[i]) {
            //se[i].play();
            play(se[i]);
            play[i] = false;
          }
        }
      }
    }
  }
}