ワンキーゲームとしてのFlappyBird
こういうの面白いですね。スパーダーマンのような気分を味わえます。
ワンキーゲーム、もしくは、ワンボタンゲームはボタン一つで設計されているゲームです。
入力を最小限に絞ることで、ゲームデザインにより集中できます。
そこで良いアイデアが生まれたなら、それをベースに、さらにアイデアをふくらませることもできるでしょう。
ゲームデザイナーの桜井さんは「連射、タイミング、早押し、切り替え」に分類しています。
というわけで、代表的なFlappyBirdっぽいものをProcessingのFisicaを使って、作ってみます。
FlappyBirdは「タイミング」に属するゲームです。
00 Fisicaライブラリのインストール
01 鳥と地面とクリック操作
Fisicaの基本的な使い方として、物理属性を持ったオブジェクトを登録して、stepとdrawで回していきます。
クリックで、上昇することを確認します。
import fisica.*;
FWorld world;
FBox bird;
void setup() {
size(1024, 768);
Fisica.init(this);
world = new FWorld();
world.setGravity(0, 2000);
makeBird();
makeGround();
}
void draw() {
background(140, 200, 255);
// fisicaの計算と表示
world.step();
world.draw();
}
// クリックで上昇
void mousePressed() {
bird.addImpulse(0, -15000);
}
//鳥生成
void makeBird() {
// 鳥
bird = new FBox(64, 64);
bird.setDensity((80f*60f)/(64f*64f));
bird.setPosition(160, 200);
bird.setFill(255, 230, 0);
bird.setNoStroke();
bird.setRestitution(0.2);
bird.setGrabbable(false);
bird.setRotatable(false);
world.add(bird);
}
//地面生成
void makeGround() {
FBox ground;
// 地面
ground = new FBox(width, 100);
ground.setPosition(width/2, height - 50);
ground.setStatic(true);
ground.setFill(0, 160, 0);
ground.setNoStroke();
ground.setRestitution(0.2);
ground.setGrabbable(false);
world.add(ground);
}
障害物の設置
右から左へ移動する障害物を作ります。
移動速度や、発生のタイミング、通り過ぎた後の削除処理などをArrayListを使って、実装します。
また、衝突検知の関数が用意されているので、衝突したら、そこでストップするようにします。contactStarted(FContact c)関数はworldで衝突が生じると呼び出されます。引数 c には、衝突した2つのオブジェクトが入っていますので、そのどちらかが、birdであれば、壁か地面にぶつかったということなので、そこで、ゲームをストップ(noLoop)させます。
現状では、やり直し方法は、再実行です。
この段階で、各種の値が、遊びやすくなっているか、確認しましょう。
import fisica.*;
FWorld world;
FBox bird;
ArrayList<FBox> walls;
float spawnInterval = 2.8;
float timer = 0;
float wallspeed = 200; // 左に流れる速度(ピクセル/秒)
void setup() {
size(1024, 768);
Fisica.init(this);
world = new FWorld();
world.setGravity(0, 2000);
makeBird();
makeGround();
walls = new ArrayList<FBox>();
}
void draw() {
background(140, 200, 255);
float dt = 1.0 / frameRate; // 1.0/60.0
timer += dt;
// 壁生成(一定時間ごと)
if (timer > spawnInterval) {
spawnWallPair();
timer = 0;
}
// パイプを左に動かす
moveWalls(dt);
removeOffscreenWalls();
// fisicaの計算と表示
world.step();
world.draw();
}
// クリックで上昇
void mousePressed() {
bird.addImpulse(0, -15000);
}
// 壁 生成
void spawnWallPair() {
float gap = 300;
float gapCenter = random(200, 500);
float WallW = 120;
float x = width + WallW;
float topH = gapCenter - gap/2;
float botH = (height - 100) - (gapCenter + gap/2);
// 上パイプ
FBox topWall = new FBox(WallW, topH);
topWall.setPosition(x, topH/2);
topWall.setStatic(true);
topWall.setFill(0, 200, 0);
topWall.setNoStroke();
world.add(topWall);
walls.add(topWall);
// 下パイプ
FBox botWall = new FBox(WallW, botH);
botWall.setPosition(x, height - 100 - botH/2);
botWall.setStatic(true);
botWall.setFill(0, 200, 0);
botWall.setNoStroke();
world.add(botWall);
walls.add(botWall);
}
// 壁 移動
void moveWalls(float dt) {
for (FBox p : walls) {
float x = p.getX() - wallspeed * dt;
p.setPosition(x, p.getY());
}
}
// 壁 削除
void removeOffscreenWalls() {
for (int i = walls.size()-1; i >= 0; i--) {
FBox p = walls.get(i);
if (p.getX() < -200) {
world.remove(p);
walls.remove(i);
}
}
}
//衝突検知
void contactStarted(FContact c) {
// fisicaで定義済みの関数
// 衝突検知すると、cの中に2つのオブジェクトが入ってくる
FBody b1 = c.getBody1();
FBody b2 = c.getBody2();
// どちらかが bird だった場合
if (b1 == bird || b2 == bird) {
println("鳥が何かに接触しました!");
noLoop();
}
}
//鳥生成
void makeBird() {
// 鳥
bird = new FBox(64, 64);
bird.setDensity((80f*60f)/(64f*64f));
bird.setPosition(160, 200);
bird.setFill(255, 230, 0);
bird.setNoStroke();
bird.setRestitution(0.2);
bird.setGrabbable(false);
bird.setRotatable(false);
world.add(bird);
}
//地面生成
void makeGround() {
FBox ground;
// 地面
ground = new FBox(width, 100);
ground.setPosition(width/2, height - 50);
ground.setStatic(true);
ground.setFill(0, 160, 0);
ground.setNoStroke();
ground.setRestitution(0.2);
ground.setGrabbable(false);
world.add(ground);
}
完成(点数、テクスチャ、やり直し)
なにか点数があったほうが、楽しいですから、開始からのフレーム数をカウントして、点数に使いました。
黄色の四角は、bird.attachImage()を使って、画像に差し替えました。
速度を参照しながら、正負で2種類の画像を差し替えてアニメーションさせています。
また、いずれかのキー入力で、最初からやり直しできるように、プログラムしました。
import fisica.*;
FWorld world;
FBox bird;
ArrayList<FBox> walls;
float spawnInterval = 2.8;
float timer = 0;
float wallspeed = 200; // 左に流れる速度(ピクセル/秒)
int score=0;
PImage rooster0, rooster1;
void setup() {
size(1024, 768);
Fisica.init(this);
world = new FWorld();
world.setGravity(0, 2000);
makeBird();
makeGround();
walls = new ArrayList<FBox>();
rooster0 = loadImage("rooster0.png");
rooster1 = loadImage("rooster1.png");
bird.attachImage(rooster0);
}
void draw() {
background(140, 200, 255);
float dt = 1.0 / frameRate; // 1.0/60.0
timer += dt;
// 壁生成(一定時間ごと)
if (timer > spawnInterval) {
spawnWallPair();
timer = 0;
}
// パイプを左に動かす
moveWalls(dt);
removeOffscreenWalls();
// fisicaの計算と表示
world.step();
world.draw();
textSize(50);
text(score/10, 50, 720);
score++;
//println(bird.getMass());
if (bird.getVelocityY()<0) {
bird.attachImage(rooster1);
} else {
bird.attachImage(rooster0);
}
println(bird.getVelocityY());
}
// クリックで上昇
void mousePressed() {
bird.addImpulse(0, -15000);
}
//キー入力でリセット
void keyPressed() {
bird.setPosition(160, 200);
bird.setVelocity(0, 0);
for (int i = walls.size()-1; i >= 0; i--) {
FBox p = walls.get(i);
world.remove(p);
walls.remove(i);
}
score = 0;
loop();
}
// 壁 生成
void spawnWallPair() {
float gap = 300;
float gapCenter = random(200, 500);
float WallW = 120;
float x = width + WallW;
float topH = gapCenter - gap/2;
float botH = (height - 100) - (gapCenter + gap/2);
// 上パイプ
FBox topWall = new FBox(WallW, topH);
topWall.setPosition(x, topH/2);
topWall.setStatic(true);
topWall.setFill(0, 200, 0);
topWall.setNoStroke();
world.add(topWall);
walls.add(topWall);
// 下パイプ
FBox botWall = new FBox(WallW, botH);
botWall.setPosition(x, height - 100 - botH/2);
botWall.setStatic(true);
botWall.setFill(0, 200, 0);
botWall.setNoStroke();
world.add(botWall);
walls.add(botWall);
}
// 壁 移動
void moveWalls(float dt) {
for (FBox p : walls) {
float x = p.getX() - wallspeed * dt;
p.setPosition(x, p.getY());
}
}
// 壁 削除
void removeOffscreenWalls() {
for (int i = walls.size()-1; i >= 0; i--) {
FBox p = walls.get(i);
if (p.getX() < -200) {
world.remove(p);
walls.remove(i);
}
}
}
//衝突検知
void contactStarted(FContact c) {
// fisicaで定義済みの関数
// 衝突検知すると、cの中に2つのオブジェクトが入ってくる
FBody b1 = c.getBody1();
FBody b2 = c.getBody2();
// どちらかが bird だった場合
if (b1 == bird || b2 == bird) {
println("鳥が何かに接触しました!");
noLoop();
}
}
//鳥生成
void makeBird() {
// 鳥
bird = new FBox(64, 64);
bird.setDensity((80f*60f)/(64f*64f));
bird.setPosition(160, 200);
bird.setFill(255, 230, 0);
//bird.setNoStroke();
bird.setRestitution(0.2);
bird.setGrabbable(false);
bird.setRotatable(false);
world.add(bird);
}
//地面生成
void makeGround() {
FBox ground;
// 地面
ground = new FBox(width, 100);
ground.setPosition(width/2, height - 50);
ground.setStatic(true);
ground.setFill(0, 160, 0);
ground.setNoStroke();
ground.setRestitution(0.2);
ground.setGrabbable(false);
world.add(ground);
}





