I'm trying to figure this out today as well. Engine call awaiter support in the vein of ToSignal could be neat, e.g. await ToComplete(...);
But do you need to use Call
? You might be able to use C# async/await features more directly. I found two ways to do something similar to GDScript's yield until coroutine "completed". At least, these techniques work with my own C# functions...
1) Use Task.StartNew
or Task.Run
to wrap a call and await
that task. Note, this technique will execute the action on a separate thread which may cause some strange interactions with the engine if you call into it. This might be a good option if you have some heavy lifting to do outside of the game world, for example if you need to call web services or compute the ten billionth prime number.
2) Use a regular C# async method and await a direct C# call to it. This will execute on the same thread and is usually what I want when breaking down game logic into composable asynchronous functions (coroutines). Basically, you let C# build the state machine and continuation flow for you...
async Task<bool> ConfirmPlayerLikesChocolate() {
// Tween in the question label...
await TweenLabel("Do you like chocolate?");
// Pop the "Yes!" button.
await FadeInButton(0.1f, "Yes!");
// Wait 5 seconds to fade in the "No." button...
await FadeInButton(5.0f, "No.");
// Wait for player to select...
// Ignore buttons because nobody dislikes chocolate.
return true;
}
async Task AnnoyPlayer() {
bool likesChocolate = await ConfirmPlayerLikesChocolate();
if (!likesChocolate) {
// CRASH!
} else {
// Oh yeah? What's your favorite kind of chocolate?!
}
}
Note this brings the usual benefits of C#: composability, name checking, type safety, etc. Assigning the await expression to int instead of bool produces compile-time error.
You can write async void functions but can't await them. Just change the return type to Task
. Or, if you need to produce a result, Task<TResult>
and return something of that type as usual.