Siv3D AdventCalendar 2016 の 3 日目の記事です.
概要
Siv3D に付属されている HamFramework は,2D カメラ操作を簡単に実装できるクラス ham::Camera2D
を提供しています.以下のコードでマウス操作や W/A/S/D/上下キー入力による 2D カメラ移動が可能になります.
# include <Siv3D.hpp>
# include <HamFramework.hpp>
void Main()
{
const Font font(30);
Camera2D camera;
while (System::Update())
{
camera.update();
{
const auto t1 = camera.createTransformer();
font(L"ようこそ、Siv3D の世界へ!").draw();
Circle(Mouse::PosF(), 50).draw({ 255, 0, 0, 127 });
}
camera.draw(Palette::Orange);
}
}
このクラスを作った理由
マウス操作でのカメラ移動が可能な状況で四角形を描画すると以下のようになります.
# include <Siv3D.hpp>
# include <HamFramework.hpp>
void Main()
{
Vec2 cameraPos(0.0, 0.0);
const Vec2 rectPos = Window::Size() / 2.0;
const Vec2 rectSize(100.0, 100.0);
Vec2 mouseClickStartPos;
while (System::Update())
{
if (Input::MouseR.clicked)
{
mouseClickStartPos = Mouse::Pos();
}
if (Input::MouseR.pressed)
{
cameraPos += (Mouse::Pos() - mouseClickStartPos) * 0.1;
Circle(mouseClickStartPos, 100.0).drawFrame(2.0, 2.0);
Circle(Mouse::Pos(), 30.0).draw();
}
RectF(rectSize).setCenter(rectPos - cameraPos).draw();
}
}
このプログラムでは
- カメラの位置
cameraPos
を用意しその座標をマウスの操作により動かす - 四角形の描画時に位置
rectPos
からカメラの位置を引く
ことにより,マウス操作によるカメラ移動を実現しています.ここで,カメラ移動だけでなく拡大縮小やキーボード入力への対応などの機能を追加したい場合にはどうすればよいでしょうか.描画対象すべての位置や拡大率を個別に操作することが必要になりそうでが,可能であれば描画とカメラ操作は分離して実装したいと思いました.他にも同じ思いをしている人がいるのではないかと考え,Siv3D 上での2Dカメラ移動を簡単に実装できる Camera2D を作成しました.
2Dカメラ操作をサポートする Camera2D
Siv3D には描画とマウスの座標に任意の行列を使って移動やスケーリングを一律に適用できる Graphics2D::PushTransform()
と Graphics2D::PopTransform()
があります.Graphics2D::PushTransform()
には移動やスケーリングを行列形式で表現する Mat3x2
型の値を渡します.Graphics2D::PopTransform()
が呼ばれると,それまで呼ばれた行列を最新の順から解除していきます.さらに,スコープから外れた際に Push した行列が自動的に Pop してくれる,Transformer2D
クラスが Siv3D に存在します.HamFramework の Camera2D はこの Transformer2D を作成する関数 createTransformer()
を使うことにより,特定のスコープ内の描画オブジェクトの移動・スケーリングを行います.
基本的な使い方
# include <Siv3D.hpp>
# include <HamFramework.hpp>
void Main()
{
Camera2D camera;
while (System::Update())
{
camera.update();
{
const auto t1 = camera.createTransformer();
Circle(100, 100, 100).draw(Palette::Red);
Rect(150, 100, 200, 200).draw(Palette::Orange);
Line(50, 400, 600, 50).draw(3, Color(128, 255, 255));
Circle(400, 400, 200).draw(Color(0, 0, 255, 127));
}
camera.draw(Palette::Orange);
}
}
-
HamFramework.hpp
をインクルード -
Camera2D
クラスのインスタンスを作成 - メインループの中で
Camera2D::update()
を呼ぶ -
{ }
でスコープを作り、その中でCamera2D::createTransformer()
を使ってTransformer2D
を作成 - カメラ操作する対象であるオブジェクトを描画
- スコープ外で
Camera2D
オブジェクトのdraw()
を呼ぶ(任意)
これにより
- マウスの右クリック・ドラッグまたは W/A/S/D キーでカメラの移動
- マウスのホイールまたは上下キーでカメラのズーム操作
ができるようになります.
マウス座標
マウス座標の取扱には注意が必要です.
# include <Siv3D.hpp>
# include <HamFramework.hpp>
void Main()
{
Camera2D camera;
while (System::Update())
{
camera.update();
{
const auto t1 = camera.createTransformer();
Circle(Mouse::Pos(), 2.0).drawFrame(0.1, 0.1);
Circle(Mouse::PosF(), 1.0).draw();
}
camera.draw(Palette::Orange);
}
}
マウス座標の取得方法には二種類あります.Point
型でマウス座標を取得する Mouse::Pos()
と Vec2
型でマウス座標を取得する Mouse::PosF()
です.Mouse::Pos()
ではズーム時に実際のマウス位置からは離れた場所を指し示すことがあるため,Camera2D とうまく連携させるには Mouse::PosF()
を利用してください.
カメラ操作の調整
キーボードまたはマウスのどちらかのみでカメラを操作したい場合や,カメラの移動速度などをカスタマイズしたいときには,Camera2D::update()
の引数を変更します.引数は以下の通りです.
-
speed
: カメラの速度 -
scaleRatio
: ズーム変更率 [0.0~1.0] -
mouseSpeedRatio
: マウス操作の影響率 [0.0~1.0] -
lerpRatio
: 補間率 [0.0~1.0](ぬるっと感) -
useKeyControl
: キーボード入力を受け付けるかどうか -
useMouseControl
: マウス入力を受け付けるかどうか
例えば,キーボード入力を受け付けずマウス入力のみを受け付けたい場合には,useKeyControl
を false
にし,useMouseControl
を true
にします.(デフォルトではどちらも true
です.)
カメラ位置・ズームの変更
# include <Siv3D.hpp>
# include <HamFramework.hpp>
void Main()
{
Camera2D camera;
Vec2 playerPos = Window::Center();
const Rect rect(20, 20, 200, 100);
const Circle circle(150, 300, 100);
const Polygon star = Geometry2D::CreateStar(200, 0.0, Vec2(400, 250));
while (System::Update())
{
camera.update(10.0, 1.1, 0.01, 0.2, false, true);
{
const auto t1 = camera.createTransformer();
playerPos += Vec2(Input::KeyRight.pressed - Input::KeyLeft.pressed, Input::KeyDown.pressed - Input::KeyUp.pressed) * 10.0;
camera.setTargetPos(playerPos);
const Circle player(playerPos, 30);
// プレイヤーと重なっていたら赤、そうでなかったら黄色で図形を描く
rect.draw(player.intersects(rect) ? Palette::Red : Palette::Yellow);
circle.draw(player.intersects(circle) ? Palette::Red : Palette::Yellow);
star.draw(player.intersects(star) ? Palette::Red : Palette::Yellow);
player.draw();
}
}
}
ここでは,Camera2D
のカメラ位置をプレイヤーと同じ位置に設定しています.
※Camera2D
の setTargetPos()
次回の Siv3D アップデートで追加される関数です.それ以前のバージョンでは使用できないので,以下のコードを HamFramework/CameraManager.hpp
の 227 行目に入れてください.
void setTargetPos(const Vec2& targetPos)
{
m_targetPos = targetPos;
}
void setTargetScale(const double targetScale)
{
m_targetScale = targetScale;
}
キーボード入力時におけるキー割り当て
Camera2D
のキーボード入力のキー割り当てはデフォルトでは移動が W/A/S/D, ズーム操作が上下キーとなっています.変更をしたい場合には,setKeyConfig()
を利用してください.たとえば,移動に上下左右キー,ズーム操作に Z・X キーを割り当てるには
# include <Siv3D.hpp>
# include <HamFramework.hpp>
void Main()
{
const std::array<s3d::Key, 6> keyConfig =
{
s3d::Input::KeyUp, s3d::Input::KeyLeft, s3d::Input::KeyDown, s3d::Input::KeyRight,
s3d::Input::KeyZ, s3d::Input::KeyX
};
Camera2D camera;
camera.setKeyConfig(keyConfig);
while (System::Update())
{
camera.update();
{
const auto t1 = camera.createTransformer();
Circle(100, 100, 100).draw(Palette::Red);
Rect(150, 100, 200, 200).draw(Palette::Orange);
Line(50, 400, 600, 50).draw(3, Color(128, 255, 255));
Circle(400, 400, 200).draw(Color(0, 0, 255, 127));
}
camera.draw(Palette::Orange);
}
}
のように,std::array<s3d::Key, 6>
で作成されるキー割り当てを Camera2D::setKeyConfig()
の引数に入れてください.デフォルトでのキー割り当ては以下のようになっています.
std::array<s3d::Key, 6> m_keyConfig =
{
s3d::Input::KeyW, s3d::Input::KeyA, s3d::Input::KeyS, s3d::Input::KeyD,
s3d::Input::KeyUp, s3d::Input::KeyDown
};
ScalableWindow との併用
Camera2D
と同様,Transformer2D
を利用することでウィンドウの大きさを自由に変更可能な機能 ham::ScalableWindow
を HamFramework で提供しています.Camera2D と ScalableWindow を同時に使いたい場合には以下のように書きます
# include <Siv3D.hpp>
# include <HamFramework.hpp>
void Main()
{
Camera2D camera;
ScalableWindow::Setup();
while (System::Update())
{
camera.update();
{
const auto t1 = camera.createTransformer();
const auto t2 = ScalableWindow::CreateTransformer();
Circle(100, 100, 100).draw(Palette::Red);
Rect(150, 100, 200, 200).draw(Palette::Orange);
Line(50, 400, 600, 50).draw(3, Color(128, 255, 255));
Circle(400, 400, 200).draw(Color(0, 0, 255, 127));
}
camera.draw(Palette::Orange);
}
}
-
ScalableWindow::Setup()
を呼ぶ -
Camera2D::createTransformer()
の後ScalableWindow::CreateTransformer()
でTransformer2D
オブジェクト生成
カメラ操作をもっとカスタマイズしたい
Camera2D
の操作をもっとカスタマイズしたい場合は,BasicCamera2D
もしくは Camera2D
を継承した独自のカメラクラスを作成してください.例えば,Y軸の正の方向を上向きに調整したカメラクラス CameraBox2D
は Camera2D
の一部の関数をオーバーライドすることで作成しました.
まとめ
Siv3D Advent Calendar 2016 3 日目は Camera2D の使い方について紹介しました.
明日は @agehama_ さんの記事です.よろしくお願いいたします.