DragTracker
GitHub 版はこちらから
説明
マウスのドラッグ操作の状態を保存しておくためのクラスです。(ドラッグ&ドロップのことではない)
サンプル概要
マウスに追尾して移動する円(白)
マウスを乗せると水色
ドラッグするとオレンジ色
ドラッグ先に線形補間で移動する円(緑)
ドラッグ最中は円は動かない
ドラッグ終了後に、ドラッグ終点に向かって線形補間で移動する
ドラッグ後に移動している円の図:
https://scrapbox.io/files/64f1a5ec73319b001c1dfb6d.png
ドラッグ中でマウスに追尾している円の図:
https://scrapbox.io/files/64f1a66b419750001b7f2599.png
code: DragTracker.cpp
#include <Siv3D.hpp> // OpenSiv3D v0.6.11 /// @brief ドラッグの状態を追跡するクラス
/// @details ドラッグの状態及び、開始位置と終了位置を追跡する
struct DragTracker
{
/// @brief ドラッグの状態
enum class State : int8
{
none = 0b00000000, ///< ドラッグしていない
down = 0b00000001, ///< ドラッグ開始
pressed = 0b00000010, ///< ドラッグ中
up = 0b00000100, ///< ドラッグ終了
};
/// @brief コンストラクタ
/// @param input ドラッグ判定に用いる入力キー等
DragTracker(InputGroup input = MouseL)
: m_input(input) {}
/// @brief ドラッグの状態を更新する
/// @param isOverStartRegion ドラッグを開始出来る位置にある場合に true を渡す
/// @return ドラッグ中(離したタイミングも含む)なら true を返す
bool update(bool isOverStartRegion) noexcept
{
// 既にドラッグが開始しているか
if (pressed())
{
// 既にドラッグが開始していれば、領域外に出ていても押し続けられてさえいれば良い
if (m_input.pressed())
{
m_state = State::pressed;
}
else
{
m_state = State::up;
}
}
else
{
// ドラッグ開始領域にカーソルがあり、かつ入力があればドラッグ開始
if (isOverStartRegion && m_input.down())
{
m_state = State::down;
}
// ドラッグ終了後は状態を none に戻す
if (m_state == State::up)
{
m_state = State::none;
}
}
switch (m_state)
{
case State::down:
{
m_from = Cursor::PosF();
m_to = none;
}
break;
case State::up:
{
m_to = Cursor::PosF();
}
break;
default:
break;
}
return duringDragging();
}
/// @brief そのフレームでドラッグが開始したか
bool down() const noexcept
{
return m_state == State::down;
}
/// @brief そのフレームでドラッグ中か
bool pressed() const noexcept
{
return m_state == State::down || m_state == State::pressed;
}
/// @brief そのフレームでドラッグが終了したか
bool up() const noexcept
{
return m_state == State::up;
}
/// @brief そのフレームでドラッグしているか
bool duringDragging() const noexcept
{
return m_state != State::none;
}
/// @brief ドラッグの状態を取得する
/// @return ドラッグの状態
State state() const noexcept
{
return m_state;
}
/// @brief ドラッグの開始位置を取得する
/// @return ドラッグの開始位置
/// @note 一度もドラッグしていない場合は none を返す
Optional<Vec2> from() const noexcept
{
return m_from;
}
/// @brief ドラッグの終了位置を取得する
/// @return ドラッグの終了位置
/// @note 一度もドラッグしていない場合と、ドラッグが開始してから終了する前は none を返す
Optional<Vec2> to() const noexcept
{
return m_to;
}
private:
InputGroup m_input;
State m_state = State::none;
Optional<Vec2> m_from, m_to;
};
void Main()
{
Window::Resize(1'755, 810, Centering::Yes);
//Scene::SetResizeMode(ResizeMode::Actual);
Scene::SetBackground(Color(U"#B8C9CD"));
// ドラッグの状態を追跡するクラスのインスタンスを生成
DragTracker trackerForCircle, trackerForLerpCircle;
// 円の位置
Vec2 posForCircle = Scene::Size() * 0.25, posForLerpCircle = Scene::Size() * 0.75;
// LerpCircle の位置を変更する際に用いるトランジション
Transition transitionForLerpCircle{0.5s, 0.0s, 1.0};
while (System::Update())
{
{
const auto circle = Circle(posForCircle, 50);
auto color = Palette::White;
// マウスを乗せてるとき
if (circle.mouseOver())
{
color = Palette::Skyblue;
}
// ドラッグしているとき
if (trackerForCircle.update(circle.mouseOver()))
{
// マウスに追尾
posForCircle = Cursor::PosF();
color = Palette::Orange;
}
circle.draw(color);
}
{
const auto circle = Circle(posForLerpCircle, 50);
auto color = Palette::Lightgreen;
// ドラッグしているとき
if (trackerForLerpCircle.update(circle.mouseOver()))
{
transitionForLerpCircle.reset();
// 強めの緑
color = Color(U"#62D662");
}
else
{
// ドラッグ終了後はトランジションを進める
transitionForLerpCircle.update(true);
const auto from = trackerForLerpCircle.from().value_or(posForLerpCircle);
const auto to = trackerForLerpCircle.to().value_or(posForLerpCircle);
// 次の位置を線形補間で計算
posForLerpCircle = Math::Lerp(from, to, transitionForLerpCircle.easeOut(EaseInBack));
// ドラッグ終了後でトランジションが終わっていないとき
if (not transitionForLerpCircle.isOne())
{
// 行き先の位置に円を描画
circle.stretched(10).setPos(to).drawFrame(10, color);
}
}
circle.draw(color);
}
}
}