Siv3D August 2016 の新機能サンプル - Siv3D/Reference-JP GitHub Wiki
⚠ このページは古い 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);
}
}
}
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()
関数により、Box
や Sphere
, 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);
}
}
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();
}
}
マウス座標と 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 で作成できる 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 の日本語入力ウィンドウの表示位置を変更します。
# 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 は、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);
}
}