Autoボタンを押すと、話が進む。画面をクリックするとAutoシステムが停止する。
復活から1秒間は無敵時間を作るように、
ゲーム開発では、何秒後に処理を行う場合があります。
Unityでは、指定した時間後に処理を行う方法として、
「Invoke」メソッドとコルーチン(Coroutine)があります。
本記事では、Invokeとコルーチンの違いに触れて、どの場面でどちらを使うのが効果的か紹介します。
- コールチンについて知りたい。
- Invokeとの違いを知りたい。
Invoke
Invokeは、Unityで特定の時間が経過した後にメソッドを一度だけ実行するシンプルな方法です。
Invoke("メソッド名", 遅延時間)
一例として、MessageFnメソッドを実行するようにします。
5秒後にConsoleウィンドウにテキストを表示します。
using UnityEngine;
public class Invoke_Example : MonoBehaviour
{
void Start()
{
// 5秒後にMessageFnメソッドを呼び出す
Invoke("MessageFn", 5f);
}
void MessageFn()
{
Debug.Log("5秒経過しました");
}
}
Invokeは他にも繰り返しの処理InvokeRepeating、
Invokeの処理を停止するCancelInvokeなどがあります。
操作も簡単ですが、複雑な処理には不向きです。
コルーチン(Coroutine)
コルーチンは、複数のフレームにまたがって実行されるタスクを扱うための方法です。
Unityだけではなく、他のプログラミング言語でも用いられる概念です。
複数フレームにわたる処理や、一定間隔で繰り返し実行する処理に適しています。
また、yield returnを使用して、任意のタイミングで一時停止や再開ができます。
Invokeよりもコード数が多くなりますが、複雑な処理ができることを示しています。
yield return null
コルーチンの実行を次のフレームまで一時停止します。
Start FrameとNext Frameのログ出力は異なるフレームで行われます。
using UnityEngine;
using System.Collections;
public class Sample1 : MonoBehaviour
{
void Start()
{
StartCoroutine(Sample1Coroutine());
}
IEnumerator Sample1Coroutine()
{
Debug.Log("Start Frame: " + Time.frameCount);
// 次のフレームまで待機
yield return null;
Debug.Log("Next Frame: " + Time.frameCount);
}
}
yield return new WaitForSeconds(seconds)
指定した秒数だけ待機するために使用します。
Invokeに似ている処理をします。
Before WaitとAfter Waitの間に2秒の待機時間が存在します。
using UnityEngine;
using System.Collections;
public class Sample2 : MonoBehaviour
{
void Start()
{
StartCoroutine(Sample2Coroutine());
}
IEnumerator Sample2Coroutine()
{
Debug.Log("Before Wait: " + Time.time);
// 2秒待機
yield return new WaitForSeconds(2f);
Debug.Log("After Wait: " + Time.time);
}
}
yield return new WaitUntil(() => condition)
指定した条件が満たされるまで待機するために使用します。
Sample3CoroutineはisConditionMetがtrueになるまで待機します。
ChangeConditionAfterDelayコルーチンが3秒後に条件を変更します。
using UnityEngine;
using System.Collections;
public class Sample3 : MonoBehaviour
{
private bool isConditionMet = false;
void Start()
{
StartCoroutine(Sample3Coroutine());
StartCoroutine(ChangeConditionAfterDelay());
}
IEnumerator Sample3Coroutine()
{
Debug.Log("Waiting for condition to be met...");
// 条件が満たされるまで待機
yield return new WaitUntil(() => isConditionMet);
Debug.Log("Condition met!");
}
IEnumerator ChangeConditionAfterDelay()
{
// 3秒待機
yield return new WaitForSeconds(3f);
isConditionMet = true;
Debug.Log("Condition changed!");
}
}
Auto機能
Auto機能には、Invokeを使うことを理解したところで、ここからは、ソースコードにInvokeとonclickを追加していきます。
今回は、csvの読み込みを左クリックと、Autoの2種します。
つまり、csvを表示する同じコードを2回書く必要があります。
void Update()
{
if(Input.GetMouseButtonDown(0))
{
A.text = csvData[i][0];
B.text = csvData[i][1];
}
}
void Auto()
{
A.text = csvData[i][0];
B.text = csvData[i][1];
}
もちろん、2回書いても正しく処理されますが、スクリプト全体が長くなってスマートではないです。
したがって、今回は、関数「Loadcsv()」にcsvを呼び起こすようにして、それぞれに渡して処理をします。
ソースコード
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using UnityEngine.EventSystems; //ボタンがクリックで反応する
public class csvcontroler : MonoBehaviour
{
public TextAsset csvFile; //CSVファイル
public static List<string[]> csvData = new List<string[]>(); //csvファイルの中身を入れるリスト
public static int i = 0;
bool Auto;
public Text Nametext;
public Text Logtext;
public Image LeftImg;
public Image RightImg;
private AudioSource audio;
private AudioClip Sound;
public Button AutoBtn;
void Start()
{
//csvファイル
csvFile = Resources.Load("Data") as TextAsset; //Resourcesにあるcsvファイルを格納
StringReader reader = new StringReader(csvFile.text); //TextAssetをStringReaderに変換
while (reader.Peek() != -1)
{
string line = reader.ReadLine(); //1行ずつ読み込み
csvData.Add(line.Split(',')); //csvDataリストに追加
}
//音声を取得
audio = GetComponent<AudioSource>();
}
void Update()
{
//ボタンを押したときは画面クリック無効
if (EventSystem.current.IsPointerOverGameObject())
{
return;
}
//左クリックで Loadcsv();メソッドを読み込む
if (!Auto && Input.GetMouseButtonDown(0)) //!Autoは、左クリックでAuto解除するときに処理しないように
{
Loadcsv();
}
//Autoシステム解除
if (Input.GetMouseButtonDown(1) || Auto && Input.GetMouseButtonDown(0)) // 右クリックを検知
{
Auto = false; // 自動モード停止
AutoBtn.image.color = Color.white; // 色を元に戻す
Debug.Log("停止!" + "フラグ::::" + Auto);
}
}
//CSVを呼び出す(ローカルのみの処理)
private void Loadcsv()
{
Debug.Log("人物:" + csvData[i][0] + "セリフ:" + csvData[i][1]);
Nametext.text = csvData[i][0];
Logtext.text = csvData[i][1];
LeftImg.sprite = Resources.Load<Sprite>(csvData[i][2]);
RightImg.sprite = Resources.Load<Sprite>(csvData[i][3]);
//音声データ
Sound = (AudioClip)Resources.Load(csvData[i][4]);
audio.PlayOneShot(Sound);
if (i <= csvData.Count)
{
i++;
}
}
//Autoシステム
public void Autobtn()
{
Invoke("Automode", 2.0f); //2秒後に"Automode"メソッドを読み込む
Auto = true;
Debug.Log("開始!" + "フラグ::::" + Auto);
AutoBtn.image.color = Color.gray; //ボタンの色をグレーにする。
}
//Invokeで使う
void Automode()
{
if (!Auto)
{
return; // Autoがfalseなら処理を中断
}
Loadcsv();
// Autobtnを押し続けるスクリプト
AutoBtn.onClick.Invoke();
}
}
UdemyでUnityを学習
Udemyは、オンデマンド式の学習講座です。
趣味から実務まで使えるおすすめの講座を紹介します。
ビックセール開催中(10月25日まで)
対象のコースが1800円から(最大95%OFF)
- 【Unity C# ゲーム開発超入門】7つのミニゲームを作っておぼえる!UnityとC#講座
Unityで頻出の機能を実際のゲーム作りで学習。
何か作りたい、今後の開発で役立てたい初心者におすすめ - 【Unity C# ゲーム開発初心者レベルアップ】7つのトランプゲームを作っておぼえる!UnityとC#講座
トランプやボードゲーム作りに特化している講座。
テーブルゲームを作りたい人におすすめ。 - 【全行程を網羅!最初に学びたい総合学習】Unityワールド制作講座
Unityワールド制作の全工程を学習できる講座。
RPGを作りたい人におすすめ。 - Unity ゲーム開発:インディーゲームクリエイターが教える C#の基礎からゲームリリースまで【スタジオしまづ】
C#の文法やApp StoreとGoogle Playへゲームをリリース方法を解説。
ゲームを出品したい人におすすめ。
解説
初めに、名前空間を追加します。
「Unity」に搭載されているUIを使用するため、「using UnityEngine.UI;」を追加します。
csvを読み込んだり、書きこんだり、データを列挙できるようにする「using System.IO;」の追加絵押します。
UIを押したときに同時に画面押下の判定が出ないように「using UnityEngine.EventSystems;」を追加します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using UnityEngine.EventSystems; //ボタンがクリックで反応する
続いて、変数を定義します。
「csvFile」は、CSVファイルを格納するためのTextAsset型の変数です。
「csvData」は 、CSVファイルの内容を格納するためのList型の変数です。
「i」は、現在のCSVデータのインデックスを表すint型変数です。
「Auto」は、オートモードの有無を表すbool型変数です。
「Nametext」、「Logtext」、「LeftImg」、「RightImg」は、それぞれテキストと立ち絵を表示するUI変数です。
「audio」は、音声再生のためのAudioSourceコンポーネントを格納する変数です。
「Sound」は、音声データを格納するためのAudioClip型の変数です。
「AutoBtn」は、自動モードを切り替えるためのボタンを格納する変数です。
public TextAsset csvFile; //CSVファイル
public static List<string[]> csvData = new List<string[]>(); //csvファイルの中身を入れるリスト
public static int i = 0;
bool Auto;
public Text Nametext;
public Text Logtext;
public Image LeftImg;
public Image RightImg;
private AudioSource audio;
private AudioClip Sound;
public Button AutoBtn;
Startメソッドでは、CSVファイルの内容を読み込んでcsvDataリストに格納し、音声再生のためのAudioSourceコンポーネントを取得します。
Resources.Loadメソッドを使用して、「Data.csv」ファイルを読み込みます。
読み込んだファイルはTextAsset型の変数csvFileに格納します。
TextAsset型の変数csvFileのtextとして、CSVファイルの内容を文字列として取得します。
取得した文字列をStringReaderに渡し、StringReaderオブジェクトreaderを作成します。
StringReaderは、文字列を行単位で読み込むためのクラスです。
while文にて、reader.Peek()メソッドを使用して、次の文字が存在するかどうかを確認します。
Peekメソッドは、次の文字を読み込まずに取得します。
戻り値が-1でない場合、まだ読み込むべき行があることを意味します。
reader.ReadLine()メソッドを使用して、CSVファイルから1行分のデータを読み込みます。
読み込んだデータは、文字列変数lineに格納します。
line.Split(‘,’)を使用して、読み込んだ行の文字列をカンマで分割し、文字列配列として取得します。
取得した文字列配列をcsvDataリストに追加します。
GetComponent()メソッドを使用して、現在のゲームオブジェクトにアタッチされているAudioSourceコンポーネントを取得します。
取得したコンポーネントは、変数audioに格納します。
void Start()
{
//csvファイル
csvFile = Resources.Load("Data") as TextAsset; //Resourcesにあるcsvファイルを格納
StringReader reader = new StringReader(csvFile.text); //TextAssetをStringReaderに変換
while (reader.Peek() != -1)
{
string line = reader.ReadLine(); //1行ずつ読み込み
csvData.Add(line.Split(',')); //csvDataリストに追加
}
//音声を取得
audio = GetComponent<AudioSource>();
}
Updateメソッドでは、入力した時に、ユーザーの入力に対する処理を行います。
「EventSystem.current.IsPointerOverGameObject()」でUIボタンを押しても、画面押下判定がされないようにします。
「!Auto」は、「Auto == false」を意味しています。Autoがfalseのときに左押下すれば、Loadcsv()関数が読み込まれます。
対して、Autoがtrueで左押下した時、あるいは右クリックしたときはオート機能を停止して、UIボタンを元に戻します。
void Update()
{
//ボタンを押したときは画面クリック無効
if (EventSystem.current.IsPointerOverGameObject())
{
return;
}
//左クリックで Loadcsv();メソッドを読み込む
if (!Auto && Input.GetMouseButtonDown(0)) //!Autoは、左クリックでAuto解除するときに処理しないように
{
Loadcsv();
}
//Autoシステム解除
if (Input.GetMouseButtonDown(1) || Auto && Input.GetMouseButtonDown(0)) // 右クリックを検知
{
Auto = false; // 自動モード停止
AutoBtn.image.color = Color.white; // 色を元に戻す
Debug.Log("停止!" + "フラグ::::" + Auto);
}
}
Loadcsv()では、格納したcsvのデータを呼び起こします。
メソッドには、privateを使用して本スクリプト内からしか呼び起こせないようにしています。
このようにすることで、他スクリプトから干渉を受けずに処理することができます。
public、serializefieldとの違いが分からない場合は、以下の記事を読んでください。
//CSVを呼び出す(ローカルのみの処理)
private void Loadcsv()
{
Debug.Log("人物:" + csvData[i][0] + "セリフ:" + csvData[i][1]);
Nametext.text = csvData[i][0];
Logtext.text = csvData[i][1];
LeftImg.sprite = Resources.Load<Sprite>(csvData[i][2]);
RightImg.sprite = Resources.Load<Sprite>(csvData[i][3]);
//音声データ
Sound = (AudioClip)Resources.Load(csvData[i][4]);
audio.PlayOneShot(Sound);
if (i <= csvData.Count)
{
i++;
}
}
最後に、Auto機能を作成します。
Autobtnメソッドは、自動モードを開始するためのボタンがクリックされたときに呼び出されます。
Invokeメソッドを使用して、2秒後にAutomodeメソッドを呼び出します。
Autoをtrueに設定し、自動モードが開始されたことを示します。
ボタンの色をグレーに変更して、動作中であることを画面上で可視化します。
onclickでAutobtnをアタッチする必要があるので、Autobtnメソッドはprivateではなくpublicにしてください。
Automodeメソッドは、自動モードが有効な場合に周期的に呼び出されます。
Autoがfalseの場合、処理を中断して返ります。これにより、自動モードが解除された場合に処理が停止します。
Loadcsvメソッドを呼び出して、次のCSVデータを読み込みます。
AutoBtn.onClick.Invoke()を使用して、Autobtnメソッドを再度呼び出すことで、自動モードを連続して実行します。
//Autoシステム
public void Autobtn()
{
Invoke("Automode", 2.0f); //2秒後に"Automode"メソッドを読み込む
Auto = true;
Debug.Log("開始!" + "フラグ::::" + Auto);
AutoBtn.image.color = Color.gray; //ボタンの色をグレーにする。
}
//Invokeで使う
void Automode()
{
if (!Auto)
{
return; // Autoがfalseなら処理を中断
}
Loadcsv();
// Autobtnを押し続けるスクリプト
AutoBtn.onClick.Invoke();
}
}
実演
今回のスクリプト「csvcontroler.cs」をHierarchyウィンドウにアタッチをして、対応するUIをInspectorウィンドウにアタッチしてください。
前回からスクリプトにアタッチするUIを増やしていないので、そのままで大丈夫です。
また、AutoBtnを選択して、onclickの項目でAutobtn()メソッドをアタッチしてください。
ここまで完了しましたら、再生ボタンを押してください。
冒頭のような処理が行われば、成功です。
まとめ
ノベルゲームでオート機能を実装すると、テキストが自動的に表示され、一定の速度で自動的に進行します。
オートモードを導入すると、プレイヤーがわざわざクリック操作をせずにストーリーの流れやイベントの展開を自動的に楽しむことができます。
Unityで導入するには、Invokeを使い、引数を渡して数秒後に動作させます。