昨日書いた様に、3D脱出ゲームはちょっと休憩して、今日はUniTaskの実験。
Unityroomのゲームジャムに参加した際、どうせならとUniTaskを導入して使ってみた。
敵キャラの動作をコルーチンの代わりにasync/awaitでやってみたのだけれど、なぜか上手くいかない。
敵キャラはオブジェクトプールを使って、同じオブジェクトを使い回す様に作ったけれど、敵がやられてオブジェクトプールに返って、
その後に再生成されると、async/awaitが動いてくれなかった。
もっとしっかり解決方法を調べたかったけれど、ゲームジャムは一週間と言う期限があったので、仕方なくコルーチンに戻して完成させる事にしたけれど、
ちょっとモヤモヤが残ってしまったので、早いうちにきちんと実験しておきたかった。
その時のスクリプトはこんな感じ。
public async void SetParameter() { if(cts==null) { cts=new CancellationTokenSource(); Token=cts.Token; } Moving(Token); } async UniTask Moving(CancellationToken token) { //省略〜 //〜省略 await UniTask.Yield(PlayerLoopTiming.Update, token); } //unitaskのキャンセル private void OnDisable() { cts.Cancel(); } private void OnDestroy() { cts.Cancel(); } }
結論から言うと、この処理がいけなかった様だ。
if(cts==null) { cts=new CancellationTokenSource(); Token=cts.Token; }
一度、CancellationTokenを作っておけば、それを使いまわせるんじゃないかと思い、cts変数の中身を確認して、空でなければ
CancellationTokenを再生成しないようにしていた。
どうも、この処理が余計だった様で、このifを外すと、オブジェクトプールから再生成した時にasync/awaitが動いてくれない問題はあっさり解決してしまいました。
でも、まだちょっと気になる点があります。
オブジェクトの数とUniTaskの数が合っていません。
実は、先ほどのifは、この解決になるのではと試行錯誤していた名残だったわけです。
で、色々と実験しているうちに、async/awaitのメソッドを呼ぶときに.forgetを使うとこの問題が解決する事に気づきました。
public async void SetParameter() { cts=new CancellationTokenSource(); Token=cts.Token; Moving(Token).Forget(); }
このforgetを使うことで、数が合わなくなることは、一応解決しました。
けれど、なぜそうなるのかは理解できませんでした。
ちなみに、forgetの意味をCopilotくんに聞いてみると、
Copilot .Forget()は、非同期メソッドを投げっぱなしで実行する際に便利なメソッドです。以下に詳細を説明します。 .Forget()の役割: UniTaskを非同期的に実行する際、awaitを使わずに非同期処理を投げっぱなしで実行したい場合があります。 このとき、コンパイラが「awaitしていない非同期メソッド」に対して警告を出します。 .Forget()を使うことで、この警告を抑制できます。 注意点: .Forget()を使う際、非同期メソッド内で発生した例外はキャッチされません。 例外処理が必要な場合、適切にハンドリングするようにしてください。
との回答で、今の疑問についての説明になっていないのではないかと思います。
色々と記事を探してみましたが、結局、コレと言う記事を見つける事はできませんでした。
UniTaskについては、まだまだ判らない事だらけですが、とりあえず最初に話していた、「オブジェクトプールから再生成した時にasync/awaitが動いてくれない問題」は解決したので、これからも色々と使ってみようと思います。