OpenSiv3Dでアナログ時計を描画してみる

4 min読了の目安(約4400字TECH技術記事 1

作ったもの

最近OpenSiv3D触れてないなぁと思い、APIの復習も兼ねてアナログ時計を描画するプログラムを作ってみました。

大きさを管理する変数と背景色の設定

最初に時計の大きさや針の太さを管理する変数を宣言します。
ついでに背景色も Scene::SetBackground(Palette::Black) を渡し、黒色に設定します。

Main.cpp
# include <Siv3D.hpp>

void Main()
{
    // 縁の半径
    const int size = 200;
    // 針の太さのベース(時針の太さ)
    const int thick = 6;
    // 時刻用のフォント
    const Font hourFont(24);

    // 背景を黒に
    Scene::SetBackground(Palette::Black);

    while (System::Update())
    {
    }
}

時計の縁を描画

次に時計の縁を画面の中心に描画していきます。画面の中心座標は Scene::Center()Point 型の中心座標を取得できます。
そして縁から中心に向かう線を描画していきます。先は全部で 60 本あるので for (auto i : step(60) を使い、60本の線を角度をずらしながら描画していきます。また i が 5 で割り切れる場合は時刻の文字も描画します。

Main.cpp
/* 省略 */
void Main()
{
    /* 省略 */
    while (System::Update())
    {
        // 中心の座標
        const auto center = Scene::Center();

        // 縁に秒単位の線を描画
        for (auto i : step(60))
        {
            // 角度
            const double theta = i * 6_deg;
            // 時刻を描画するか
            const bool isHour = i % 5 == 0;
            // 描画する座標
            const Vec2 pos = OffsetCircular(center, size, theta);

            if (isHour)
            {
                // i == 0の場合は12を描画
                const String hourString = i == 0 ? U"12" : ToString(i / 5);
                hourFont(hourString).drawAt(pos.lerp(center, 0.2), Palette::White);
            }

            // 線を描画
            Line(pos, pos.lerp(center, isHour ? 0.1 : 0.05)).draw(thick / 2, Palette::White);
        }

        // 縁の描画
        Circle(center, size).drawFrame(thick / 2, Palette::White);
    }
 }

針を描画する

DateTime::Now() で現在時刻を取得し、時刻に合わせて各針を描画していきます。

Main.cpp
/* 省略 */
void Main()
{
   /* 省略 */
   while (System::Update())
   {
       /* 省略 */
           // 現在時刻を取得
       const DateTime now = DateTime::Now();

       // 秒針を描画
       Line(center, OffsetCircular(center, size * 0.9, now.second * 6_deg)).draw(thick / 3, Palette::White);
       // 分針を描画
       Line(center, OffsetCircular(center, size * 0.85, now.minute * 6_deg)).draw(thick / 2 , Palette::White);
       // 時針を描画
       Line(center, OffsetCircular(center, size * 0.5, now.hour * 30_deg)).draw(thick, Palette::White);

       // 針の中心に円を描画
       Circle(center, size * 0.03).draw(Palette::White);
   }
}

時針を修正

先程の針を描画するプログラムでは時針が1時間毎にしか動かないため、分を使い、時針を描画することで本物の時計に近づけていきます。

Main.cpp
 /* 省略 */
void Main()
{
   /* 省略 */
   while (System::Update())
   {
       /* 省略 */
   // 時針を描画
       Line(center, OffsetCircular(center, size * 0.5, (now.hour % 12 + now.minute / 60.0) * 30_deg)).draw(thick, Palette::White);
   /* 省略 */
   }
}

おわり

最後に色や線のバランスを調整して完成です。

コード全文

Main.cpp

# include <Siv3D.hpp>

void Main()
{
   // 縁の半径
   const int size = 200;
   // 針の太さのベース(時針の太さ)
   const int thick = 6;
   // 時刻用のフォント
   const Font hourFont(24);

   // 背景を黒に
   Scene::SetBackground(Palette::Black);

   while (System::Update())
   {
       // 中心の座標
       const auto center = Scene::Center();

       // 縁に秒単位の線を描画
       for (auto i : step(60))
       {
           // 角度
           const double theta = i * 6_deg;
           // 時刻を描画するか
           const bool isHour = i % 5 == 0;
           // 描画する座標
           const Vec2 pos = OffsetCircular(center, size, theta);

           if (isHour)
           {
               // i == 0の場合は12を描画
               const String hourString = i == 0 ? U"12" : ToString(i / 5);
               hourFont(hourString).drawAt(pos.lerp(center, 0.2), Palette::White);
           }

           // 線を描画
           Line(pos, pos.lerp(center, isHour ? 0.1 : 0.03)).draw(thick / 2, isHour ? Palette::White : Palette::Lightgrey);
       }

       // 縁の描画
       Circle(center, size).drawFrame(thick / 2, Palette::White);

       // 現在時刻を取得
       const DateTime now = DateTime::Now();

       // 秒針を描画
       Line(center, OffsetCircular(center, size * 0.9, now.second * 6_deg)).draw(thick / 3, Palette::Yellow);
       // 分針を描画
       Line(center, OffsetCircular(center, size * 0.85, now.minute * 6_deg)).draw(thick / 2 , Palette::White);
       // 時針を描画
       Line(center, OffsetCircular(center, size * 0.6, (now.hour % 12 + now.minute / 60.0) * 30_deg)).draw(thick, Palette::White);

       // 針の中心に円を描画
       Circle(center, size * 0.03).draw(Palette::White);
   }
}