Skip to content

Instantly share code, notes, and snippets.

@Reputeless
Last active August 10, 2023 11:01
Show Gist options
  • Save Reputeless/834bda9457df2a6015df39c2d3d0eed6 to your computer and use it in GitHub Desktop.
Save Reputeless/834bda9457df2a6015df39c2d3d0eed6 to your computer and use it in GitHub Desktop.
# include <Siv3D.hpp>
/// @brief 目標に追従するカメラクラス
/// @note 追従方向、追従距離、追従高さを指定して、目標に追従するカメラを作成します。
class SimpleFollowCamera3D : public BasicCamera3D
{
public:
SimpleFollowCamera3D() = default;
/// @brief 3D カメラを作成します。
/// @param sceneSize シーンのサイズ
/// @param verticalFOV 縦方向の視野角(ラジアン)
/// @param focusPosition 注目点
/// @param followAngle 追従方向(0 が Z 軸正方向、時計まわり)
/// @param followDistance 追従距離
/// @param followHeight 追従高さ
SimpleFollowCamera3D(const Size& sceneSize, double verticalFOV, const Vec3& focusPosition, double followDirection, double followDistance, double followHeight) noexcept
: BasicCamera3D{ sceneSize, verticalFOV, (focusPosition + CalculateFollowOffset(followDirection, followDistance, followHeight)), focusPosition }
, m_currentFocus{ focusPosition }
, m_targetFocus{ focusPosition }
, m_followDirection{ followDirection }
, m_followDistance{ followDistance }
, m_followHeight{ followHeight } {}
/// @brief カメラを更新します。
/// @param smoothTime 平滑化時間(最大速度で目標に向かうときに期待される所要時間)。動く目標を追いかけるときの遅延時間で、小さいと目標に早く到達する。
/// @param deltaTime 前回からの経過時間。デフォルトでは Scene::DeltaTime()
void update(double smoothTime, double deltaTime = Scene::DeltaTime())
{
m_currentFocus = Math::SmoothDamp(m_currentFocus, m_targetFocus, m_focusVelocity, smoothTime, unspecified, deltaTime);
BasicCamera3D::setView((m_currentFocus + getFollowOffset()), m_currentFocus, m_upDirection);
}
/// @brief 追従する対象の位置を設定します。
/// @param targetFocus 追従する対象の位置
/// @param direction 追従する対象の向いている方向(0 が Z 軸正方向、時計まわり)
void setTarget(const Vec3& targetFocus, double followAngle = 0.0) noexcept
{
m_targetFocus = targetFocus;
m_followDirection = followAngle;
}
/// @brief 追従する対象の位置を設定します(即座に移動します)。
/// @param targetFocus 追従する対象の位置
/// @param followAngle 追従する対象の向いている方向(0 が Z 軸正方向、時計まわり)
void jumpToTarget(const Vec3& targetFocus, double followAngle = 0.0) noexcept
{
m_currentFocus = m_targetFocus = targetFocus;
m_focusVelocity = Vec3{ 0, 0, 0 };
m_followDirection = followAngle;
BasicCamera3D::setView((m_currentFocus + getFollowOffset()), m_currentFocus, m_upDirection);
}
/// @brief 追従距離と追従高さを設定します。
/// @param distance 追従距離
/// @param height 追従高さ
void setFollowOffset(double followDistance, double followHeight) noexcept
{
m_followDistance = followDistance;
m_followHeight = followHeight;
}
/// @brief 追従対象に対するオフセットを計算します。
/// @return 追従対象に対するオフセット
[[nodiscard]]
Vec3 getFollowOffset() const noexcept
{
return CalculateFollowOffset(m_followDirection, m_followDistance, m_followHeight);
}
private:
static Vec3 CalculateFollowOffset(double direction, double distance, double height) noexcept
{
return Cylindrical{ distance, (-90_deg - direction), height };
}
Vec3 m_currentFocus = Vec3{ 0, 0, 0 };
Vec3 m_targetFocus = Vec3{ 0, 0, 0 };
Vec3 m_focusVelocity = Vec3{ 0, 0, 0 };
double m_followDirection = 0.0_deg;
double m_followDistance = 10.0;
double m_followHeight = 10.0;
};
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 });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment