Skip to content

Siv3D August 2016 の新機能サンプル

Ryo Suzuki edited this page Dec 29, 2021 · 19 revisions

⚠ このページは古い Siv3D (August 2016 v2) のリファレンスのアーカイブです。
最新版の Siv3D (OpenSiv3D) については OpenSiv3D Web サイト をご覧ください。


文章読み上げ

Println() と同じように Say() を使うと、テキストやデータを読み上げてくれます。 より細かい制御や設定を行いたい場合は Speech.hpp ヘッダをご覧ください。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		if (Input::KeyA.clicked)
		{
			Say(L"こんにちは。シブスリーティーの文章読み上げ機能です。");
		}
		else if (Input::KeyB.clicked)
		{
			Say(L"Today's lucky number is ", Random(100));
		}

		if (Speech::IsSpeaking())
		{
			const double t = Time::GetMillisec() * 0.002;

			Circle(Window::Center(), 40)
				.drawArc(t, 30_deg, 8)
				.drawArc(t + 120_deg, 30_deg, 8)
				.drawArc(t + 240_deg, 30_deg, 8);
		}
	}
}

3D 描画時のシャドウ

Deferred Rendering 時に、.drawShadow() で影を描画できます。 影をつくる光源の射影情報は Graphics3D::SetShadowLight() で設定します。
シャドウマップの解像度は Graphics3D::SetShadowMapResolution() で 16~8192 の範囲で設定します。デフォルトは 1024 です。

# include <Siv3D.hpp>

void Main()
{
	Graphics::SetBackground(Color(80, 160, 230));
	Graphics3D::SetAmbientLight(ColorF(0.3));

	const Texture textureGround(L"Example/grass.jpg", TextureDesc::For3D);
	const Mesh meshGround(MeshData::Plane(30, 30, { 6, 6 }));
	const Texture textureBox(L"Example/brick.jpg", TextureDesc::For3D);
	const Model model(L"Example/Well/Well.wavefrontobj");

	ShadowLight shadowLight;
	shadowLight.lookat.set(0, 0, 0);

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

		const double y = 30 + Sin(Time::GetMillisec() * 0.001) * 10;
		const double angle = Time::GetMillisec() * 0.0005;	
		const Vec3 lightPos = Cylindrical(25, angle, y);

		shadowLight.position.set(lightPos);
		Graphics3D::SetShadowLight(shadowLight);
		Graphics3D::SetLight(0, Light::Directional(lightPos.normalized()));

		meshGround.draw(textureGround);

		Sphere(0, 5, 0, 2).draw().drawShadow();

		model.draw(Mat4x4::Translate(5, 0, 0)).drawShadow(Mat4x4::Translate(5, 0, 0));

		for (auto i : step(6))
		{
			const Vec3 pos = Vec3(0, 0, 6) + Cylindrical(4, Radians(i*60), 0);

			Cylinder(pos, pos + Vec3(0, 4, 0), 0.2).draw(HSV(i*60, 0.6, 1.0)).drawShadow();
		}

		for (auto i : step(6))
		{
			Box(-5 + i * 2, 0.5, -5, 1).draw(textureBox).drawShadow();
		}
	}
}

視錐台

3D カメラに写っている範囲を表す視錐台を Camera 型から取得できます。
.intersects().contains() 関数により、BoxSphere, Triangle3D などの 3D 形状が視錐台と交差しているか、包含されているかを調べられます。

# include <Siv3D.hpp>

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

	Array<Box> boxes;

	for (auto i : step(100))
	{
		boxes.emplace_back(RandomVec3({ -30, 30 }, { -30, 30 }, { -30, 30 }), 3.0);
		boxes[i].rotation.rotateRollPitchYaw(Random(TwoPi), Random(TwoPi), Random(TwoPi));
	}

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

		for (auto& box : boxes)
		{
			box.rotation.rotateRollPitchYaw(0.01, 0.01, 0.01);
		}

		int32 count = 0;

		const ViewFrustum vf = Graphics3D::GetCamera().calcViewFrustum();

		for (const auto& box : boxes)
		{
			if (vf.intersects(box))
			{
				box.draw();

				++count;
			}
		}

		font(L"描画された Box: {}/{}"_fmt, count, boxes.size()).draw(10, 10, Palette::Orange);
	}
}

Transformer2D

2D 描画やマウスカーソルに対するスケーリングや移動などの座標変換を Push / Pop するクラスです。
Transformer2D オブジェクトのスコープが有効な間、設定した座標変換が、それまでの座標変換に乗算する形で適用されます。 第 3 引数に true を設定すると、マウスカーソルにも座標変換が適用されます。

# include <Siv3D.hpp>

void Main()
{
	const Font font(30);
	
	const Circle circle(200, 200, 100);
	
	const Rect rect(300, 300, 100);

	Stopwatch stopwatch(true);

	while (System::Update())
	{
		const double scale = Max(1.0 - stopwatch.ms() * 0.0001, 0.1);
		const double angle = stopwatch.ms() * 0.001;

		{
			const Transformer2D transformer(Mat3x2::Scale(scale, Window::Center()).rotate(angle, Window::Center()), true);

			circle.draw(circle.mouseOver ? Palette::Red : Palette::Yellow);

			rect.draw(rect.mouseOver ? Palette::Red : Palette::Yellow);

			font(L"Siv3D").draw(400, 200);
		}

		Rect(20, 20, 50).draw();
	}
}

ScalableWindow

マウス座標と 2D 描画の座標をウィンドウに合わせてスケーリングします。
ScalableWindow::Setup() で基準の解像度を設定し、基準の解像度を想定してプログラムを書けば対応できます。
Window::Width(), Window::Height(), Window::Size(), Window::Center() 等は、ウィンドウサイズ変更の影響を受けてしまうため、代わりに Window::BaseWidth(), Window::BaseHeight(), Window::BaseSize(), Window::BaseCenter() を使います。
縮小時に Vec2 型でマウスカーソルの座標を得たい場合には Mouse::PosF() を使います。

参考比較: https://github.com/Siv3D/Reference-JP/wiki/ドットお絵かき

# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
	ScalableWindow::Setup(640, 480);

	Graphics::SetBackground(Palette::White);

	const int32 dotSize = 40;

	Grid<int32> dots(Window::BaseWidth() / dotSize, Window::BaseHeight() / dotSize);

	while (System::Update())
	{
		{
			const auto transformer = ScalableWindow::CreateTransformer();

			for (auto p : step({ dots.width, dots.height }))
			{
				const Rect rect(p * dotSize, dotSize, dotSize);

				if (rect.leftClicked)
				{
					++dots[p.y][p.x] %= 4;
				}

				const Color color(240 - dots[p.y][p.x] * 70);

				rect.stretched(-1).draw(color);
			}
		}

		ScalableWindow::DrawBlackBars(HSV(40, 0.2, 0.9));
	}
}

正投影

Cmaera のパラメータを変更すると、3D 描画が正投影になります。詳しくは Camera.hpp ヘッダを確認してください。
現バージョンでは実験的な機能のため、Mouse::Ray() などの座標計算関連の機能とは連動しません。

# include <Siv3D.hpp>

void Main()
{
	Graphics3D::SetAmbientLight(ColorF(0.3));

	Camera camera(Vec3(0, 8, 0), Vec3(0, 0, 0), Vec3(0, 1, 0), -20, 20, -15, 15, 0.1, 200.0);

	Graphics3D::SetCamera(camera);

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

		for (auto p : step({ 21,21 }))
		{
			Box(-50 + p.x * 5, 1.5, 50 - p.y * 5, 3).draw(HSV(p.x * 10 + p.y * 3));
		}
	}
}

クリップボードの更新検知

最後に Clipboard::HasChanged() が呼ばれたあとに、クリップボードの中身が変更されたり消去されたりしたときに、true を返してそれを通知します。

# include <Siv3D.hpp>

void Main()
{
	while (System::Update())
	{
		if (Input::MouseR.clicked)
		{
			Clipboard::Clear();
		}

		if (Clipboard::HasChanged())
		{
			Println(L"HasChanged");
		}
	}
}

MeshData::Capsule()

MeshData で作成できる 3D 形状に、カプセル形状が追加されました。

# include <Siv3D.hpp>

void Main()
{
	Graphics::SetBackground(Color(80, 160, 230));
	Graphics3D::SetAmbientLight(ColorF(0.3));

	const Texture textureGround(L"Example/grass.jpg", TextureDesc::For3D);
	const Mesh meshGround(MeshData::Plane(30, 30, { 6, 6 }));

	const Mesh mesh(MeshData::Capsule(1, 1.5));

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

		meshGround.draw(textureGround);

		mesh.translated(0, 2, 0).draw().drawShadow();
	}
}

IME::SetCompositionWindowPos()

IME の日本語入力ウィンドウの表示位置を変更します。

# include <Siv3D.hpp>

void Main()
{
	Graphics::SetBackground(Color(160, 200, 100));

	GUI gui(GUIStyle::Default);
	gui.setTitle(L"タイトル");
	gui.add(L"text", GUITextField::Create(6));

	while (System::Update())
	{
		IME::SetCompositionWindowPos(gui.getPos());
	}
}

Box2D と CameraBox2D と ScalableWindiow

Box2D による物理演算を簡単に扱う機能です。
左クリックで長方形を出現させます。
CameraBox2D は、W/A/S/D キーやマウス右ボタン、ホイールで 2D カメラの移動やズームを操作できます。

# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
	ScalableWindow::Setup();
	CameraBox2D camera(Vec2(0, 0), 17.0);

	PhysicsWorld world;

	auto ground = world.createLineString(Vec2(0, 0), { Vec2(-20, 20), Vec2(-20, 10), Vec2(20, 0), Vec2(20, 20) }, none, none, PhysicsBodyType::Static);

	Array<PhysicsBody> bodies;

	bodies.push_back(world.createPolygon(Vec2(-8, 9), Geometry2D::CreateStar(1)));

	for (auto i : step(100))
	{
		bodies.push_back(world.createCircle(Vec2(0, 5 + i * 2), 0.5));
	}

	while (System::Update())
	{
		world.update();
		camera.update();
		{
			const auto t1 = camera.createTransformer();
			const auto t2 = ScalableWindow::CreateTransformer();

			if (Input::MouseL.clicked)
			{
				bodies.push_back(world.createRect(Mouse::PosF(), Vec2(1, 2)));
			}

			ground.draw();

			for (const auto& body : bodies)
			{
				body.draw(Palette::Skyblue);
			}
		}
		camera.draw(Palette::Orange);
	}
}

サンプルゲーム

Box2D 機能を使ったサンプル 3D ゲームです。
XInput 対応コントローラでも操作できます。

# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
	Window::Resize(1280, 720);
	Window::SetStyle(WindowStyle::Sizeable);
	Graphics::SetBackground(ColorF(0.3, 0.7, 1));
	Graphics3D::SetAmbientLight(ColorF(0.3));
	Graphics3D::SetFog(Fog::SquaredExponential(ColorF(0.3, 0.7, 1), 0.004));

	const PerlinNoise noise;
	Array<Vec2> points = { Vec2(500, -50), Vec2(-1, -50) };
	for (auto i : step(500))
	{
		points.emplace_back((i * 1) - 1, noise.octaveNoise(i*0.01, 4) * 24);
	}
	std::reverse(points.begin(), points.end());

	PhysicsWorld world;
	const auto geround = world.createLineString(Vec2(0, 0), points, PhysicsMaterial(0.0, 0.1, 0.6), none, PhysicsBodyType::Static);
	const auto car = world.createRect(Vec2(0, 1), RectF(-1, -0.1, 2, 3.3), PhysicsMaterial(0.04));
	auto wheel1 = world.createCircle(Vec2(-1, 0.35), 0.4, PhysicsMaterial(1.0, 0.1, 0.9));
	auto wheel2 = world.createCircle(Vec2(1, 0.4), 0.4, PhysicsMaterial(1.0, 0.1, 0.9));

	PhysicsWheelJoint spring1 = world.createWheelJoint(car, wheel1, wheel1.getPos(), { 0.0,1.0 }, WheelJointState(true, 20.0, 4.0));
	PhysicsWheelJoint spring2 = world.createWheelJoint(car, wheel2, wheel2.getPos(), { 0.0,1.0 }, WheelJointState(false, 20.0, 4.0));

	const Model model(L"Example/Well/Well.wavefrontobj");
	const Mesh groundMesh(MeshData::Polygon(Polygon(points), 200.0, { 0.1, 0.1 }, { 0, 0 }));
	TextureAsset::Register(L"Brick", L"Example/brick.jpg", TextureDesc::For3D);
	const Texture brick(L"Example/brick.jpg", TextureDesc::For3D);
	const Texture textureGround(L"Example/Ground.jpg", TextureDesc::For3D);
	const Texture textureParticle(L"Example/Particle.png", TextureDesc::For3D);

	const auto DrawWheel = [](const Vec2& pos, double angle) {
		Cylinder(0.4, 1.0, Quaternion::Yaw(angle).pitch(HalfPi)).asMesh().translated(pos.x, pos.y, 0)
			.draw(TextureAsset(L"Brick")).drawShadow();
	};

	Array<Particle> smokes;
	Graphics3D::SetDepthStateForward(DepthState::TestOnly);

	XInput xinput(0);
	xinput.setRightThumbDeadZone();

	Camera camera;
	Spherical s(15, 80_deg, -90_deg);
	double previousVY = 0.0;

	while (System::Update())
	{
		if (Abs(wheel1.getVelocity().x) > 0.1)
		{
			wheel1.applyForce({ Sign(wheel1.getVelocity().x) * -0.5,0 });
			wheel2.applyForce({ Sign(wheel2.getVelocity().x) * -0.5,0 });
		}
		else if (Abs(wheel1.getVelocity().x) > 0.01)
		{
			wheel1.applyForce({ Sign(wheel1.getVelocity().x) * -4,0 });
			wheel2.applyForce({ Sign(wheel2.getVelocity().x) * -4,0 });
		}

		if ((Input::KeyZ | xinput.buttonX).pressed)
		{
			spring1.setMotor(true, 30.0);
		}
		else if ((Input::KeyX | xinput.buttonA).pressed)
		{
			spring1.setMotor(true, -30.0);
		}
		else
		{
			spring1.setMotor(false);
		}

		s.phi -= xinput.rightThumbX * 0.02;
		s.theta = Clamp(s.theta - xinput.rightThumbY *0.02, 10_deg, 90_deg);

		world.update();

		const Vec3 carPos(car.getPos(), 0);
		camera.pos = Lerp(camera.pos, carPos + s, 0.1);
		camera.lookat = carPos;
		Graphics3D::SetCamera(camera);
		Graphics3D::SetShadowLight(ShadowLight(carPos + Vec3(10, 10, -10), carPos));

		const double currentVY = car.getVelocity().y;
		const double vi = Max(currentVY - previousVY, 0.0) / 1.0;
		previousVY = currentVY;
		xinput.setVibration(vi, vi + Abs(spring1.getMotorSpeed() * 0.003) + Abs(0.005 * car.getVelocity().x));

		if (vi > 0.005)
		{
			const Vec3 pos(carPos + Vec3(System::FrameCount() % 2 ? -0.5 : 1, -0.7, 0));
			smokes.push_back(Particle(pos, 1.5, ColorF(0.6)));
		}

		for (auto& smoke : smokes)
		{
			smoke.pos.moveBy(-0.02f, 0.04f, 0.02f, 0.0f);
			smoke.scaling *= 1.025f;
			smoke.color.w = Pow(Max((1.5f - smoke.pos.z) / 2.0f, 0.0f), 2.0f);
		}

		if (!smokes.empty() && smokes.front().pos.z > 1.5)
		{
			smokes.erase(smokes.begin());
		}

		groundMesh.rotated(HalfPi, 0, 0).draw(textureGround);
		DrawWheel(wheel1.getPos(), wheel1.getAngle());
		DrawWheel(wheel2.getPos(), wheel2.getAngle());

		model.draw(Mat4x4::Rotate(0, 0, car.getAngle()).translated(carPos))
			.drawShadow(Mat4x4::Rotate(0, 0, car.getAngle()).translated(carPos));

		Graphics3D::DrawParticlesForward(smokes, textureParticle);
	}
}

Siv3D について

  1. Siv3D の基本
  2. 図形を描く
  3. テクスチャを描く
  4. テキストを描く
  5. 文字列と数値の変換
  6. キーボード入力
  7. マウス入力
  8. サウンドの再生
  9. MIDI の再生
  10. ウィンドウと背景
  11. 図形のあたり判定
  12. 乱数
  13. ダイアログ
  14. ドラッグ & ドロップ
  15. アプリの状態
  16. テキストファイル
  17. INI, CSV, JSON
  18. バイナリファイル
  19. GUI
  20. アセット管理
  21. 画像編集
  22. Web カメラ
  23. マイク入力
  24. 経過時間の測定
  25. HSV カラー
  26. ファイルダウンロード
  27. 3D 描画
  28. 2D のレンダーステート
  29. 3D のレンダーステート
  30. パーティクル
  31. スクリーンショット
  32. アプリケーションの公開
  33. さらに学ぶには

表現テクニック集

入出力デバイス

開発のヒント

Clone this wiki locally