【Unity】非同期式の入れ子処理で遅延のない時間監視を実現する
IT技術
非同期式制御を用いてゲーム内の時間を監視しよう!
今回は、ゲームを作る上で必要になってくる「制限時間の監視」について紹介したいと思います。
Unity における時間計測の方法は、いくつかあります。
1つ目は、VisualStudioなどのコンパイラにデフォルトでついている「ヘッダーを使う方法」。
2つ目は、「専用のアセットを使う方法」です。
拡張性の問題
この「専用のアセットを使う方法」ですが、もちろん質のいいアセットをインポートして使うことが出来れば良いのですが、それではあまり拡張性がありません。
「時間に合わせて自作のゲージを動かしたり」、「ゲーム上のキャラクターの動きに時間を反映させたり」する場合などは、拡張性が理想的ではありません。
他のオブジェクトに時間を戻り値として返す時に、返すタイミングや型などに、制限がついているためです。
時間計測、監視が出来ない問題
また、通常の時間計測、監視の構文を、void Start(){} や void Update(){} の中で動かしてしまうと、正しい時間計測、監視が出来ない原因となります。
同じ関数の中で動いている構文に、処理時間を費やされたり、メモリを消費されたりしてしまうためです。
今回はそういった遅延が起きないよう、非同期式の入れ子処理で、遅延のない、時間計測、監視の開発していきたいと思います!
開発における注意点
- Unity の ver は「2018 3.14f1」です。2019など、他の年の ver ではエラーが出ることがあります。
- VisualStudio などの情報は2020年3月10日現在のものです。
- 最終的にはアセットなどを一切使わないのでインストールの必要はありません。
一番シンプルな時間計測「非同期式制御」で開発
では早速、「非同期式制御」を用いて遅延のない時間計測、監視の開発をしてみようと思います。
「同期式制御」と「非同期式制御」
まず、「同期式制御」とは、通常の void Start(){} や void Update(){} 文の中で、コードに記載された順番ごとにひとつひとつ処理していくことをいいます。
それに対し、「非同期式制御」は void Start(){} や void Update(){} の中ではない「コルーチン」という方式で関数のようなものを定義します。
その「コルーチン」を void Start(){} や void Update(){} の中に書かれているコードとは別系統で動作させます。
このような形式で動かしているので、他のコードによる処理の影響を受けづらく、遅延のない時間計測を実現できます。
4秒待つだけの制御のコード
1void Start()
2 {
3 StartCoroutine("Limittime");
4 }
5
6 // Update is called once per frame
7 void Update()
8 {
9 }
10
11 IEnumerator Limittime() {
12
13 yield return new WaitForSeconds(4);
14
15 }
非常に短いですね。
IEnumerator Limittime() で、関数のように「コルーチン」を定義しています。
そして、void Start(){} で StartCoroutine("Limittime"); を用いて定義した「コルーチン」を呼び出しています。
using について
スクリプト冒頭の using の部分は、下記のようにスクリプトを作った初期状態のままです。
1using System.Collections;
2using System.Collections.Generic;
3using UnityEngine;
拡張性を生かして制限時間やライフのゲージを表現する
では、次はスクリプトを加工して、ライフゲージを作っていきましょう。
今回は、以下の画像のように、6つのハートの3Dオブジェクトを消していくことで表現していきます。
ソースコード
空のゲームオブジェクトに、以下のようなコードをアタッチします。
1using System.Collections;
2using System.Collections.Generic;
3using UnityEngine;
4
5public class RedeffectCTRL2 : MonoBehaviour
6{
7 public GameObject heart1, heart2, heart3, heart4, heart5, heart6;
8
9 // Start is called before the first frame update
10 void Start()
11 {
12
13 StartCoroutine("Limittime");
14
15 }
16
17 // Update is called once per frame
18 void Update()
19 {
20 //こちらにメインのゲームの処理を書く。
21 }
22
23 IEnumerator Limittime() {
24
25 yield return new WaitForSeconds(4);
26 heart1.SetActive(false);
27 yield return new WaitForSeconds(4);
28 heart2.SetActive(false);
29 yield return new WaitForSeconds(4);
30 heart3.SetActive(false);
31 yield return new WaitForSeconds(4);
32 heart4.SetActive(false);
33 yield return new WaitForSeconds(4);
34 heart5.SetActive(false);
35 yield return new WaitForSeconds(4);
36 heart6.SetActive(false);
37 }
38}
「入れ子」と配列を用いてコードをシンプルに
このままでも十分に遅延の対策はできています。
ですが、先ほどの例の場合、ハートの数がとてつもなく多くすると、スクリプトが長くなってしまいます。
しかし、コルーチンの中で「for文」や、「while文」を回しても元も子もないので、「入れ子」を使っていこうと思います。
入れ子とは
「入れ子」というのは、自分自身の関数を、その関数内で呼び出すことをいいます。
木構造のデータ配列の解析や、ヒープソートなどを行うときにも用います。
同期式制御の関数ではなく、非同期式制御でのコルーチンを使っているので、コルーチンの中でコルーチンを呼び出しても処理速度に影響はありません。
ソースコード(元のソースコードをコメントで表示)
次のコードでは、入れ子の場合とそうでない場合を比較するために、元の場合をコメント文で表示しています。
1using System.Collections;
2using System.Collections.Generic;
3using UnityEngine;
4
5public class RedeffectCTRL2 : MonoBehaviour
6{
7
8 public GameObject eff;
9 public static int i;
10 // public GameObject heart1, heart2, heart3, heart4, heart5, heart6;
11 public GameObject[] hearts;
12 // Start is called before the first frame update
13 void Start()
14 {
15
16 eff.SetActive(false);
17 i = 0;
18
19 StartCoroutine("Limittime");
20 StartCoroutine("Redeffect");
21 }
22
23 // Update is called once per frame
24 void Update()
25 {
26 }
27
28 IEnumerator Limittime() {
29
30 yield return new WaitForSeconds(4);
31 /*
32 heart1.SetActive(false);
33 yield return new WaitForSeconds(4);
34 heart2.SetActive(false);
35 yield return new WaitForSeconds(4);
36 heart3.SetActive(false);
37 yield return new WaitForSeconds(4);
38 heart4.SetActive(false);
39 yield return new WaitForSeconds(4);
40 heart5.SetActive(false);
41 yield return new WaitForSeconds(4);
42 heart6.SetActive(false);
43 */
44
45 //入れ子ver
46 if (i < 7)
47 hearts[i].SetActive(false);
48 i++;
49 StartCoroutine("Limittime");
50 //入れ子ver
51 }
52
53 IEnumerator Redeffect() {
54 yield return new WaitForSeconds(0.5f);
55 eff.SetActive(true);
56 yield return new WaitForSeconds(0.5f);
57 eff.SetActive(false);
58 StartCoroutine("Redeffect");
59 }
60}
最後の部分で、IEnumerator Redeffect(){} というコルーチンを定義しています。
ここでは画面が赤く点滅しているように表現しています。
そして入れ子にした事で、「for文」や「while文」を使った時のように、オブジェクトの配列に対して、アクティブの操作ができるようになっています。
さいごに
今回は、ゲーム制作に欠かせない時間の計測、監視を「非同期式制御」と「入れ子」を使って開発しました。
これで遅延を解消してみてはいかがでしょうか!
こちらの記事もオススメ!
2020.07.28Unity 特集知識編おすすめのゲームエンジン5選実装編※最新記事順に並べています。VR環境でテキストを表示する方法非同期式の入れ子処...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit