質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

2700閲覧

Unity:処理できる時間に依存してフリーズするのかどうか?

shimazu

総合スコア38

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2017/07/31 08:18

(一般的なコードではありませんが,Unityの処理については一般的な問題だと思うので質問させていただきます)

##前提
Unityで以下の取り組みをしています。簡単に言えば,人がAIを構築して対戦するゲームで,人ではなく自動生成でAIを構築して対戦させようとする取り組みです。
https://github.com/Tsunehiko511/project-puppeteer

・ファイルの説明設定
Setting.csでプレイヤーのデータを設定して,GameMaster.csのPlay関数で対戦させています。

・問題
Game.Play関数を実行する中で,ある関数の処理をInvoke("ある関数", 0)に変えた場合と変えない場合でUnityの処理時間に違いがありました。Invokeを使えばスムーズに処理されInvokeを使わなければ,ややフリーズしてからコンソールに結果が表示されます。(リンク先のプロジェクトファイルをインポートすれば同様の環境が構築できます)

##実現したいこと
Game.Play関数を高速に処理したいです。そのためにInvoke関数を排除したいのですが,排除するとUnityの処理がややフリーズしてしまいます。これらの原因と解決方法を教えてください。

###補足情報
以下のリンクによれば,Invoke関数で0秒を指定した時は,呼び出された場所の最後に実行されているのかなと思っています。
http://www.urablog.xyz/entry/2016/08/27/103229
このことから,Unity上で関数の処理を行う時に何らかの時間的制約があるのではないかと思っています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Invokeを使わないバージョンだと、PushTestButton内の処理(多数のNextPhaseの再帰呼び出し)が全部終わるまでUIに処理が戻らず、フリーズしたような動きになるんじゃないでしょうかね?
一方Invokeを使った場合だと、早い段階でPushTestButtonを終えているように見えました。

リファレンスにもInvokeの動きについては詳しく書かれておらずよくわからなかったのですが、もしかするとInvokeあり版だと処理の実行順が狂って、まずいことになっているかもしれません(Game.Play(player1, player2);の直前にDebug.LogFormat("Play {0}", i);と入れてみたら、Play 0の終了を待たずにPlay 1がスタートしている様子でした)。
呼び出しの階層を中かっこで表現すると、

PushTestButtonを実行 { 1回目のPlayを実行 { MainPlayを実行 { NextPhaseを実行 { NextPhaseを実行 { (中略、再帰的にNextPhaseが呼び出されていく) NextPhaseを実行 { Invoke("NextPhase", 0f)...NextPhaseの実行を予約、このNextPhaseの実行は終了 } (中略、呼び出しスタックをさかのぼっていく) } } } } 2回目のPlayを実行 { MainPlayを実行 { NextPhaseを実行 { NextPhaseを実行 { (中略、再帰的にNextPhaseが呼び出されていく) NextPhaseを実行 { Invoke("NextPhase", 0f)...NextPhaseの実行を予約、このNextPhaseの実行は終了 } (中略、呼び出しスタックをさかのぼっていく) } } } } } この時点でPushTestButtonは処理を終え、ボタンの状態が元に戻る? 予約されたNextPhaseを実行...この中でInvokeが行われると、さらに予約キューにNextPhaseが追加されていく? (中略) ある時点でplay_modeが"GAME_OVER"になると、以降のNextPhaseはGameOverPhaseを実行して処理を終える

こんな動きになってしまっている気がします。戦闘結果が妥当かどうかまでは確認していないのですが、問題はなさそうでしょうか?

投稿2017/07/31 14:32

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shimazu

2017/07/31 21:44

ご指摘の通り,Invokeを使用すると処理を待たずに次を実行していました。 一回の戦闘結果についてはどちらもあっています。あっているという判断はコンソールに表示されているのが 残り時間:赤組の残りユニット数:赤組のキングHP 残り時間:青組の残りユニット数:青組のキングHP であり,Invokeの使用に関わらず対戦結果(実際にゲーム内で確認したデータ)と同じです。 Game.Play(player1, player2)なら 37:My_Left_Count:4:398 37:Enemy_Left_Count:0:115 Game.Play(player2, player1)なら 7:My_Left_Count:0:-59 7:Enemy_Left_Count:2:173 (なお,以前のコードでは赤と青の指定でミスがあったので修正しました。githubから最新のコードを取得し書き換えていただけると同じ環境になります。) ただし,2連続して対戦すると,Invokeを使用すれば前の処理が終わる前に次の対戦を行なっているように見え,最後に行なった対戦の結果が2回表示されました。一方Invokeを使用しないなら,ちゃんとした結果が出ました。 このことから,Invokeを呼ぶことによって,並列的な処理を行なっているので処理が速いと理解したらいいでしょうか?(一回の対戦結果に影響しないのは,たまたま処理が邪魔されていないだけ?) 高速化に関して:Invokeを使った方が遅くなるんじゃないかと思っていたのですが,1回の処理が速くなっているのなら,Invokeを使用して「対戦が終わったら次の対戦をする」という工夫をすれば高速化できる気がしてきました。
Bongo

2017/07/31 22:36

Invokeがマルチスレッドで動作するかは情報を見つけられませんでしたが、コルーチンについてはシングルスレッドで動くらしいことを書いている記事(http://techblog.kayac.com/unity_advent_calendar_2016_8、http://posposi.blog.fc2.com/blog-entry-220.html、http://tsubakit1.hateblo.jp/entry/20121110/1352555965)がいくつか見受けられました。 UnityのAPIもメインスレッドからしか使えないようなことが書かれており、類推するとInvokeも並列実行されるわけではない(例えばPlayをInvokeした直後にもう一つPlayをInvokeしても、二つのPlayが複数のCPUコアを使って同時に実行されるわけではない...つまり全体の時間短縮にはつながらない)ように思われます。 マルチスレッド化したい場合、上記の各記事によるとUnityのAPIが使えない制限があるものの、Threadで実現できるようです(http://nyahoon.com/blog/315もご参考になりそうです)。今回のケースですと、戦闘処理部分にUnityのAPIに依存する箇所はなさそうですので、マルチスレッド化が可能かもしれません。これなら全体の処理時間を短縮できるのではないでしょうか。 ただ、現状では質問者さんのおっしゃるように、例えばplay_modeなど共通して使用する変数が上書きされて意図しない動作になるでしょう。スレッドごとに独立した変数を使うなどして競合を回避する工夫を加える必要があるかと思います。
shimazu

2017/08/03 08:09

ありがとうございます。マルチスレッドの知識はないので,とりあえずシングルで攻めてみます。 ただ,やはり速度に問題がありそうなので,マルチ化も想定して行きます。 Invokeについてですが,InvokeをなくしDebug.Logもなくしたところ,Invokeありよりも高速に処理しました。よってInvokeを使った方が速いというのは勘違いでした。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問