1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ProcessingのFisicaつかってFlappyBirdつくる

Last updated at Posted at 2025-11-06

ワンキーゲームとしてのFlappyBird

こういうの面白いですね。スパーダーマンのような気分を味わえます。

ワンキーゲーム、もしくは、ワンボタンゲームはボタン一つで設計されているゲームです。
入力を最小限に絞ることで、ゲームデザインにより集中できます。
そこで良いアイデアが生まれたなら、それをベースに、さらにアイデアをふくらませることもできるでしょう。

ゲームデザイナーの桜井さんは「連射、タイミング、早押し、切り替え」に分類しています。

というわけで、代表的なFlappyBirdっぽいものをProcessingのFisicaを使って、作ってみます。
FlappyBirdは「タイミング」に属するゲームです。

00 Fisicaライブラリのインストール

image.png

01 鳥と地面とクリック操作

image.png

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);
}

障害物の設置

image.png

右から左へ移動する障害物を作ります。
移動速度や、発生のタイミング、通り過ぎた後の削除処理などを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);
}

完成(点数、テクスチャ、やり直し)

rooster0.png
rooster1.png

image.png

なにか点数があったほうが、楽しいですから、開始からのフレーム数をカウントして、点数に使いました。

黄色の四角は、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);
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?