Unity を見てみたおかげで、C++ のコルーチンが何を目的としているのか理解できました。

2026/03/22 8:19

Unity を見てみたおかげで、C++ のコルーチンが何を目的としているのか理解できました。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

(すべての重要ポイントを統合し、曖昧な表現を明確化したもの)


要約

C++23では

<generator>
co_yield
キーワードが導入され、開発者は Unity の C#
yield return null
パターンに非常に近いコルーチンスタイルのコードを書ける標準的な手段を得ました。Unity ではコルーチンは主に数フレームにわたってオブジェクトをフェードアウトさせるなどの一時的な挙動(例:
StartCoroutine(Fade())
)に使用されます。Unity のシステムは C# の
await
より前に構築されたため、ボイラープレートを低く抑える生成器スタイルのワークアラウンドに頼っています。

対照的に、C++ で同様のロジックを書きたい場合、生成器が無いと完全な状態機械(手動 enum 状態、カウンタ、しばしば冗長な演算子オーバーロード)が必要になります。新しい

<generator>
機能により、開発者は Fibonacci などの単純な生成器を最小限のコードで書けますが、
co_await
はまだ問題があります。言語自体に具体的な実行フレームワークがないため、スケジューリングや実行キュー、既存の並列モデルとの統合についての疑問は未解決です。

この記事では、C++ で最小限の Unity スタイルのエグゼキュータを提示しています。これは

std::generator<std::monostate>
オブジェクトを保持するマネージャーで、各フレームごとにそれらを進め、完了したものは「remove‑if」パターンで削除します。また、このエグゼキュータを拡張して描画可能オブジェクトを返すカスタム
Draw
構造体を yield する方法も示し、データ駆動型のレンダリングパイプラインを実現しています。効果更新の並列実行は TBB の
parallel_for
を使用しており、多数のコルーチンが同時に走る際に性能向上が確認できます。

C++26 では最終的に適切な

co_await
を可能にする実行フレームワークが追加される予定です。しかし、既にプロジェクトは Boost.Asio や Intel TBB のようなカスタムスケジューラやライブラリを使用しているため、新しいフレームワークの統合は挑戦的になる可能性があります。それまでは、生成器ベースのエグゼキュータを採用して冗長な状態機械を置き換えることで、保守性と性能を向上させつつ、サードパーティの非同期ライブラリに対する標準化された代替手段を提供できます。

本文

2026年3月20日 – C++ とゲーム開発

私はコルーチンに関する多くの講演を見てきましたが、Unity が C# でそれらをどのように使っているかを見るまで、本当に理解できたことはありませんでした。
C++ におけるコルーチンはすでに6年間存在しますが、それでも実際のプロダクションコードで触れたことはありません。これは、低レベル機能であり、プロジェクトへ組み込むには多くのカスタム plumbing が必要だからだと考えられます。さらに重要なのは、具体的な例が不足している点です。


Unity のコルーチン例

void Update()
{
    if (Input.GetKeyDown("f"))
        StartCoroutine(Fade());
}

IEnumerator Fade()
{
    Color c = renderer.material.color;
    for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
    {
        c.a = alpha;
        renderer.material.color = c;
        yield return null;   // “次のフレーム”
    }
}

C# の純粋主義者は

yield
を使ったこの書き方に異議を唱えるかもしれません。意味論が誤っています:何も返さずに
yield
していますが、実際には
await NextFrame()
に似た挙動を期待しているのです。Unity のトリックは初期の C# が
await
をサポートしなかったことから生まれました。それが今日まで使われ続けています。


なぜコルーチンなのか?

上記の例は単純です。もう少し複雑なエフェクトを試してみましょう。

IEnumerator TimeWarp()
{
    // 左へジャンプ
    transform.position.x -= 1.f;
    yield return null;

    // 右へステップ
    for (int i = 0; i < 4; ++i)
    {
        transform.position.x += 0.2f;
        yield return null;
    }

    // … 手を腰に当てる …

    // 再度タイムワープ
    for (int i = 0; i < 4; ++i)
    {
        transform.Rotate(0.f, 90.f * i, 0.f);
        yield return null;
    }
}

これを C++ の通常の関数オブジェクトやラムダで書くと、汚い状態機械が出来上がります。

class TimeWarp
{
public:
    enum class State { Jump, StepRight, HandsOnHips, DoAgain };
    State _state = State::Jump;
    int _i = 0;
    Transform* _transform;

    TimeWarp(Transform& transform) : _transform(&transform) {}

    bool operator()()
    {
        switch (_state)
        {
            case State::Jump:
                _transform->position.x -= 1.f;
                _state = State::StepRight;
                break;
            case State::StepRight:
                _transform->position.x += 0.2f;
                if (++_i == 4) { _state = State::HandsOnHips; _i = 0; }
                break;
            // …
            case State::DoAgain:
                _transform->Rotate(0.f, 90.f * i, 0.f);
                if (++_i == 4) return true;   // 完了
                break;
        }
        return false;
    }
};

見た目が汚く、読みづらいです。エフェクトを個別の動作に分割したり継続をキューに入れたりすることは可能ですが、面倒です。


C++23 実装

<generator>
を使えば、各フレームで制御を yield するコルーチンを書けます。

std::generator<std::monostate> TimeWarp(GameObject& obj)
{
    // 左へジャンプ
    obj.transform.position.x -= 1.f;
    co_yield {};

    // 右へステップ
    for (int i = 0; i < 4; ++i)
    {
        obj.transform.position.x += 0.2f;
        co_yield {};
    }

    // … 手を腰に当てる …

    // 再度タイムワープ
    for (int i = 0; i < 4; ++i)
    {
        obj.transform.Rotate(0.f, 90.f * i, 0.f);
        co_yield {};
    }
}

これは実質 Unity が使うハックと同じです。

co_yield
は「次のフレーム」を意味します。
co_await
を使う方が複雑で、何を待つか、いつ準備完了か、どのスケジューラを使うかを決める必要があります。C++26 で
execution
が登場する予定ですが、現時点では generator アプローチが実用的です。


C++ の簡易 Unity‑style コルーチンランナー

以下は毎フレームすべてのアクティブコルーチンを1回だけ走らせる最小限のエグゼキュータです。

class effects_manager
{
public:
    void add(std::generator<std::monostate> effect)
    {
        _effects.push_back(std::move(effect));
        _iterators.push_back(_effects.back().begin());
    }

    void run()
    {
        // 完了したコルーチンを除去
        int first = 0;
        for (; first != _effects.size() &&
               _iterators[first] != _effects[first].end(); ++first);

        if (first != _effects.size())
        {
            for (int i = first; ++i != _effects.size(); )
                if (_iterators[i] != _effects[i].end())
                {
                    _effects[first] = std::move(_effects[i]);
                    _iterators[first] = std::move(_iterators[i]);
                    ++first;
                }
            _effects.erase(begin(_effects) + first, end(_effects));
            _iterators.erase(begin(_iterators) + first, end(_iterators));
        }

        // すべてのコルーチンを進める
        for (int i = 0; i < _effects.size(); ++i)
            ++_iterators[i];
    }

private:
    std::vector<std::generator<std::monostate>> _effects;
    using effect_iterator =
        decltype(std::declval<std::generator<std::monostate>>().begin());
    std::vector<effect_iterator> _iterators;
};

使用例:

effects_manager effects;
effects.add(TimeWarp(object));
...
effects.run();   // 毎フレーム1回呼び出す

ボーナス:副作用ではなく描画データを返す

コルーチンから

Draw
オブジェクトを返し、すべての描画を1度に集めることもできます。

struct Draw { Model model; Transform transform; };

std::generator<Draw> TimeWarp(const Model& model)
{
    vec3 position{-1.f, 0.f, 0.f};
    co_yield Draw{model, {.position = position}};
    for (int i = 0; i < 4; ++i) {
        position.x += 0.2f;
        co_yield Draw{model, {.position = position}};
    }
    // …
}

ランナーは

Draw
オブジェクトのベクタを生成します。

std::vector<Draw> run()
{
    std::vector<Draw> draws;
    draws.reserve(_effects.size());
    for (int i = 0; i < _effects.size(); ++i) {
        draws.push_back(*_iterators[i]);
        ++_iterators[i];
    }
    return draws;
}

TBB や他の並列ツールを使えば、ループを並列化できます。

std::vector<Draw> draws(_effects.size());
tbb::parallel_for(0zu, _effects.size(),
                  [this, &draws](size_t i)
                  {
                      draws[i] = *_iterators[i];
                      ++_iterators[i];
                  });
return draws;

結論

C++ の

<generator>
を使ったコルーチンは、手作りの状態機械を必要とせずに簡潔で読みやすい操作列を書けます。パターンは実装が簡単で、数十行程度のコードで済み、既存のゲームループに自然に統合できます。他の Fibonacci ジェネレータよりもずっと面白い使い道です!

同じ日のほかのニュース

一覧に戻る →

2026/03/26 6:11

テスラ・モデル 3 のコンピュータをデスク上で稼働させ、事故車から取り出した部品を使用しています。

## 日本語訳: ## 要約: この記事では、セキュリティ研究のためにテスラ・モデル 3 MCU(モーター制御ユニット)の取得とセットアップ方法を説明しています。テスラのバグバウンティプログラムが研究者に車両内の脆弱性発見を奨励していることを強調し、筆者はeBayから安価な部品(約 $200–$300)を購入し、DC電源と最大8 Aまで供給可能な12 Vアダプタで組み立てました。さらに、Rosenbergerケーブル(パーツ番号1067960‑XX‑E)が必要で、個別販売されていないためダッシュボードロウムを購入しました。BMW LVDSコネクタを使った初期試行ではMAX16932制御チップがショートし、筆者は現地で修復して2つの機能的MCUを得ました。テスラの電気参照書にケーブル部品番号が確認されています。次のステップとして、MCUのユーザーインターフェース、ネットワークインターフェース(CANバス、ポート 22のSSH、ポート 8080のREST‑ライクAPI)を探索し、システム稼働時にファームウェアを抽出する可能性があります。これらの方法でルートアクセスを取得すると、研究者はテスラの「Root Access Program」のための重要な脆弱性を特定でき、車両セキュリティの向上につながる可能性があります。 ## 要約骨格 **本文が主に伝えたいこと(メインメッセージ)** 筆者はテスラ・モデル 3 MCUを取得し設定する方法を示し、その電源供給とネットワークサービスへのアクセス手順を強調しています。 **根拠/推論(なぜこう言われているか)** - テスラのバグバウンティプログラムは研究者に脆弱性発見を促している。 - 筆者はeBayから安価な部品($200–$300)を購入し、DC電源と最大8 Aまで供給可能な12 Vアダプタで組み立てた。 - 配線には特定のRosenbergerケーブル(パーツ番号1067960‑XX‑E)が必要で、個別販売されていないためダッシュボードロウムを購入した。 **関連ケース/背景(文脈・過去事例・周辺情報)** - BMW LVDSコネクタを使用した初期試行は失敗し、即席配線でMAX16932制御チップがショートした。 - 損傷したチップは現地で修復され、2つの機能的MCUが得られた。 - テスラ公開電気参照書に必要なケーブル部品番号が記載されている。 **今後起こりうること(本文中の将来展望/予測)** 筆者はMCUのユーザーインターフェース、ネットワークインターフェース、CANバスを探索し、システム稼働時にファームウェアを抽出する計画だ。 **影響(利用者・企業・業界への影響)** SSH(ポート 22)またはREST‑ライクAPI(ポート 8080)でMCUにアクセスできれば、研究者はテスラのバグバウンティ「Root Access Program」のための根本的脆弱性を特定し、車両セキュリティ向上に寄与する可能性がある。

2026/03/26 3:16

ARC‑AGI‑3(アーク・AGI・3)

## Japanese Translation: ARC‑AGI‑3は、AIエージェントを真に適応的かつ継続的な学習へと導く新しい対話型推論ベンチマークです。モデルに探索・目標追求・環境変化への世界モデリングを課し、単発の回答ではなく効率的なスキル獲得と長期計画を評価します。完璧なスコア(100 %)は、エージェントがすべてのゲームで人間よりも優れたまたは同等の性能を示し、多様なタスクにおける習熟度を証明することを意味します。 ベンチマーク設計は、事前学習済み知識なし、明確な目標、有意義なフィードバック、およびブルートフォース記憶化を防ぐ新規性を重視しています。開発者向けには、エージェントの意思決定が構造化されたタイムラインに記録される再生可能実行、使いやすいAPI、環境アクセスとエージェント統合用の包括的ドキュメント、およびリアルタイムでエージェント挙動を確認できるUIが提供されています。 ARC‑AGI‑3は迅速な反復と透明性のある評価を奨励し、研究者が多様なシナリオで継続的に学習可能なAIシステムを構築する手助けとなります。ユーザーはプラットフォーム上のインタラクティブインターフェースを通じて「エージェントをテストしよう!」と呼びかけられ、プレビュー再生でエージェント挙動を反復的にテスト・検査できます。

2026/03/26 5:27

EUは、依然としてあなたの個人メッセージや写真をスキャンしようとしています。

## Japanese Translation: ## 改訂概要 保守党(欧州人民党)は、無差別スキャンに関する議会の以前の「NO」決定を覆すため、4月26日木曜日に新たな国会投票を求めています。彼らはこの決定を逆転させることが民主主義への攻撃であり、プライバシー権を明白に無視する行為だと主張し、「No means no」というスローガンのもと支持者を集結させています。この要求は、議会が無差別スキャンを承認しなかったことに続くものであり、保守党はこれを政府の過剰介入として捉え、民主主義原則および個人プライバシーを侵害するものと見ています。今後の投票結果は、そのような広範なデータ監視ツールが実施可能かどうかを決定し、市民のプライバシー保護を再構築するとともに、管轄区域内のテクノロジー企業の規制慣行にも影響を与える可能性があります。

Unity を見てみたおかげで、C++ のコルーチンが何を目的としているのか理解できました。 | そっか~ニュース