フラッピーバードを作ろう
フラッピバードを知ろう
Processingをインストール
上記サイトからダウンロード出来ます。
※寄付はしなくても大丈夫です!
テンプレートプログラムを実行してみよう
Processingの三角ボタンをクリックすると、ゲーム実行!

■ボタンで終了。
何も起こらない・・・・プレイヤーを表示しよう!
プレイヤーを表示するプログラムを追加しよう!
このプログラムをコピーして、 // 1. 鳥の画像を表示 の場所に貼り付けてみよう

image(birdImage, birdX, birdY);

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

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

ジャンプを調整してみよう
何だかジャンプが力強くて難しいですね・・・?
ジャンプする力と、落ちてくるスピードを調整して、土管を潜れる難易度にしてみよう!
12行目と13行目が、鳥が上に飛ぶ力と落ちる速さ。

それぞれ値を変更して、鳥の飛び方を調整してみよう。
土管の当たり判定を調整
土管と鳥の当たり判定が厳しいな・・・と思ったら、
プログラムの一番下あたりにある、この箇所を修正!

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

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


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 を、追加した画像の名前に変更する。


無事、謎の生物が出現!
好きにアレンジして、ゲームを面白くしてみよう!!
配布プログラム一式
配布プログラム全文
// 鳥の位置と速度に関する変数
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;
}