Play Siv3D!

ゲームとメディアアートのための C++ ライブラリ「Siv3D」

ゲームとメディアアートのための C++ ライブラリ「Siv3D」

ゲームとメディアアートのための C++ ライブラリ「Siv3D」

C++ Advent Calendar 2015, 25 日目の記事です。

年末年始は C++ Advent Calendar を振り返って、テンプレートメタプログラミングの腕を磨いたり、GPGPU に挑戦したり、Boost の知らなかった機能を試すのも楽しいでしょう。そしてまた、Siv3D を使ってコンピューターを遊びつくすのも面白い選択肢の1つです。

45 秒で始められる

豊富な機能と優れた性能にもかかわらず、Siv3D の導入はとても簡単です。このサイトから 50MB の SDK をダウンロードし、ワンクリックのインストーラを実行するだけで、グラフィックスも、音楽再生も、画像処理も、カメラやマイクといった入力デバイスも、数行の C++ コードでコントロールできる贅沢な開発環境が手に入ります。


▲ ダウンロードから 45 秒で最初のサンプルをビルド・実行

活用事例

2015 年夏にリリースされた現バージョン Siv3D June 2015 v2 は 2,100 ダウンロード、専門学校での授業、ゲームコンテストへの応募作品、大学の研究のビジュアライゼーション、ロボットの操作など、幅広い分野で活用されています。

東京ゲームショウ 2015

SIGGRAPH 2015

2015 国際ロボット展

サンプルプログラム

Siv3D でどんなことができるのでしょうか。簡単なサンプルをいくつか紹介します。
驚く方もいるかもしれませんが、ここに載っている数行~数十行のコードだけでアプリケーションが動きます。
[NEW] は、2016 年 1 月 11 日に登場する最新版 Siv3D January 2016 の新機能です。

図形の表示とあたり判定

# include <Siv3D.hpp>

void Main()
{
	const Rect rect(20, 20, 200, 100);

	const Circle circle(150, 300, 100);

	const Polygon star
	{
		{ 430, 100 }, { 470, 240 },
		{ 610, 240 }, { 505, 325 },
		{ 545, 460 }, { 430, 380 },
		{ 315, 460 }, { 355, 325 },
		{ 250, 240 }, { 390, 240 }
	};

	while (System::Update())
	{
		const Circle player(Mouse::Pos(), 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();
	}
}
リアルタイム電卓 [NEW]

# include <Siv3D.hpp>

void Main()
{
	Graphics::SetBackground(Color(240));
	
	const Font font(40);

	String expression;

	while (System::Update())
	{
		Input::GetCharsHelper(expression);

		font(expression).draw(10, 10, Palette::Black);

		if (const auto result = EvaluateOpt(expression))
		{
			font(result.value()).draw(10, 250, Palette::Black);
		}
	}
}
マイク入力音声の解析

# include <Siv3D.hpp>

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

	Recorder recorder(0, 5s, RecordingFormat::S44100, true);

	recorder.start();

	while (System::Update())
	{
		const auto fft = FFT::Analyze(recorder);

		for (auto i : step(640))
		{
			RectF(i, Window::Height(), 1, -Pow(fft.buffer[i], 0.5) * 500).draw(HSV(i));
		}

		const int mouseX = Mouse::Pos().x;

		Rect(mouseX, 0, 1, Window::Height()).draw();

		font(L"{:.1f}Hz"_fmt, fft.resolution() * mouseX).draw(Mouse::Pos().moveBy(0, -50));
	}
}
ブロック崩し

# include <Siv3D.hpp>

void Main()
{
	const Size blockSize(40, 20);
	const double speed = 8.0;
	Rect wall(60, 10);
	Circle ball(320, 400, 8);
	Vec2 ballSpeed(0, -speed);

	Array<Rect> blocks;
	for (auto p : step({ Window::Width() / blockSize.x , 5}))
	{
		blocks.emplace_back((p*blockSize).moveBy(0, 60), blockSize);
	}

	while (System::Update())
	{
		ball.moveBy(ballSpeed);
		wall.setCenter(Mouse::Pos().x, 420);

		for (auto it = blocks.begin(); it != blocks.end(); ++it)
		{
			if (it->intersects(ball))
			{
				(it->bottom.intersects(ball) || it->top.intersects(ball)
					? ballSpeed.y : ballSpeed.x) *= -1;

				blocks.erase(it);
				break;
			}
		}

		for (auto const& block : blocks)
		{
			block.stretched(-1).draw(HSV(block.y - 40));
		}

		if (ball.y<0 && ballSpeed.y<0)
		{
			ballSpeed.y *= -1;
		}

		if ((ball.x < 0 && ballSpeed.x < 0) || (Window::Width() < ball.x && ballSpeed.x > 0))
		{
			ballSpeed.x *= -1;
		}

		if (ballSpeed.y > 0 && wall.intersects(ball))
		{
			ballSpeed = Vec2((ball.x - wall.center.x) / 8, -ballSpeed.y).normalized()*speed;
		}

		ball.draw();
		wall.draw();
	}
}
Twitter クライアント

# include <Siv3D.hpp>

void Main()
{
	Graphics::SetBackground(Color(160, 200, 100));
	
	// Consumer Key (API Key)
	const String API_key = L"XXXXXXXXXXXXXXXXXXXXXXXXXXXX";
	
	// Consumer Secret (API Secret)
	const String API_secret = L"XXXXXXXXXXXXXXXXXXXXXXXXXXXX";

	experimental::TwitterClient twitter(API_key, API_secret);
	twitter.openTokenPage();

	GUI guiAuth(GUIStyle::Default);
	guiAuth.setTitle(L"PIN の入力");
	guiAuth.addln(L"PIN", GUITextField::Create(7));
	guiAuth.add(L"auth", GUIButton::Create(L"認証"));
	guiAuth.setPos(40, 40);

	GUI guiTweet(GUIStyle::Default);
	guiTweet.setTitle(L"ツイート");
	guiTweet.addln(L"text", GUITextArea::Create(6, 24, 140, false));
	guiTweet.add(L"tweet", GUIButton::Create(L"ツイート"));
	guiTweet.setPos(40, 200);

	while (System::Update())
	{
		guiAuth.button(L"auth").enabled = (guiAuth.textField(L"PIN").text.length == 7);
		guiTweet.button(L"tweet").enabled = !guiTweet.textArea(L"text").text.isEmpty;

		if (guiAuth.button(L"auth").pushed && twitter.verifyPIN(guiAuth.textField(L"PIN").text))
		{
			guiAuth.release();
			guiTweet.textArea(L"text").enabled = true;
		}

		if (guiTweet.button(L"tweet").pushed)
		{
			twitter.tweet(guiTweet.textArea(L"text").text);

			guiTweet.textArea(L"text").setText(L"");
		}
	}
}
対称定規

# include <Siv3D.hpp>

void Main()
{
	const int N = 12;

	Window::Resize(1280, 720);

	Image image(Window::Size(), Color(20, 40, 60));

	DynamicTexture texture(image);

	Mouse::SetTransform(Mat3x2::Translate(Window::Center()));

	while (System::Update())
	{
		if (Input::MouseL.pressed)
		{
			for (auto i : step(N))
			{
				Circular cs[2] = { Input::MouseL.clicked ? Mouse::Pos() : Mouse::PreviousPos(), Mouse::Pos() };

				for (auto& c : cs)
				{
					c.theta = i % 2 ? -c.theta - TwoPi / N * (i - 1) : c.theta + TwoPi / N * i;
				}

				Line(cs[0], cs[1]).moveBy(Window::Center()).overwrite(image, 2, HSV(System::FrameCount(), 0.5, 1.0));
			}

			texture.tryFill(image);
		}
		else if (Input::MouseR.clicked)
		{
			image.fill(Color(20, 40, 60));

			texture.fill(image);
		}

		texture.draw();
	}
}
Leap Motion

# include <Siv3D.hpp>
# include <Siv3DAddon/LeapMotion.hpp>

void Main()
{
	Window::Resize(1024, 640);

	LeapMotion::RegisterAddon();

	const Font font(36), font2(18);

	while (System::Update())
	{
		Graphics3D::FreeCamera();

		for (const auto& hand : LeapMotion::Hands())
		{
			const Vec3 scaledPos = hand.pos*0.05;

			Sphere(scaledPos, 0.5).draw(HSV(hand.id * 30).toColor());

			const Vec2 screenBase = Graphics3D::ToScreenPos(scaledPos).xy;

			font(hand.isLeft ? L"Left" : L"Right").drawCenter(screenBase.movedBy(20, -160));

			const Circle pinchCircle(screenBase.movedBy(-60, -260), 60);

			const Circle grabCircle(screenBase.movedBy(100, -260), 60);

			pinchCircle.draw(HSV(hand.id * 30).toColor(60));

			pinchCircle.drawPie(0.0, hand.pinchStrength * TwoPi, HSV(hand.id * 30));

			grabCircle.draw(HSV(hand.id * 30).toColor(60));

			grabCircle.drawPie(0.0, hand.grabStrength * TwoPi, HSV(hand.id * 30));

			font2(L"Pinch").drawCenter(pinchCircle.center);

			font2(L"Grab").drawCenter(grabCircle.center);

			for (const auto& finger : hand.fingers)
			{
				for (const auto& joint : finger.joints)
				{
					Sphere(joint*0.05, 0.2).draw(HSV(finger.id * 10).toColor());
				}
			}
		}

		Box(1).draw();
	}
}
MIDI ビジュアライザー [NEW]


文字や記号の認識 [NEW]


エレガントな C++

Siv3D は C++ の最新規格やテクニックを駆使して、コードを読み書きしやすくする仕組みを提供しています。

ループのユーティリティ
# include <Siv3D.hpp>

void Main()
{
	// 0 ~ 9 を表示
	for (auto i : step(10))
	{
		Println(i);
	}

	// 10 ~ 0 を表示
	for (auto i : step_to(10, 0, -1))
	{
		Println(i);
	}

	// 横 3 個、縦 4 個、計 12 個の四角形を表示(本来なら二重ループ)
	for (auto p : step({ 3, 4 }))
	{
		Rect(p.x * 10, p.y * 10, 8, 8).draw();
	}

	WaitKey();
}
文字列フォーマット
# include <Siv3D.hpp>

void Main()
{
	Println(L"{}+{}={}"_fmt, 1, 1, 1+1); // 1+1=2

	Println(L"{2}/{1}/{0}"_fmt, 2015, 12, 25); // 25/12/2015 

	Println(L"{:.2f}, {:.5f}"_fmt, Pi, Pi); // 3.14, 3.14159

	Println(L"This is {}"_fmt, Window::GetTitle()); // This is Siv3D App

	WaitKey();
}
Optional
# include <Siv3D.hpp>

void Main()
{
	const CSVReader csv(L"test.csv");

	// CSV ファイルの 2 行, 3 列目の文字列を double 型に変換
	if (const auto cell = csv.getOpt<double>(2, 3))
	{
		// 変換できたら値を取得
		const double value = cell.value();
	}
}
ユーザ定義リテラル [NEW]
# include <Siv3D.hpp>

void Main()
{
	Stopwatch s(true);

	while (System::Update())
	{
		// 開始から 3 秒未満なら
		if (s.elapsed() < 3s)
		{
			// 四角形を 30° 傾けて描画
			Rect(100, 100, 50).rotated(30_deg).draw();
		}
	}
}

コンピューターで遊ぶなら C++ が一番!

となる日を夢見て Siv3D を開発しています。
知れば知るほど奥が深い Siv3D で、寒い冬をエキサイティングに過ごしてみてはいかがでしょうか。
Siv3D Advent Calendar 2015 も面白い読み物です。
Siv3D に関する質問や相談、開発の協力は Slackユーザ助け合い所, Twitter でお気軽に声をかけてください。



C++ Advent Calendar 2015 参加者・読者の皆様ありがとうございました。
良いクリスマス、年末年始をお過ごしください。