コンテンツにスキップ

34. マウス入力

マウスによる入力を処理する方法を学びます。

34.1 マウスカーソルの座標を取得する

マウスカーソルの座標は Cursor::Pos() を使うと Point 型で取得できます。シーンが実ウィンドウサイズと異なる (チュートリアル 32. 参照) 場合、Cursor::PosF() を使うと Vec2 型で小数点数以下の座標も取得できます。

Cursor::Pos() で取得できるマウスカーソル座標は、最後の System::Update() の呼び出し時点での座標であるため、実際画面に見えている最新のマウスカーソルの位置よりも古い座標を示す場合があります。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		ClearPrint();
		Print << Cursor::Pos();
		Print << Cursor::PosF();
		Circle{ Cursor::PosF(), 50 }.draw(Palette::Skyblue);
	}
}

34.2 マウスカーソルの移動量を取得する

1 フレーム前のマウスカーソル座標は Cursor::PreviousPos() / Cursor::PreviousPosF() で取得できます。1 フレーム前からのマウスカーソルの移動量は Cursor::Delta() / Cursor::DeltaF() で取得できます。

Cursor::Delta() == (Cursor::Pos() - Cursor::PreviousPos()) です。

# include <Siv3D.hpp>

void Main()
{
	// 円をつかんでいるか
	bool grab = false;

	Circle circle{ Scene::Center(), 50 };

	while (System::Update())
	{
		if (grab)
		{
			// 移動量分だけ円を移動させる
			circle.moveBy(Cursor::Delta());
		}

		if (circle.leftClicked()) // 円を左クリックしたら
		{
			grab = true;
		}
		else if (MouseL.up()) // マウスの左ボタンが離されたら
		{
			grab = false;
		}

		if (grab || circle.mouseOver())
		{
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		circle.draw(Palette::Skyblue);
	}
}

34.3 マウスカーソルのスクリーン座標を取得する

マウスカーソルがスクリーン座標でデスクトップ上のどの位置にあるかを取得するには Cursor::ScreenPos() を使います。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		ClearPrint();

		// スクリーン座標におけるマウスカーソル座標
		Print << Cursor::ScreenPos();
	}
}

34.4 マウスのボタンの入力状態を調べる

マウスのボタンには、以下の Input 型の値が割り当てられています。

定数 対応するボタン
MouseL 左ボタン
MouseR 右ボタン
MouseM 中央ボタン
MouseX1 拡張ボタン 1
MouseX2 拡張ボタン 2
MouseX3 拡張ボタン 3
MouseX4 拡張ボタン 4
MouseX5 拡張ボタン 5

チュートリアル 33. のキーボードと同様に、押した瞬間であるかを .down(), 押し続けているかを .pressed(), 離した瞬間であるかを .up() を使って bool 値で取得できます。

関数 押していないとき 押した瞬間 押し続けている 離した瞬間 離し続けている
.down() false ✔ true false false false
.pressed() false ✔ true ✔ true false false
.up() false false false ✔ true false
# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		ClearPrint();
		Print << MouseL.pressed();
		Print << MouseM.pressed();
		Print << MouseR.pressed();
	}
}

34.5 マウスのボタン入力をキャンセルする

現在のフレーム内で、以降のマウスのボタンの入力をキャンセルするには、Input のメンバ関数 .clearInput() を呼びます。ボタンなどの UI が重なっているときに、背面に入力が伝わらないようにするときなどに使います。

MouseLMouseR を同時にキャンセルする場合は Mouse::ClearLRInput() が使えます。

# include <Siv3D.hpp>

class MyButton
{
public:

	MyButton() = default;

	explicit MyButton(const Rect& rect)
		: m_rect{ rect } {}

	bool update() const
	{
		if (m_rect.leftClicked())
		{
			MouseL.clearInput();
			return true;
		}
	}

	void draw() const
	{
		if (m_rect.mouseOver())
		{
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		m_rect.draw().drawFrame(1, 0, Palette::Black);
	}

private:

	Rect m_rect{ 0, 0, 0, 0 };
};

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
	const MyButton button0{ Rect{ 100, 100, 200, 100 } };
	const MyButton button1{ Rect{ 150, 150, 200, 100 } };

	while (System::Update())
	{
		if (button0.update())
		{
			Print << U"button0";
		}

		if (button1.update())
		{
			Print << U"button1";
		}

		button1.draw();
		button0.draw();
	}
}

34.6 ボタンが押されていた時間を調べる

Input.pressedDuration() は、その入力が押され続けている時間を Duration 型の値で返します。

押され続けている時間は .up()true になるフレームまで有効です。.up() されたときに .pressedDuration() を調べると、そのボタンが離されるまで何秒間押され続けていたかを取得できます。

# include <Siv3D.hpp>

void Main()
{
    while (System::Update())
    {
        ClearPrint();
        Print << MouseL.pressedDuration();
		Print << MouseM.pressedDuration();
		Print << MouseR.pressedDuration();
    }
}

34.7 ダブルクリックを判定する

次のようなクラスを作成して、ダブルクリックを判定することができます。

# include <Siv3D.hpp>

class DoubleClick
{
public:

	void update()
	{
		if (m_step == 3)
		{
			m_step = 0;
		}

		if (MouseL.down())
		{
			if (m_step == 0)
			{
				m_step = 1;
			}
			else if (m_step == 2)
			{
				if (const uint64 d = (Time::GetMillisec() - m_previousTimeMillisec);
					d < DoubleClickThresholdMillisec)
				{
					m_step = 3;
				}
				else
				{
					m_step = 1;
				}
			}
		}

		if (m_step == 0)
		{
			return;
		}

		if (not Cursor::Delta().isZero())
		{
			m_step = 0;
		}

		if ((m_step == 1) && MouseL.up())
		{
			if (MouseL.pressedDuration() < Milliseconds{ MaxClickTimeMillisec })
			{
				m_step = 2;
				m_previousTimeMillisec = Time::GetMillisec();
			}
			else
			{
				m_step = 0;
			}
		}
	}

	[[nodiscard]]
	bool doubleClicked() const noexcept
	{
		return (m_step == 3);
	}

private:

	// 1 回目のクリックの長さ(ミリ秒)
	static constexpr int32 MaxClickTimeMillisec = 500;

	// 1 回目のクリックと 2 回目のクリックの最大間隔(ミリ秒)
	static constexpr int32 DoubleClickThresholdMillisec = 500;

	int32 m_step = 0;

	int64 m_previousTimeMillisec = 0;
};

void Main()
{
	DoubleClick dc;

	while (System::Update())
	{
		// 毎フレーム 1 回必ず呼ぶ
		dc.update();

		// ダブルクリックされたら
		if (dc.doubleClicked())
		{
			Print << U"double click";
		}
	}
}

34.8 すべてのマウスボタン入力を取得する

Mouse::GetAllInputs() は、.down(), .pressed(), .up() のいずれかが true になっている、アクティブなマウスボタンの一覧を Array<Input> で返します。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		ClearPrint();

		// down() / pressed() / up() のいずれかが true になっているマウスボタン一覧を取得
		const Array<Input> buttons = Mouse::GetAllInputs();

		for (const auto& button : buttons)
		{
			Print << button.name() << (button.pressed() ? U" pressed" : U" up");
		}
	}
}

34.9 マウスホイールの回転量を取得する

直前のフレームからのマウスホイールのスクロール量は、Mouse::Wheel() によって double 型で取得できます。水平ホイールのスクロール量は、Mouse::WheelH() によって double 型で取得できます。

マウスホイールのスクロール量はフレームレートに依存しないため、Scene::Delta() で調整する必要はありません。

# include <Siv3D.hpp>

void Main()
{
	Vec2 pos = Scene::Center();

	while (System::Update())
	{
		ClearPrint();

		// マウスホイールのスクロール量
		Print << Mouse::Wheel();

		// マウスの水平ホイールのスクロール量
		Print << Mouse::WheelH();

		pos.y -= (Mouse::Wheel() * 10);
		pos.x += (Mouse::WheelH() * 10);

		RectF{ Arg::center = pos, 200 }.draw();
	}
}

34.10 マウスカーソルがクライアント領域上にあるかを調べる

Cursor::OnClientRect() は、マウスカーソルがウィンドウのクライアント領域上にある場合 true を、それ以外の場合は false を返します。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		ClearPrint();

		// マウスカーソルがウィンドウのクライアント領域上にあるかを表示
		Print << Cursor::OnClientRect();

		if (Cursor::OnClientRect())
		{
			Scene::SetBackground(ColorF{ 0.4 });
		}
		else
		{
			Scene::SetBackground(ColorF{ 0.2 });
		}
	}
}

34.11 マウスカーソルを指定した位置に移動させる

Cursor::SetPos() を使うと、指定した位置にマウスカーソルを移動できます。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		ClearPrint();
		Print << Cursor::Pos();

		if (SimpleGUI::Button(U"center", Vec2{ 100, 20 }))
		{
			// マウスカーソルをシーンの中心に移動させる
			Cursor::SetPos(Scene::Center());
		}
	}
}

34.12 マウスカーソルの移動を制限する (Windows 版)

Windows 版では、Cursor::ClipToWindow(true) を呼ぶとマウスカーソルが移動できる領域をウィンドウのクライアント領域上に制限できます。制限を解除するには Cursor::ClipToWindow(false) を呼びます。

# include <Siv3D.hpp>

void Main()
{
	bool clip = false;

	while (System::Update())
	{
		ClearPrint();
		Print << Cursor::Pos();

		if (SimpleGUI::CheckBox(clip, U"clip", Vec2{ 100, 20 }))
		{
			if (clip)
			{
				// マウスカーソルの移動をウィンドウのクライアント領域上に制限
				Cursor::ClipToWindow(true);
			}
			else
			{
				// 制限を解除
				Cursor::ClipToWindow(false);
			}
		}
	}
}

34.13 マウスカーソルのスタイルを変更する(標準スタイル)

Cursor::RequestStyle(style) によって、そのフレームにおけるマウスカーソルのスタイルを変更できます。style には下記の CursorStyle のいずれかを指定します。

CursorStyle 説明
CursorStyle::Arrow 矢印(デフォルト)
CursorStyle::IBeam I マーク
CursorStyle::Cross 十字のマーク
CursorStyle::Hand 手のアイコン
CursorStyle::NotAllowed 禁止のマーク
CursorStyle::ResizeUpDown 上下のリサイズ
CursorStyle::ResizeLeftRight 左右のリサイズ
CursorStyle::ResizeNWSE 左上 - 右下のリサイズ
CursorStyle::ResizeNESW 右上 - 左下のリサイズ
CursorStyle::ResizeAll 上下左右方向のリサイズ
CursorStyle::Hidden 非表示
CursorStyle::Default Arrow と同じ

Cursor::RequestStyle() はそのフレームのみの変更であり、次のフレームでは元のスタイルに戻ります。マウスカーソルのスタイルを継続的に変更したい場合は、毎フレーム Cursor::RequestStyle() を呼ぶ必要があります。

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(Palette::White);

	const ColorF buttonColor{ 0.2, 0.6, 1.0 };
	const Circle button{ 400, 300, 60 };
	Transition press{ 0.05s, 0.05s };

	while (System::Update())
	{
		const bool mouseOver = button.mouseOver();

		// 円の上にマウスカーソルがあれば
		if (mouseOver)
		{
			// マウスカーソルを手の形にする
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		press.update(button.leftPressed());

		const double t = press.value();

		button.movedBy(Vec2{ 0, 0 }.lerp(Vec2{ 0, 4 }, t))
			.drawShadow(Vec2{ 0, 6 }.lerp(Vec2{ 0, 1 }, t), (12 - t * 7), (5 - t * 4))
			.draw(buttonColor);
	}
}

34.14 マウスカーソルのスタイルを変更する(カスタム画像)

Image クラス(チュートリアル 53. 参照)で作成した任意の画像をマウスカーソルとして使うことができます。Cursor::RegisterCustomCursorStyle(name, image, hotSpot) で、画像を name という名前で登録します。hotSpot には、画像中のクリック位置を指定します。登録した画像は Cursor::RequestStyle(name) で指定できます。名前が異なれば複数のカスタムカーソルを登録できます。

# include <Siv3D.hpp>

Image CreateCursorImage()
{
	Image image{ 32, 32, Palette::White };

	for (int32 y = 0; y < image.height(); ++y)
	{
		for (int32 x = 0; x < image.width(); ++x)
		{
			image[y][x] = ColorF{ (x / 31.0), (y / 31.0), 1.0 };
		}
	}

	return image;
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// カスタムカーソルを登録する。画像中の (0, 0) がクリック位置
	Cursor::RegisterCustomCursorStyle(U"MyCursor", CreateCursorImage(), Point{ 0, 0 });

	const Circle circle{ 400, 300, 100 };

	while (System::Update())
	{
		Cursor::RequestStyle(U"MyCursor");

		circle.draw(circle.mouseOver() ? Palette::Orange : Palette::White);
	}
}