HN
Today

Looking at Unity made me understand the point of C++ coroutines

The author shares their "aha!" moment regarding C++ coroutines, realizing their power for managing complex, sequential game effects by observing Unity's C# implementation. This practical example, moving beyond abstract async I/O, resonates with developers seeking concrete applications for intricate language features. The post provides a lightweight C++ implementation, demonstrating how to simplify what would otherwise be cumbersome state machines, hitting a sweet spot for Hacker News's technically curious audience.

54
Score
29
Comments
#3
Highest Rank
10h
on Front Page
First Seen
Mar 25, 10:00 AM
Last Seen
Mar 25, 7:00 PM
Rank Over Time
663111889121212

The Lowdown

The article explores the practical utility of C++ coroutines, a feature the author initially struggled to grasp beyond async I/O. Their "aha!" moment came from observing Unity's C# coroutine usage in game development for managing ephemeral behaviors and effects.

  • Unity's C# IEnumerator coroutines allow developers to write sequential, time-dependent logic (like fading an object or a sequence of movements) in a clean, imperative style.
  • The author highlights how implementing such multi-step, frame-dependent behaviors without coroutines typically devolves into complex, hard-to-manage state machines with switch statements and explicit state variables.
  • C++23's std::generator offers a solution, enabling similar imperative-style code with co_yield to suspend execution, handing control back to a caller for later resumption.
  • A key distinction is drawn between co_yield (relatively straightforward, akin to the "Unity hack") and co_await (much harder to implement, requiring a comprehensive execution framework like the upcoming C++26 execution proposal or custom promise types).
  • The author then presents a minimal, less-than-100-line effects_manager in C++ that emulates Unity's co_yield-based coroutine system, running effects frame-by-frame.
  • A bonus section illustrates how this system can be adapted to generate renderable objects directly from coroutines, allowing for easy parallelization, further showcasing its power for game logic and visual effects.

Ultimately, the piece argues that this "Unity hack" provides a compelling, concrete use case for C++ coroutines that is far more relatable and useful than typical theoretical examples like Fibonacci generators.

The Gossip

Coroutine Conundrums and C++ Quibbles

Commenters debate the merits and complexity of C++20 coroutines. Some argue they are "over-engineered" and lead to "template hell," preferring simpler stackful coroutines implemented with assembly or `setjmp/longjmp` for portability and avoiding "colored functions" and heap allocations. Others view C++ coroutines as a necessary evolution for managing complex control flow, especially when contrasted with verbose state machines or callbacks. The difficulty of integrating `co_await` with existing concurrency solutions is a common point of concern, with many noting the need for better library support.

Unity's Yielding Saga

There's considerable discussion about Unity's historical use of `IEnumerator` for coroutines, with some calling it an "ancient" and "hacky" approach due to its roots in older C# versions lacking `async/await`. However, others defend it as a "good hack" that was effective for its time. Commenters note Unity's progress, with newer versions (like 2022.3 and upcoming 6.8) finally integrating proper `async/await` support via CoreCLR, which significantly modernizes their scripting capabilities while retaining the legacy `IEnumerator` for compatibility.

Game State Gymnastics

The discussion touches on the broader challenges of managing time-dependent logic and state in game development, often describing it as a "rube goldberg machine." Coroutines are seen by some as an elegant solution, transforming complex state machines into readable sequential code. However, alternative approaches are also considered, such as using `std::future` for async state machines. The concept of "reinversion of control" from Haskell is also brought up as an analogous technique, highlighting different programming paradigms for similar problems.