FlappyBird

フラッピーバードを作ろう

フラッピバードを知ろう

Play Flappy Bird
Play Flappy Bird for free online in HTML5

Processingをインストール

Welcome to Processing!
Processing is a flexible software sketchbook and a language for learning how to code. Since 2001, Processing has promoted software literacy within the visual ar...

上記サイトからダウンロード出来ます。
※寄付はしなくても大丈夫です!

テンプレートプログラムを実行してみよう

Processingの三角ボタンをクリックすると、ゲーム実行!

■ボタンで終了。

何も起こらない・・・・プレイヤーを表示しよう!

プレイヤーを表示するプログラムを追加しよう!

このプログラムをコピーして、 // 1. 鳥の画像を表示 の場所に貼り付けてみよう

image(birdImage, birdX, birdY);

こう出来たら、プログラムを実行し直してみよう!

スペースキーでゲームスタート

スペースキーを押すと、ゲームスタート!
鳥がジャンプして、土管が流れてくる。
上手くジャンプして土管の間をくぐり抜けてみよう!

ジャンプを調整してみよう

何だかジャンプが力強くて難しいですね・・・?
ジャンプする力と、落ちてくるスピードを調整して、土管を潜れる難易度にしてみよう!

12行目と13行目が、鳥が上に飛ぶ力と落ちる速さ。

それぞれ値を変更して、鳥の飛び方を調整してみよう。

土管の当たり判定を調整

土管と鳥の当たり判定が厳しいな・・・と思ったら、

プログラムの一番下あたりにある、この箇所を修正!

係数を 小さくすると、見た目よりも当たり判定を小さくすることが出来る。

土管を増やそう!

鳥の飛び方を調整して土管をくぐり抜けられるようになっても、土管の数が少なくてつまらないですね。
プログラムを書いて、流れてくる土管を増やしてみよう!

土管を作っているのは、下記のプログラム。

160行目の、 topPipes[i++] = new Pipe(100, 3, 2000, “dokan_tate.png”); をコピーして、下の行に貼り付けよう。

new Pipe(); の中の3つの数値は、それぞれ
パイプが出てくる高さ、パイプのスピード、パイプが出てくる時間(ミリ秒)。

数値を変更して、4本目のパイプが出てくる位置とタイミングを調整してみよう!

例えば、これだと3本目より少し下に、3本目よりも1秒遅れて出てくる。

パイプの本数を増やして、ゲームを面白くしていこう!

ゲームクリアまでの時間を調整

このゲームは、一定時間、土管に当たらず、下に落ちずに耐えればゲームクリアになる。
今は、すぐにゲームが終わってしまうので、ゲームクリアになるまでの時間を変えてみよう!

10000が今のゲームクリア時間(ミリ秒)。10000ミリ秒なので、10秒間でクリアになっている。
設置した土管の数によって、クリアにかかる時間も変わりますね。自分のプログラムに合わせて、クリアタイムも変更してみよう。

ボーナスキャラを登場させる

当たると、クリアまでの時間を短くしてくれるボーナスキャラを登場させてみよう!

下記のプログラムをコピーして、

image(kurage, b1x, b1y);
b1x += b1xSpeed;
if(dist(b1x, b1y, birdX, birdY) < size/2){
  bonusTime += 1000;  
  b1x -= 5000;  // 衝突したら、遥か左に瞬間移動させる
}

ここに貼り付けよう。

クラゲがボーナスキャラです。
・・・土管のすぐそばにいて良くないですね。出現位置を調整しましょう。

ボーナスキャラの位置やスピードはここで設定している。

b1x, b1y の値を変更してみよう。
b1xを大きくすれば出現位置が右側に、小さくすれば左側に。
b1yを大きくすれば下側に、小さくすれば上側に出現するようになる。

ボーナスキャラを増やす

ボーナスキャラを2体以上にすることも出来る!
少し難しいので、間違えないように慎重にプログラミングしていこう。

1.この4行をコピーして

2.下に貼り付け(赤い波下線が出るけど、今は気にしない)

3.貼り付けた方の 1 を 2 に書き換える

4.b2x, b2y, b2xSpeed, b2ySpeedの値を変更する。

※同じままにしてしまうと、同じ場所に出現するから増えたことが分からない。

5.2体目のボーナスキャラを実装する。
  このプログラムをコピーして、

下の行に貼り付けて、

1を2に書き換える

これで、2体目のボーナスキャラが出現する。

自分で描いた絵を登場させてみよう!

ゲームに出てくるキャラを自分で描いてみよう!

※手順が少し長いので、質問しながらじっくり進めよう。

Picnote
Pixnote Editor Lite をクリック

+ をクリックして、新しく絵を描いていく。

そのまま、 [Create]

謎の生物を描きます。


描き終わったら、上の方にある (PNG) ボタンをクリック。


[ DL ] ボタンをクリックしてダウンロード。

ダウンロードした画像ファイルを保存。


Processingに戻って、 [スケッチ] → [ファイルを追加] とクリック。

ダウンロードした画像を選択する。

クラゲを自分が描いた絵に変えてみる。

ここの、 kurage.png を、追加した画像の名前に変更する。


無事、謎の生物が出現!

好きにアレンジして、ゲームを面白くしてみよう!!

配布プログラム一式

配布プログラム一式ダウンロード(Zipファイル)

配布プログラム全文

// 鳥の位置と速度に関する変数
float birdX = 50;
float birdY = 400;
float birdVelocity = 0;  // 鳥の上下移動速度
PImage birdImage;        // 鳥の画像
PImage birdImage1;        // 鳥の画像 差分1
PImage birdImage2;        // 鳥の画像 差分2
int jumpNum = 0;
int size = 64; // 鳥の大きさ(正方形)

// 2. ジャンプと重力の設定
float jumpVelocity = -20;  // 上に飛ぶ力(マイナスで上に移動)
float gravity = 0.8;       // 毎フレーム落ちる速さ(重力)

boolean gameStart = false; // ゲーム開始状態かどうか

Pipe[] topPipes = new Pipe[50]; // パイプを保存する配列(最大50本)

float aliveTime = 0;      // 鳥が生きている時間(ミリ秒)
float gameStartTime = 0;  // ゲーム開始時の時間

int gameState = 0;        // ゲームの状態(0: 通常 1: ゲームオーバー 2: クリア)

PImage kurage;
PImage rabbit;
PImage cat;
PImage coara;
PImage fish;
PImage panda;

PImage backGroundImage;

float b1x = 800;  // ボーナスキャラのx座標
float b1y = 300;  // ボーナスキャラのy座標
float b1xSpeed = -3;  // ボーナスキャラのx方向のスピード マイナスで左に進む
float b1ySpeed = 3;  // ボーナスキャラのy方向のスピード 初期状態だと使わない



float bonusTime = 0;

void setup() {
  // 画面の初期設定
  PFont font = createFont("Meiryo", 50); // 日本語フォントの読み込み
  textFont(font);
  size(800, 600);         // 画面サイズ
  imageMode(CENTER);       // 画像の表示位置を中央に設定
  birdImage1 = loadImage("bird1.png");  // 鳥の画像を読み込み
  birdImage1.resize(size, size);         // 鳥の画像のサイズを変更

  birdImage2 = loadImage("bird2.png");  // 鳥の画像を読み込み
  birdImage2.resize(size, size);         // 鳥の画像のサイズを変更

  birdImage = birdImage2;

  // 画像を使う準備
  kurage = loadImage("kurage.png");
  kurage.resize(size, size);
  rabbit = loadImage("rabbit.png");
  rabbit.resize(size, size);
  cat = loadImage("cat.png");
  cat.resize(size, size);
  coara = loadImage("coara.png");
  coara.resize(size, size);
  fish = loadImage("fish.png");
  fish.resize(size, size);
  panda = loadImage("panda.png");
  panda.resize(size, size);

  backGroundImage = loadImage("bg_natural_sougen.jpg");
  backGroundImage.resize(width, height);

  createPipe();  // パイプの作成
}

void draw() {
  // ゲームオーバー画面
  if (gameState == 1) {
    background(255);
    PImage gameOverImg = loadImage("text_gameover_j.png");
    gameOverImg.resize(500, 200);
    image(gameOverImg, 400, 250);
    return;
  }

  // ゲームクリア画面
  if (gameState == 2) {
    background(255);
    PImage gameClearImg = loadImage("text_gameclear_j.png");
    gameClearImg.resize(500, 200);
    image(gameClearImg, 400, 250);
    image(cat, 300, 100);

    return;
  }

  // ゲームプレイ中の背景と情報の描画
  background(255);
  image(backGroundImage, width/2, height/2);
  fill(0);
  text("経過時間 : " + aliveTime / 1000, 100, 200); // 秒単位で表示
  fill(255, 255, 0);
  // 1. 鳥の画像を表示
  image(birdImage, birdX, birdY);

  // ゲームが始まっている場合の処理
  if (gameStart) {
    aliveTime = millis() - gameStartTime + bonusTime; // 生存時間を更新

    if (aliveTime > 10000) { // 4. ゲームクリアまでの時間(ミリ秒)
      gameState = 2;
    }

    // 重力による落下処理
    birdVelocity += gravity;
    birdY += birdVelocity;

    // パイプの表示と衝突判定
    for (int i = 0; i < topPipes.length; i++) {
      if (topPipes[i] != null) {
        topPipes[i].movePipe();

        // 上パイプとの衝突チェック
        if (checkCollision(topPipes[i].x, topPipes[i].y, topPipes[i].pipeWidth, topPipes[i].pipeHight, birdX, birdY, size)) {
          gameState = 1; // ゲームオーバー
        }

        // 下パイプとの衝突チェック
        if (checkCollision(topPipes[i].x, topPipes[i].y + topPipes[i].pipeSpace, topPipes[i].pipeWidth, topPipes[i].pipeHight, birdX, birdY, size)) {
          gameState = 1;
        }
      }
    }

    // 5. ボーナスキャラクターを実装





    // 地面に落ちたらゲームクリア
    if (birdY >= height) {
      gameState = 1;
    }
  }
}

void keyPressed() {
  // スペースキーでジャンプ
  if (key == ' ') {
    if (!gameStart) {
      gameStart = true;
      gameStartTime = millis(); // ゲーム開始時刻を記録
    }
    birdVelocity = jumpVelocity; // 上にジャンプ
    jumpNum++;
    if (jumpNum % 2 == 0) {
      birdImage = birdImage1;
    } else {
      birdImage = birdImage2;
    }

    saveFrame("frame.png");
  }
}

// パイプの初期データを追加する関数
void createPipe() {
  int i = 0;
  // new Pipe( パイプの高さ, パイプのスピード, パイプが出てくる時間(ミリ秒), "土管の画像名");
  topPipes[i++] = new Pipe(0, 3, 0, "dokan_tate.png");
  topPipes[i++] = new Pipe(0, 3, 1000, "dokan_tate.png");
  topPipes[i++] = new Pipe(100, 3, 2000, "dokan_tate.png");
  // 3. パイプを追加する
}

// パイプクラス
class Pipe {
  float x, y, speed, waitTime;
  PImage img;

  int pipeWidth = 100;  // パイプの幅
  int pipeHight = 256;  // パイプの高さ("height"のスペルミス注意)

  int pipeSpace = 400;  // 上下のパイプの間隔

  // コンストラクタ(パイプの初期設定)
  Pipe(float y, float speed, float waitTime, String imgName) {
    this.x = width + 50;
    this.y = y;
    this.speed = speed;
    this.waitTime = waitTime;
    img = loadImage(imgName);
    img.resize(pipeWidth, pipeHight);
  }

  // パイプの移動と表示
  void movePipe() {
    if (waitTime > aliveTime) {
      return; // 指定時間前ならまだ表示しない
    }

    this.x -= speed; // 左に移動

    image(img, x, y); // 上パイプ
    image(img, x, y + pipeSpace); // 下パイプ
  }
}

// 鳥とパイプの当たり判定を行う関数
// (四角形のx, y, 幅, 高さ, 円のx, y, 直径)
boolean checkCollision(float x, float y, float w, float h, float x2, float y2, float r) {
  r = r / 2; // 直径を半径に変換

  x2 = x2 - r/2;
  y2 = y2 - r/2;
  x = x - w/2;
  y = y - h/2;

  // 当たり判定を甘くする
  // 係数を変えて難易度を調整してみよう!
  r = r * 0.8;
  w = w * 0.8;
  h = h * 0.8;

  // 鳥の中心がパイプの内側に入っているかどうか
  if (x - r <= x2 && x + w + r >= x2 &&
    y <= y2 && y + h >= y2) {
    return true;
  }

  // 鳥がパイプの上下にはみ出しているかどうか
  if (x <= x2 && x + w >= x2 &&
    y - r <= y2 && y + h + r >= y2) {
    return true;
  }

  // 四隅との距離チェック(円と矩形の角)
  if (dist(x, y, x2, y2) < r) return true;
  if (dist(x + w, y, x2, y2) < r) return true;
  if (dist(x + w, y + h, x2, y2) < r) return true;
  if (dist(x, y + h, x2, y2) < r) return true;

  return false;
}