Open3
Siv3D v0.6.11 新規追加・更新サンプル
2D の軌跡の描画
1. 基本
# include <Siv3D.hpp>
void Main()
{
// 点の基本寿命(秒)
constexpr double LifeTime = 1.0;
// 軌跡
Trail trail{ LifeTime };
while (System::Update())
{
// 軌跡を更新する
trail.update();
// 軌跡に点を追加する
const double t = Scene::Time();
const Vec2 pos{ (400 + 200 * Math::Cos(t)), (300 + 200 * Math::Sin(t)) };
const ColorF color{ 0.3, 0.9, 0.6 };
const double size = 20.0;
trail.add(pos, color, size);
// 軌跡を描画する
trail.draw();
}
}
2. スケールの制御
# include <Siv3D.hpp>
void Main()
{
// 点の基本寿命(秒)
constexpr double LifeTime = 1.0;
// 点のスケールを、残り寿命によらず常に 1.0 にする
auto ScaleFunc = [](double) { return 1.0; };
// 軌跡
Trail trail{ LifeTime, ScaleFunc };
while (System::Update())
{
// 軌跡を更新する
trail.update();
// 軌跡に点を追加する
const double t = Scene::Time();
const Vec2 pos{ (400 + 200 * Math::Cos(t)), (300 + 200 * Math::Sin(t)) };
const ColorF color{ 0.3, 0.9, 0.6 };
const double size = 20.0;
trail.add(pos, color, size);
// 軌跡を描画する
trail.draw();
}
}
3. テクスチャの使用
# include <Siv3D.hpp>
void Main()
{
// 軌跡用のテクスチャ
const Texture texture{ U"example/particle.png", TextureDesc::Mipped };
// 点の基本寿命(秒)
constexpr double LifeTime = 1.0;
// 軌跡
Trail trail{ LifeTime };
while (System::Update())
{
// 軌跡を更新する
trail.update();
// 軌跡に点を追加する
const double t = Scene::Time();
const Vec2 pos{ (400 + 200 * Math::Cos(t)), (300 + 200 * Math::Sin(t)) };
const ColorF color{ 0.3, 0.9, 0.6 };
const double size = 20.0;
trail.add(pos, color, size);
// テクスチャを指定して軌跡を描画する
trail.draw(texture);
}
}
4. プロットの頻度と軌跡の品質
# include <Siv3D.hpp>
void Main()
{
// 軌跡用のテクスチャ
const Texture texture{ U"example/particle.png", TextureDesc::Mipped };
// 点の基本寿命(秒)
constexpr double LifeTime = 0.5;
// 軌跡
Trail trail{ LifeTime };
// 軌跡のプロット頻度(Hz)。増やすと品質が向上する
double frequency = 30.0;
// 再生時間(秒)
double time = 0.0;
// 蓄積時間(秒)
double accumulatedTime = 0.0;
while (System::Update())
{
// 軌跡の更新間隔(秒)
const double updateInterval = (1.0 / frequency);
accumulatedTime += Scene::DeltaTime();
// 蓄積時間が周期を超えていたら、軌跡に点を追加する
while (updateInterval <= accumulatedTime)
{
time += updateInterval;
accumulatedTime -= updateInterval;
const Vec2 pos{ (400 + 300 * Math::Sin(time * 8)), (300 + 200 * Math::Sin(time * 12)) };
const ColorF color{ 1.0, 0.8, 0.4 };
const double size = 30.0;
trail.update(updateInterval);
trail.add(pos, color, size);
}
// テクスチャを指定して軌跡を描画する
trail.draw(texture);
// 軌跡のプロット頻度を調整するスライダー
SimpleGUI::Slider(U"{:.0f} Hz"_fmt(frequency), frequency, 15.0, 240.0, Vec2{ 560, 40 }, 80, 120);
}
}
5. 軌跡のモーション定義クラス
TrailMotion
クラスにいくつかの関数やパラメータを制御することで、自動で軌跡をプロットできます。
# include <Siv3D.hpp>
void Main()
{
Window::Resize(1280, 720);
Scene::SetBackground(ColorF{ 0.15 });
const Texture texture{ U"example/particle.png", TextureDesc::Mipped };
// 軌跡のモーション
TrailMotion trail1 = TrailMotion{}
.setCenter(500, 300)
.setPositionFunction([](double t) { return Vec2{ (300 * Sin(t * 6.0)), (200 * Cos(t * 9.0)) }; })
.setColor(HSV{ 30, 0.5, 1.0 })
.setSize(20.0);
TrailMotion trail2 = TrailMotion{}
.setCenter(500, 300)
.setPositionFunction([](double t) { return Vec2{ (300 * Sin(t * 2.0)), (160 * Cos(t * 5.0)) }; })
.setColor(HSV{ 70, 0.5, 1.0 })
.setSize(20.0);
TrailMotion trail3 = TrailMotion{ 0.5, 240 }
.setCenter(500, 300)
.setPositionFunction([](double t) { return Vec2{ (400 * Sin(t * 3.0)), (120 * Cos(t * 3.0)) }; })
.setColor(HSV{ 270, 0.5, 1.0 })
.setSizeFunction([](double t) { return (8.0 + Periodic::Sine0_1(0.15s, t) * 36.0); });
TrailMotion trail4 = TrailMotion{ 0.5, 240.0, [](double) { return 1.0; }, EaseOutExpo }
.setCenter(500, 560)
.setPositionFunction([](double t) { return Vec2{ (240 * Cos(t * 6.0)), (60 * Sin(t * 6.0)) }; })
.setColor(ColorF{ 0.5, 0.8, 1.0 })
.setSize(24.0);
Array<TrailMotion> electroTrails(4,
TrailMotion{ 0.5, 240 }
.setCenter(0, 0)
.setColor(ColorF{ 1.0 })
.setSize(12.0));
{
const double offsets[4] = { Random(1.0, 2.0), Random(1.0, 2.0), Random(1.0, 2.0), Random(1.0, 2.0) };
for (size_t i = 0; i < electroTrails.size(); ++i)
{
electroTrails[i].setPositionFunction(
[=](double t)
{
double x = ((80.0 + i * 8) + Random(-4.0, 4.0)) * Math::Cos(offsets[i] + t * (4.0 + i * (1 + offsets[i])));
double y = ((80.0 + i * 8) + Random(-4.0, 4.0)) * Math::Sin(offsets[i] + t * (4.0 + i * (1 + offsets[i])));
return Vec2{ x, y };
}
);
}
}
double timeScale = 1.0;
bool wireframe = false;
while (System::Update())
{
for (int32 y = 0; y < 36; ++y)
{
for (int32 x = 0; x < 64; ++x)
{
if (IsEven(y + x))
{
Rect{ (x * 20), y * 20, 20 }.draw(ColorF{ 0.11 });
}
}
}
{
const double deltaTime = (Scene::DeltaTime() * timeScale);
trail1.update(deltaTime);
trail2.update(deltaTime);
trail3.update(deltaTime);
trail4.update(deltaTime);
for (auto& trail : electroTrails)
{
trail.update(deltaTime);
}
}
if (wireframe)
{
const ScopedRenderStates2D blend{ RasterizerState::WireframeCullNone };
const ScopedColorAdd2D colorAdd{ ColorF{ 1.0 } };
trail1.draw();
trail2.draw();
trail3.draw();
trail4.draw();
{
const Transformer2D transformer{ Mat3x2::Translate(Cursor::PosF()) };
for (const auto& trail : electroTrails)
{
trail.draw();
}
}
}
else
{
trail1.draw(texture);
trail2.draw(texture);
trail3.draw(texture);
trail4.draw();
{
const Transformer2D transformer{ Mat3x2::Translate(Cursor::PosF()) };
for (const auto& trail : electroTrails)
{
trail.draw(texture);
}
}
}
RoundRect{ 945, 20, 300, 120, 8 }.drawShadow({ 4, 4 }, 8, 3).draw();
SimpleGUI::CheckBox(wireframe, U"wireframe", Vec2{ 960, 40 });
SimpleGUI::Slider(U"speed x{:.2f}"_fmt(timeScale), timeScale, 0.01, 1.0, Vec2{ 960, 80 }, 140, 140);
}
}
6. 軌跡によるエフェクトのサンプル
# include <Siv3D.hpp>
struct EffectCircle
{
ColorF color;
bool additiveBlend = false;
Array<TrailMotion> trails;
void update()
{
for (auto& trail : trails)
{
trail.update();
}
}
void draw(const Vec2& center, double size, const Texture& texture) const
{
const Transformer2D transform{ Mat3x2::Scale(size / 80.0).translated(center) };
const Circle circle{ 80 };
circle.drawFrame(0, 12, ColorF{ 0.0, 0.2 }, ColorF{ 0.0, 0.0 })
.draw((color + ColorF{ 0.05 }), (color - ColorF{ 0.05 }));
{
const ScopedRenderStates2D blend{ BlendState::Additive };
circle.drawFrame(12, 0, (color * 0.8));
}
{
const ScopedRenderStates2D blend{ additiveBlend ? BlendState::Additive : BlendState::Default2D };
for (const auto& trail : trails)
{
trail.draw(texture);
}
}
}
};
void Main()
{
Window::Resize(1280, 720);
Scene::SetBackground(ColorF{ 0.6 });
const Texture texture{ U"example/particle.png", TextureDesc::Mipped };
const double offsets[3] = { Random(1.0, 2.0), Random(1.0, 2.0), Random(1.0, 2.0) };
double size = 120.0;
bool wireframe = false;
EffectCircle pyro{ ColorF{ 0.95, 0.4, 0.3 } };
for (int32 i = 0; i < 3; ++i)
{
const HSV hsv{ 10.0, 0.67, (0.8 + i * 0.1) };
pyro.trails << TrailMotion{}
.setScaleFunction([](double t)
{
return ((0.9 < t) ? EaseOutBack((1.0 - t) * 10.0) : 1.0);
})
.setPositionFunction([i, &offsets](double t)
{
double x = ((80 + (i + 1) * 4)) * -Math::Cos(offsets[i] + t * (4.0 + i * (1 + offsets[i])));
double y = ((80 + (i + 1) * 4)) * Math::Sin(offsets[i] + t * (4.0 + i * (1 + offsets[i])));
return Vec2{ x, y };
})
.setSizeFunction([](double t)
{
return (6.0 + Periodic::Sine0_1(0.2s, t) * 28.0);
})
.setColor(hsv)
.setFrequency(240.0);
}
EffectCircle electro{ ColorF{ 0.5, 0.3, 0.9 }, true };
for (int32 i = 0; i < 3; ++i)
{
electro.trails << TrailMotion{}
.setAlphaFunction([](double) { return 1.0; })
.setPositionFunction([i, &offsets](double t)
{
double x = ((80 + (i + 1) * 4) + Random(-4.0, 4.0)) * -Math::Cos(offsets[i] + t * (4.0 + i * (1 + offsets[i])));
double y = ((80 + (i + 1) * 4) + Random(-4.0, 4.0)) * Math::Sin(offsets[i] + t * (4.0 + i * (1 + offsets[i])));
return Vec2{ x, y };
})
.setSize(12.0)
.setColor(ColorF{ electro.color, 0.2 })
.setFrequency(300.0);
}
EffectCircle cryo{ ColorF{ 0.5, 0.8, 1.0 }, true };
for (int32 i = 0; i < 3; ++i)
{
const HSV hsv{ 220, 0.67, (0.9 + i * 0.05) };
cryo.trails << TrailMotion{}
.setScaleFunction([](double t)
{
return (0.5 - AbsDiff(t, 0.5));
})
.setPositionFunction([i, &offsets](double t)
{
double x = ((80 + i * 4)) * -Math::Cos(offsets[i] + t * (5.0 + i * (1 + offsets[i])));
double y = ((80 + i * 4)) * Math::Sin(offsets[i] + t * (5.0 + i * (1 + offsets[i])));
return Vec2{ x, y };
})
.setSizeFunction([](double t)
{
return (18.0 + Periodic::Sine0_1(0.2s, t) * 42.0);
})
.setColor(hsv);
}
EffectCircle anemo{ ColorF{ 0.4, 0.8, 0.4 } };
for (int32 i = 0; i < 3; ++i)
{
const HSV hsv{ 120, 0.5, (0.7 + i * 0.1) };
anemo.trails << TrailMotion{}
.setScaleFunction([](double t)
{
return (0.5 - AbsDiff(t, 0.5));
})
.setPositionFunction([i, &offsets](double t)
{
double x = ((72 + i * 4)) * -Math::Cos(offsets[i] + t * (4.0 + i * (1 + offsets[i])) * 3.0);
double y = ((72 + i * 4)) * Math::Sin(offsets[i] + t * (4.0 + i * (1 + offsets[i])) * 2.0);
return Vec2{ x, y };
})
.setSizeFunction([](double t)
{
return (16.0 + Periodic::Sine0_1(0.2s, t) * 48.0);
})
.setColor(hsv)
.setFrequency(240.0);
}
while (System::Update())
{
for (int32 y = 0; y < 36; ++y)
{
for (int32 x = 0; x < 64; ++x)
{
if (IsEven(y + x))
{
Rect{ (x * 20), y * 20, 20 }.draw(ColorF{ 0.55 });
}
}
}
pyro.update();
electro.update();
cryo.update();
anemo.update();
{
const ScopedRenderStates2D rs{ wireframe ? RasterizerState::WireframeCullBack : RasterizerState::Default2D };
const ScopedColorAdd2D add{ wireframe ? ColorF{ 1.0, 1.0 } : ColorF{ 0.0, 0.0 } };
pyro.draw(Vec2{ 250, 200 }, size, texture);
electro.draw(Vec2{ 250, 520 }, size, texture);
cryo.draw(Vec2{ 650, 200 }, size, texture);
anemo.draw(Vec2{ 650, 520 }, size, texture);
}
SimpleGUI::Slider(U"size", size, 0.0, 200.0, Vec2{ 880, 80 }, 80, 100);
SimpleGUI::CheckBox(wireframe, U"wireframe", Vec2{ 880, 120 }, 180);
}
}
9 スライスの描画
1. 基本
下記の画像ファイルをプロジェクトに配置する
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.7 });
constexpr int32 CellSize = 20;
NinePatch ninePatch{ U"patch0.png", 64 };
while (System::Update())
{
for (int32 y = 0; y < (Scene::Height() / CellSize); ++y)
{
for (int32 x = 0; x < (Scene::Width() / CellSize); ++x)
{
if (IsEven(y + x))
{
Rect{ (x * CellSize), (y * CellSize), CellSize }.draw(ColorF{ 0.65 });
}
}
}
ninePatch.draw(Rect{ 40, 40, 100, 100 }, 0.5);
ninePatch.draw(Rect{ 40, 160, 160, 160 }, 0.125);
ninePatch.draw(Rect{ 220, 160, 160, 160 }, 0.25);
ninePatch.draw(Rect{ 400, 160, 160, 160 }, 0.5);
ninePatch.draw(Rect{ 580, 160, 160, 160 }, 1.0);
ninePatch.draw(Rect{ 40, 340, 400, 200 }, 0.5);
}
}
2. 原理の説明
# include <Siv3D.hpp>
Texture MakeExample9PatchTexture()
{
// 描画された最大のアルファ成分を保持するブレンドステートを作成する関数
auto MakeBlendState = []()
{
BlendState blendState = BlendState::Default2D;
blendState.srcAlpha = Blend::SrcAlpha;
blendState.dstAlpha = Blend::DestAlpha;
blendState.opAlpha = BlendOp::Max;
return blendState;
};
MSRenderTexture renderTexture{ Size{ 128, 128 }, ColorF{ 0.5, 0.0 } };
{
// レンダーターゲットを renderTexture に変更する
const ScopedRenderTarget2D target{ renderTexture };
// 描画された最大のアルファ成分を保持するブレンドステート
const ScopedRenderStates2D blend{ MakeBlendState() };
Triangle{ 32, 0, 32, 32, 0, 32 }.draw(ColorF{ 1.0 });
Triangle{ 96, 0, 128, 32, 96, 32 }.draw(ColorF{ 1.0 });
Triangle{ 32, 96, 32, 128, 0, 96 }.draw(ColorF{ 1.0 });
Triangle{ 96, 96, 128, 96, 96, 128 }.draw(ColorF{ 1.0 });
for (int32 x = 0; x < 4; ++x)
{
Rect{ (32 + x * 16), (x % 2 * 16), 16 }.draw(HSV{ 20.0, 0.4, 1.0 });
Rect{ (32 + x * 16), ((x + 1) % 2 * 16), 16 }.draw(HSV{ 40.0, 0.4, 1.0 });
Rect{ (32 + x * 16), (96 + x % 2 * 16), 16 }.draw(HSV{ 60.0, 0.4, 1.0 });
Rect{ (32 + x * 16), (96 + (x + 1) % 2 * 16), 16 }.draw(HSV{ 80.0, 0.4, 1.0 });
}
for (int32 y = 0; y < 4; ++y)
{
Rect{ (y % 2 * 16), (32 + y * 16), 16 }.draw(HSV{ 100.0, 0.4, 1.0 });
Rect{ ((y + 1) % 2 * 16), (32 + y * 16), 16 }.draw(HSV{ 120.0, 0.4, 0.9 });
Rect{ (96 + y % 2 * 16), (32 + y * 16), 16 }.draw(HSV{ 140.0, 0.4, 1.0 });
Rect{ (96 + (y + 1) % 2 * 16), (32 + y * 16), 16 }.draw(HSV{ 160.0, 0.4, 0.9 });
}
for (int32 y = 0; y < 4; ++y)
{
for (int32 x = 0; x < 4; ++x)
{
Rect{ (32 + x * 16), (32 + y * 16), 16 }.draw(HSV{ (IsEven(x + y) ? 180.0 : 200.0), 0.4, 1.0 });
}
}
}
Graphics2D::Flush();
renderTexture.resolve();
return renderTexture;
}
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
const Texture ninePatchTexture = MakeExample9PatchTexture();
constexpr int32 TextureCornerSize = 32;
const NinePatch ninePatch{ ninePatchTexture, TextureCornerSize, NinePatch::Style::Default() };
const NinePatch ninePatchTiled{ ninePatchTexture, TextureCornerSize, NinePatch::Style::TileAll() };
bool repeat = true;
bool linearFilter = true;
size_t textureScaleIndex = 2;
double width = 400.0;
double height = 300.0;
while (System::Update())
{
RoundRect{ 40, 40, 500, 180, 10 }.draw(ColorF{ 0.25 });
font(U"9-patch texture").draw(30, Vec2{ 60, 60 }, ColorF{ 0.98 });
ninePatchTexture.draw(Vec2{ 320, 70 });
const double textureScale = Math::Exp2(textureScaleIndex - 2.0);
const TextureFilter textureFilter = (linearFilter ? TextureFilter::Linear : TextureFilter::Nearest);
if (repeat)
{
ninePatchTiled.draw(RectF{ 40, 240, width, height }, textureScale, textureFilter);
}
else
{
ninePatch.draw(RectF{ 40, 240, width, height }, textureScale, textureFilter);
}
SimpleGUI::CheckBox(repeat, U"Tiled", Vec2{ 560, 40 }, 200);
SimpleGUI::CheckBox(linearFilter, U"LinearFilter", Vec2{ 560, 80 }, 200);
SimpleGUI::RadioButtons(textureScaleIndex, { U"0.25", U"0.5", U"1.0", U"2.0" }, Vec2{ 560, 120 }, 200);
SimpleGUI::Slider(width, 64.0, 480.0, Vec2{ 560, 280 }, 200);
SimpleGUI::Slider(height, 64.0, 320.0, Vec2{ 560, 320 }, 200);
}
}
3. 発展
下記の 5 つの画像ファイルをプロジェクトに配置する
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.7 });
constexpr int32 CellSize = 20;
const Font font{ FontMethod::MSDF, 48, U"example/font/RocknRoll/RocknRollOne-Regular.ttf" };
Stopwatch stopwatch{ StartImmediately::Yes };
Array<NinePatch> ninePatches =
{
NinePatch{ U"patch0.png", 64 },
NinePatch{ U"patch1.png", 64 },
NinePatch{ U"patch2.png", 64 },
NinePatch{ U"patch3.png", 30 },
NinePatch{ U"patch4.png", 8 },
};
size_t index = 0;
double width = 640;
double height = 220;
while (System::Update())
{
for (int32 y = 0; y < (Scene::Height() / CellSize); ++y)
{
for (int32 x = 0; x < (Scene::Width() / CellSize); ++x)
{
if (IsEven(y + x))
{
Rect{ (x * CellSize), (y * CellSize), CellSize }.draw(ColorF{ 0.65 });
}
}
}
{
const Transformer2D transformer(Mat3x2::Translate(80, 40));
ninePatches[index].getTexture().draw();
const auto& patchSize = ninePatches[index].getPatchSize();
patchSize.topLeftRect().drawFrame(1, 0);
patchSize.topRect().drawFrame(1, 0);
patchSize.topRightRect().drawFrame(1, 0);
patchSize.leftRect().drawFrame(1, 0);
patchSize.centerRect().drawFrame(1, 0);
patchSize.rightRect().drawFrame(1, 0);
patchSize.bottomLeftRect().drawFrame(1, 0);
patchSize.bottomRect().drawFrame(1, 0);
patchSize.bottomRightRect().drawFrame(1, 0);
}
if (SimpleGUI::RadioButtons(index, { U"patch0", U"patch1", U"patch2", U"patch3", U"patch4" }, Vec2{ 460, 40 }, 120))
{
stopwatch.restart();
}
SimpleGUI::Slider(width, 100.0, 800.0, Vec2{ 600, 40 }, 120);
SimpleGUI::Slider(height, 100.0, 320.0, Vec2{ 600, 80 }, 120);
const RectF rect{ ((800 - width) / 2), 280, width, height };
if (InRange((int32)index, 0, 2))
{
ninePatches[index].draw(rect, 0.5);
}
else if (index == 3)
{
ninePatches[index].draw(rect, 2.0, TextureFilter::Nearest);
}
else if (index == 4)
{
ninePatches[index].draw(rect, 8.0, TextureFilter::Nearest);
}
font(U"Hello, Siv3D!"_sv.substr(0, stopwatch.ms() / 30)).draw(36, rect.stretched(-60, -48), ColorF{ 0.98 });
}
}
目標に追従するシンプルな 3D カメラ
# include <Siv3D.hpp>
void Main()
{
Window::Resize(1280, 720);
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
const ColorF backgroundColor = ColorF{ 0.4, 0.6, 0.8 }.removeSRGBCurve();
const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB };
const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes };
constexpr double Speed = 4.0;
Vec3 currentPos{ 0, 1, 0 };
Vec3 targetPos{ 0, 1, 0 };
Vec3 velocity{ 0, 0, 0 };
constexpr double FollowDistance = 15.0;
double followHeight = 8.0;
double followDirection = 0.0_deg;
SimpleFollowCamera3D camera{ renderTexture.size(), 30_deg, currentPos, 0.0_deg, FollowDistance, followHeight };
while (System::Update())
{
const double deltaTime = Scene::DeltaTime();
// 追従の調整
{
if (KeyLeft.pressed())
{
followDirection -= (90_deg * deltaTime);
}
if (KeyRight.pressed())
{
followDirection += (90_deg * deltaTime);
}
if (KeyDown.pressed())
{
followHeight = Min((followHeight + 4.0 * deltaTime), 20.0);
}
if (KeyUp.pressed())
{
followHeight = Max((followHeight - 4.0 * deltaTime), 0.0);
}
}
// キャラクターの移動
{
if (KeyA.pressed())
{
targetPos.x -= (Speed * deltaTime);
}
if (KeyD.pressed())
{
targetPos.x += (Speed * deltaTime);
}
if (KeyW.pressed())
{
targetPos.z += (Speed * deltaTime);
}
if (KeyS.pressed())
{
targetPos.z -= (Speed * deltaTime);
}
currentPos = Math::SmoothDamp(currentPos, targetPos, velocity, 0.12);
}
if (KeyC.down())
{
currentPos = targetPos = Vec3{ 0, 1, 0 };
velocity = Vec3{ 0, 0, 0 };
followHeight = 8.0;
followDirection = 0.0_deg;
camera.jumpToTarget(currentPos, followDirection);
}
// カメラの更新
{
camera.setTarget(currentPos, followDirection);
camera.setFollowOffset(FollowDistance, followHeight);
camera.update(0.15);
Graphics3D::SetCameraTransform(camera);
}
// 3D 描画
{
const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };
Plane{ 64 }.draw(uvChecker);
Box{ currentPos, 2 }.draw(ColorF{ 0.8, 0.6, 0.4 }.removeSRGBCurve());
}
// 3D シーンを 2D シーンに描画
{
Graphics3D::Flush();
renderTexture.resolve();
Shader::LinearToScreen(renderTexture);
}
RoundRect{ 20, 20, 680, 200, 10 }.draw(ColorF{ 0.0, 0.6 });
font(U"キャラクター位置: {:.2f}"_fmt(currentPos)).draw(24, Vec2{ 40, 40 });
font(U"追従方向: {:.1f}°"_fmt(Math::ToDegrees(Math::NormalizeAngle(followDirection)))).draw(24, Vec2{ 40, 80 });
font(U"追従距離: {:.2f}"_fmt(FollowDistance)).draw(24, Vec2{ 240, 80 });
font(U"追従高さ: {:.2f}"_fmt(followHeight)).draw(24, Vec2{ 440, 80 });
font(U"追従オフセット: {:.2f}"_fmt(camera.getFollowOffset())).draw(24, Vec2{ 40, 120 });
font(U"移動: WASD, 追従方向: ←→, 追従高さ: ↑↓ リセット: C"_fmt(camera.getFollowOffset())).draw(24, Vec2{ 40, 160 });
}
}