Siv3D での 2D 物理演算をサポートする Physics (Box2D)

[Siv3D AdventCalendar 2016](http://qiita.com/advent-calendar/2016/siv3d) の 18 日目の記事です．

# 概要
Siv3D に付属されている HamFramework の機能の一つに，簡易的な 2D 物理シミュレーションを実装できる「Physics」があります．以下のソースコードで玉を転がす遊びができます．

![Siv3DBox2D001.gif](https://qiita-image-store.s3.amazonaws.com/0/34295/9cff37e3-49ca-816e-5a09-cf22bdcec527.gif)

```cpp:Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
    CameraBox2D camera(Vec2(0, 10), 17.0);

    PhysicsWorld world;

	auto ground = world.createLineString(Vec2(0, 0), { Vec2(-20, 10), Vec2(-20, 0), Vec2(20, 0), Vec2(20, 10) }, none, none, PhysicsBodyType::Static)
		.addLine({ Vec2(15, 20), Vec2(0, 15) })
		.addLine({ Vec2(-15, 10), Vec2(0, 5) });

    Array<PhysicsBody> bodies;

    while (System::Update())
    {
        world.update();
        camera.update();
        {
            const auto t1 = camera.createTransformer();

            if (Input::MouseL.clicked)
            {
                bodies.push_back(world.createCircle(Mouse::PosF(), 1.0));
            }
			
			Circle(Mouse::PosF(), 1.0).drawFrame(0.05);
			
            ground.draw();

            for (const auto& body : bodies)
            {
                body.draw();
            }
        }
        camera.draw();
    }
}

```

# 2D ゲーム と物理演算
物理演算を活用した 2D ゲームは多く存在します．例えば，Rovio Entertainment が開発した Angry Birds は，物理演算を用いて鳥の主人公を飛ばし，敵やオブジェクトを破壊するゲームです．また Umea 大学の学生 Emil Ernerfeldt の修士論文で開発された Phun (現 Algodoo) は，お絵かき感覚で物理演算を楽しむことができるゲーム（ツール）であり，インターネット上に多くの作品が公開されています．このような物理演算を活用したゲームを作るためには，自分で物理演算用の計算をするか，Box2D や Chipmunk2D 等の物理演算ライブラリを利用する必要がありますが，どちらもソースコードが長くなる傾向がありました．そこで，2D の物理演算を簡単に楽しめる Physics の基本的な使い方を紹介します．

# Box2D と Siv3D
HamFramework の Physics は，Box2D を Siv3D から利用しやすいようにまとめた機能です．Box2D とは オープンソースで開発された C++ の 2D ゲーム用物理演算ライブラリで，円形や多角形の剛体の運動や衝突判定をシミュレーションすることが可能です．（先程紹介した Angry Birds もこの Box2D を用いています．）HamFramework の Physics では Siv3D が C++ で開発されていることや，Box2D のドキュメントが豊富であることから，2D 物理演算に Box2D を採用しました．

# 基本的な使い方

## ワールドの生成と更新

```Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
    PhysicsWorld world;

    while (System::Update())
    {
        world.update();        
    }
}

```

1. `HamFramework.hpp` をインクルード
2. `PhysicsWorld` クラスのインスタンスを作成
3. メインループの中で `PhysicsWorld::update()` を呼ぶ

これにより，物理演算を行うための環境が生成されました．この時点では画面に何も表示されません．今後は，この `PhysicsWorld` オブジェクトを利用して，運動や衝突判定を行う物体 `PhysicsBody` の生成を行いきます．

## 物体の生成と描画

![Siv3DBox2D002.gif](https://qiita-image-store.s3.amazonaws.com/0/34295/c7fc3843-2d19-68da-30d6-10dcca2847ae.gif)

```Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
    PhysicsWorld world;

	const auto ground = world.createLine(Vec2(0, 10), Line({ 0, 0 }, {640, 0 }), none, none, PhysicsBodyType::Static);
	const auto ball = world.createCircle(Vec2(320, 300), Circle(100));

    while (System::Update())
    {
        world.update();        

		ground.draw();
		ball.draw();
    }
}

```

1. `PhysicsWorld::create***()` で `PhysicsBody` オブジェクトを生成
2. メインループの中で `PhysicsBody::draw()` を呼ぶ

`PhysicsWorld::create***()` の *** の部分にはRect，Circleなどの Siv3D で用いられる図形の名前が入ります．引数には，

1. 中央の位置
2. 図形
3. 物理定数（指定がない場合は none）
4. フィルタ（指定がない場合は none）
5. 静止するか運動するを決める `PhysicsBodyType`

の順番で指定します．中央の位置はその `PhysicsBody` の配置を意味します．しかし，このままでは不自然な点いくつかあります．

1. 重力の方向が上に向かっている
2. 物体の動きが遅い

これは，Box2D の単位がメートル単位であることや，Y 軸の方向が，上方向を正にしている事が原因で発生した現象です．（つまり上の例では，半径 100 [m] の円が高さ 290 [m] から降っている事になります．）そこで以前紹介した `ham::Camera2D` 機能を活用し，上記の不自然な点を解決します．
http://qiita.com/hamukun8686/items/95471335b8a0fa1d877f

## Camera2D の利用

![Siv3DBox2D003.gif](https://qiita-image-store.s3.amazonaws.com/0/34295/38cdd3f5-5afb-afea-8322-16718bd032af.gif)

```Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
	PhysicsWorld world;

	const auto ground = world.createLine(Vec2(0, -10), Line({ -20, 0 }, { 20, 0 }), none, none, PhysicsBodyType::Static);
	const auto ball = world.createCircle(Vec2(0, 10), Circle(1));

	CameraBox2D camera(Vec2(0, 0), 20);

	while (System::Update())
	{
		camera.update();
		{
			const auto t1 = camera.createTransformer();

			world.update();

			ground.draw();
			ball.draw();
		}
		camera.draw();
	}
}

```

1. `Camera2D` の派生クラスである `CameraBox2D` オブジェクトの作成と利用

これにより，Y 軸の正の方向は上方向になりした．また，カメラ位置や拡大率を変更できるようになりました．

# 今後の機能について
HamFramework の Physics 機能は Box2D の機能全体をカバーするため，今後のアップデートで新しい機能を追加していきます．具体的には，衝突時の処理を記述する方法，物体同士を接続して動かすジョイント機能などです．

# まとめ
今回は 2D の物理演算をサポートする Physics の基本的な使い方について解説しました．Physics を用いたゲームが公開されることを期待しています．

明日は @Nicious さんの記事です．よろしくお願いいたします．