イベントタイマー

Siv3D Advent Calendar 2013, 11 日目の記事です。
今日は December 2013 で追加されたイベントタイマー機能を紹介します。

EventTimer は Timer の強化版で、設定したイベント時間の通過を教えてくれます。

イベントの設定は EventTimer::set(event, time)
あるイベントを通過したかどうかは EventTimer::onTriggered(event) で判定します。

EventTimer::clearEvents() するまでイベント情報は消えないので、EventTimer::restart() で何回でもイベントを発生させることができます。
Timer と違い、イベント通過判定のために毎フレーム EventTimer::update() する必要がある点だけ注意してください。

サンプル


# include <Siv3D.hpp>

void Main()
{
	Graphics::SetBackground(Palette::Gray);

	EventTimerMillisec eventTimer;
	eventTimer.setEvent(L"Red", 2000);
	eventTimer.setEvent(L"Blue", 6000);
	eventTimer.setEvent(L"Green", 4000);

	eventTimer.start();

	while (System::Update())
	{
		const unsigned elapsed = eventTimer.update();

		if (eventTimer.onTriggered(L"Red"))
		{
			Println(L"Red: ", elapsed);

			Graphics::SetBackground(Palette::Red);
		}
		else if (eventTimer.onTriggered(L"Green"))
		{
			Println(L"Green: ", elapsed);

			Graphics::SetBackground(Palette::Green);
		}
		else if (eventTimer.onTriggered(L"Blue"))
		{
			Println(L"Blue: ", elapsed);

			Graphics::SetBackground(Palette::Blue);
		}

		if (Input::MouseR.clicked)
		{
			Println(L"restart");

			Graphics::SetBackground(Palette::Gray);

			eventTimer.restart();
		}
	}
}

色のいろんな作り方

Siv3D Advent Calendar 2013, 10 日目の記事です。
今日は Siv3D での色の作り方を紹介します。

Color 型のコンストラク

Color(Palette)

140 種類の Web カラーネーム が定義されています。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw(Palette::Orange);
	}
}
Color(r, g, b)
# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw({ 80, 240, 120 });
	}
}
Color(r, g, b, a)
# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Rect(100, 100).draw();

		Circle(100, 100, 100).draw({ 80, 240, 120, 80 });
	}
}
Color(Palette, a)
# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Rect(100, 100).draw();

		Circle(100, 100, 100).draw({ Palette::Red, 80 });
	}
}
Color(rgb)
# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw(Color(120));

		Circle(100, 300, 100).draw(Color(220));
	}
}
Color(rgb, a)
# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Rect(100, 100).draw();

		Circle(100, 100, 100).draw({ 0, 180 });
	}
}
Color(String)

3 桁もしくは 6 桁の 16 進数カラーコードで色を表現します。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw(Color(L"#F00"));

		Circle(100, 300, 100).draw(Color(L"#2580D0"));
	}
}

そのほかの便利な機能として
[0.0, 1.0] で各成分を表す ColorF 型

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw(ColorF(0.1, 0.6, 0.9));

		Circle(100, 200, 100).draw(ColorF(0.8));

		Circle(100, 300, 100).draw(ColorF(0.5, 0.4));
	}
}

HSV 表色系で色を表現する HSV

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw(HSV(60));

		Circle(100, 200, 100).draw(HSV(120, 0.5, 1.0));

		Circle(100, 300, 100).draw(HSV(240, 1.0, 0.8));
	}
}

Color(255,255,255,alpha) を返す Alpha(alpha) 関数

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw(HSV(0));

		Circle(100, 200, 100).draw(Alpha(120));
	}
}

HSV 型の演算

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		Circle(100, 100, 100).draw(Color(0, 64, 160));

		Circle(100, 200, 100).draw(Color(0, 64, 160) + HSV(80, 0, 0));

		Circle(100, 300, 100).draw(Color(0, 64, 160) + HSV(0, 0.5, 0));

		Circle(100, 400, 100).draw(Color(0, 64, 160) + HSV(0, 0, 0.5));
	}
}

といった機能があります。
必要な色を最小限の書き方で得られるようになると便利です。

HSV 表色系のサンプル


マウスを動かすと色相が変化します。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		for (int y = 0; y <= 10; ++y)
		{
			for (int x = 0; x <= 10; ++x)
			{
				const Color color = HSV(Mouse::Pos().x, x / 10.0, 1.0 - y / 10.0);

				Rect(20 + 40 * x, 20 + 40 * y, 40, 40).draw(color);
			}
		}
	}
}

ペンタブレットを使う

Siv3D Advent Calendar 2013, 9 日目の記事です。
今日はペンタブレット入力の機能を紹介します。

※この記事は Siv3D December 2013 に基づいています。最新の Siv3D のコードは
リファレンス | ペンタブレット で入手できます。

主要な関数は 3 つです。
地面に対するペンの傾きを取得する Pentablet::DegreeXZ()
垂直な軸まわりのペンの回転角度を取得する Pentablet::DegreeY()
筆圧を取得する Pentablet::Pressure()

座標やクリックは Mouse::Pos() や Input::MouseL / MouseR に反映されます。

# include <Siv3D.hpp>

void Main()
{
	const Font font(30);

	if (!Pentablet::SupportsPressure())
	{
		return; // 筆圧測定に対応していない
	}

	while (System::Update())
	{
		// 地面に対する傾き [0, 90]
		const int degreeXZ = Pentablet::DegreeXZ();

		// 垂直な軸まわりの回転角度 [0, 359)
		const int degreeY = Pentablet::DegreeY();

		// 筆圧 [0.0, 1.0]
		const double pressure = Pentablet::Pressure();

		Circle(Mouse::Pos(), pressure * 200).draw(Palette::Skyblue);

		Circle(Mouse::Pos() + Circular(pressure * 200, Radians(degreeY)), 20).draw(Palette::Red);

		font.draw(Format(
			L"XZ: ", degreeXZ, L'\n',
			L"Y:", degreeY, L'\n',
			L"pressure: ",pressure));
	}
}
サンプルゲーム

筆圧に応じて水色のリングの大きさが変わります。
リングとリングの間に収まるちょうどいい状態を維持するとクリアです。

# include <Siv3D.hpp>

Circle CreateCircle()
{
	return{ Random(80.0, 560.0), 
			Random(80.0, 400.0), 
			Random(10.0, 80.0) };
}

void Main()
{
	const Sound sound(L"Example/Sound.mp3");

	const Font font(30);

	if (!Pentablet::SupportsPressure())
	{
		return;
	}

	Circle circle = CreateCircle();

	const int maxCount = 15;

	int count = 0;

	while (System::Update())
	{
		const Circle outerCircle = circle.stretched(20.0);

		const double pressure = Pentablet::Pressure()*100.0;

		const Circle pressureCircle(Mouse::Pos(), pressure);

		if (circle.r <= pressure && pressure <= outerCircle.r
			&& circle.within(pressureCircle)
			&& pressureCircle.within(outerCircle))
		{
			++count;
		}
		else
		{
			count = 0;
		}

		if (count == maxCount)
		{
			count = 0;

			sound.playMulti();

			circle = CreateCircle();
		}

		circle.drawFrame(1.0, 1.0);
		
		outerCircle.drawFrame(1.0, 1.0);
		
		pressureCircle.drawFrame(1.0, 1.0, Palette::Skyblue);

		pressureCircle.drawPie(0.0, TwoPi / maxCount*count, Palette::Skyblue);
	}
}

文字の TextureRegion を取得して「大破」する

Siv3D Advent Calendar 2013, 8 日目の記事です。
今日は December 2013 で追加された機能の 1 つ、Font から指定した文字の TextureRegion を取得する機能を紹介します。

これまでの Font を使った文字描画では、文字単位での描画や変形をサポートしていませんでしたが、December 2013 で追加された Font::getTexture(wchar_t) メソッドを使うと、Font 内部で確保された Texture 上にある、指定した文字の TextureRegion にアクセスできます。

TextureRegion というのはあまり目にしないクラスですが、例えばよく使う以下のようなコードでも TextureRegion オブジェクトが作成されています。

# include <Siv3D.hpp>

void Main()
{
	const Texture texture(L"Example/Windmill.png");

	while (System::Update())
	{
		//texture(0, 0, 100, 100).draw() を分解

		const TextureRegion t = texture(0, 0, 100, 100);

		t.draw();
	}
}

このように、もとになる Texture があり、その一部分を切り抜いたものが TextureRegion です。

さっそく Font::getTexture(wchar_t) を使ったサンプルを見てみましょう。


# include <Siv3D.hpp>

void Main()
{
	const Font font(100);

	while (System::Update())
	{
		const TextureRegion t = font.getTexture(L'あ');

		t.mirror().draw();

		font.getTexture(L'い').rotate(0.6).draw(100, 200);

		font.getTexture(L'う').scale(1.0,0.5).draw(300, 150);

		font.getTexture(L'え').flip().draw(300, 300);
	}
}

文字ごとに自由な変形を適用できます。
ただし、元のフォントのサイズが小さい場合は拡大した際にドットが荒くなってしまうので注意してください。

さて、今回の機能を使って最近流行りのゲームの文句を真似てみました。
結果画像がこれです。

# include <Siv3D.hpp>

void Main()
{
	Window::ShowCursor(false);

	const Font font(72, L"MS 明朝");

	const Color color(255, 101, 93);

	Graphics::SetBackground({ 150, 200, 220 });

	std::vector<Vec2> pts;

	for (int i = 0; i < 36; ++i)
	{
		pts.push_back(Circular(90 + (i % 2) * 20, TwoPi / 36 * i));
	}

	const Polygon back(pts);

	while (System::Update())
	{
		const Vec2 center = Mouse::Pos();

		const Vec2 taiPos = center - Vec2(50, 50);

		const Vec2 haPos = center + Vec2(55, 20);

		back.draw(center);

		back.drawFrame(center, 8.0, color);

		for (int i = 0; i < 16; ++i)
		{
			const Vec2 offset = Circular(5, TwoPi / 16 * i);

			font.getTexture(L'大').rotate(0.3).drawAt(taiPos + offset, Palette::Black);
		}

		font.getTexture(L'大').rotate(0.3).drawAt(taiPos, color);

		for (int i = 0; i < 16; ++i)
		{
			const Vec2 offset = Circular(5, TwoPi / 16 * i);

			font.getTexture(L'破').scale(0.8).rotate(0.3).drawAt(haPos + offset, Palette::Black);
		}

		font.getTexture(L'破').scale(0.8).rotate(0.3).drawAt(haPos, color);
	}
}

スクリーンショットを保存する

Siv3D Advent Calendar 2013, 7 日目の記事です。
今日は Siv3D のスクリーンショットに関する機能を紹介します。


スクリーンショットを指定したファイル名で保存する

Graphics::SaveScreenshot(path) を呼ぶと、現在のフレームの描画が完了したときに、指定したファイル名でスクリーンショットが保存されます。使用できる拡張子は Image::save() と同様 bmp, png, jpg, jpeg, dds, gif, tif, tiff, tga, webp, jp2, ppm です。
第一引数のファイル名を省略した場合は Screenshot フォルダが作られ、その中にユニークなファイル名で保存されます。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		if (Input::KeyS.clicked)
		{
			Graphics::SaveScreenshot(L"screenshot.png");
		}
	}
}

スクリーンショットを Image 形式で取得する

スクリーンショットをアプリケーションで使うときは、ファイルを介さずに直接 Image 形式で取得しましょう。直前のフレームで Graphics::RequestScreenCapture() を呼び、次のフレームで Graphics::ReceiveScreenCapture() から Image を受け取ります。

# include <Siv3D.hpp>

void Main()
{
	bool request = false;

	while (System::Update())
	{
		if (request)
		{
			request = false;

			const Image image = Graphics::ReceiveScreenCapture();
		}

		if (Input::KeyS.clicked)
		{
			Graphics::RequestScreenCapture();

			request = true;
		}
	}
}

Print Screen キーでスクリーンショットを保存する

Print Screen キーを押したときに自動でスクリーンショットを保存するよう設定できます。プレイ画像をいつでも好きな時に撮りたいときはこの機能を利用しましょう。

設定手順は engine フォルダにある config.ini の

bSaveScreenshot = false

bSaveScreenshot = true

に変更するだけです。
保存先は Graphics::SaveScreenshot() と同じく Screenshot フォルダです。

Siv3D で音楽プレイヤーを作る

Siv3D Advent Calendar 2013, 6 日目の記事です。
今日は Siv3D の GUI と Sound 機能を使って音楽プレイヤーを作ってみました。

このプログラムのソースコードと解説は Qiita | Siv3D で音楽プレイヤーを作る に掲載しています。GUI の使い方に慣れる良いチュートリアルになると思います。

開発中の機能 | 手書き文字認識

Siv3D Advent Calendar 2013, 5 日目の記事です。
今日は開発中の機能の 1 つ「手書き文字認識」を紹介します。

このように、書いている途中でも候補の文字を高速に取得できます。

判定には機械学習アルゴリズムを用いた文字認識エンジン Zinnia を使っています。
学習データを用意することで、オリジナルの図形や文字に対応させることもできます。

この機能は、早ければ次のアップデートで実験的機能として搭載する予定です。
学習データを作るツールも用意しようと思っています。

計算問題の答えをマウスやペンで入力させる脳トレのようなゲーム、紋章をマウスで描くと魔法が使える RPG ・・・, 使い道を想像するだけでも楽しいですね。