Unity:少し特殊なストップウォッチアプリ制作日記_2:フレームに換算する

・まず問題は何か

 さてミリ秒単位で時間を計ることはできた。次にその計測した時間を24、30、60といったフレームに変換する部分を考えなくてはならない。いやそれはわかる。秒以下の単位を1000で割るのを24で、30で60で割ればいい。問題はその各モードをどうやって管理して切り替えるかだ。プログラマのプの字にもなれていない自分はもうここで悩み始めた。

 さてUnityのUIにはトグルという機能を持ったボタンがある。これは押す度にオンとオフが切り替わるだけの電灯のスイッチみたいなボタンだ。まずこれを計測のモード切り替えに使うことにした。

・切り替え用のボタンを用意する

 UIからトグルボタンをデフォルト(1000)、24、30、60のフレームで切り替える用に4つ用意する。

スクリーンショット 2021-07-20 032212

 ボタンを作ると最初は小さなチェックボックスが表示される。上の画像にあるボタンは中のパーツの大きさや位置を変えたもので中身は同じだ。まず絵は仮でいいので必要なものを揃えて配置を決める。
 ボタンを押すと、それぞれのボタンでチェックマークのオンオフが切り替わる。ただしやりたいのはどれかをオンにすると他がオフになるような挙動で、もちろんそれを実現する機能はちゃんとある。
 ボタンの1つ上の階層に、ここではFrameRateSwitchと名付けたnullの階層を作り、ToggleGroupというComponentを付ける。そして下の4つのボタンに同じToggleGroupの指定をすると常にどれか1つのボタンだけがオンになるように動いてくれる。まさにUnityさまさまだ。起動時の「Made with Unity」の文言を「Unityのおかげです」に変えたいくらいだ。

・じゃぁ、どうやって実装しよう?

 さて見た目だけは機能するモードの切り替えスイッチができた。次にこのスイッチで実際にフレームレートの数値を切り替えられるようにしなくちゃいけない。さてどうするか。最初はこんな小さな所でも躓く。ド素人を舐めてもらっちゃ困る。

 まずやるべきことを整理すると、どこかに用意した「フレームレート」というパラメータを、押したボタン毎に決められた1000/24/30/60という数字に書き換える。ということになる。

 フレームレートのパラメータはメインのスクリプトに持たせるのがまぁ妥当だろう。そして各スイッチに「ボタンが押されたとき、メインのスクリプト中にあるフレームレートの数字を書き換える」スクリプトをくっつければ良いはずだ。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FrameRateSet : MonoBehaviour
{
   //各ボタン毎のフレームレートの指定値
   public int frameRateSet;

   //フレームレートの値を持っているオブジェクトを格納するための変数
   //これはメインのスクリプトが付いているGameObjectを指定する
   public GameObject mainObject;

   //次にcurrentFrameRateのパラメータを持つTimeCountスクリプトを指定するための変数
   TimeCount timeCountScript;


   // Start is called before the first frame update
   void Start()
   {
       //currentFrameRateの値を持つTimeCountスクリプトをMainObjectから取得する
       timeCountScript = mainObject.GetComponent<TimeCount>();
   }

   //ボタンが押された時の処理
   public void FrameRateValueSet()
   {
       //フレームレートをボタン指定の値で上書きする。
       timeCountScript.currentFrameRate = frameRateSet;
   }

   // Update is called once per frame
   void Update()
   {
       
   }
}

 これがボタンに付くスクリプトで、メインのスクリプトがついているオブジェクトを取得して、さらにそのオブジェクトに付いているスクリプトを名前で取得する。そうすれば目的のパラメータまでたどり着ける。
 そしてボタンが押された時用の関数を作って、そこでお目当てのパラメータにボタン毎の数値をブチ込めば勝利できる。

スクリーンショット 2021-07-20 033758

 ボタンが押された時の処理はインスペクター上で設定する。ボタン自身に貼り付けられているさっき用意した専用の処理を行うようにすればいい。

 メインのスクリプト側にフレームレート用の変数を用意して、あとはその値を結果に反映すればいい。他は表示を整えたりとか。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TimeCount : MonoBehaviour
{
   private float time;
   //表示するテキストオブジェクトを取得。
   //publicで放り込むことでテキストオブジェクトをFindしたりGetComponentする必要がない。
   public Text text;

   //フレームレートを表すパラメータ。各ボタンから値を書き換えられる
   public int currentFrameRate;


   // Start is called before the first frame update
   void Start()
   {
       time = 0f;

       //フレームレートの初期値(ミリ秒で計測)
       currentFrameRate = 1000;
   }

   // Update is called once per frame
   void Update()
   {
       //毎フレームの経過時間を取得
       time += Time.deltaTime;

       //表示用のテキスト形式の時間を用意
       string timeText;

       //秒カウント用の数字
       int second;

       //int化して小数点以下を切り捨て
       second = (int)time;

       //ミリ秒カウント用の数字
       int msecond;

       //n倍にした数字をnで割った余り、更に整数化すれば1秒以下の端数(フレーム数)が手に入る。
       msecond = (int)(time * currentFrameRate % currentFrameRate);

       if(currentFrameRate == 1000)
       {
           //ミリ秒表示の場合、小数点以下は3桁固定になるよう指定
           timeText = second.ToString() + "." + msecond.ToString("000");
       }
       else
       {
           //フレーム表示の場合は小数点以下を2桁+"F"を付ける
           timeText = second.ToString() + "." + msecond.ToString("00") + "F";

       }

       //Debug.Log(script.frameRate);
       text.text = timeText;
   }
}

 でこれらをまとめて

スクリーンショット 2021-07-20 040323

 ちゃんと動いてるっぽい!

 では次はUnityのスタート/一時停止でなく、ゲーム画面中にボタンを用意して、ストップウォッチに必要なスタート/ストップ/リセットが出来るようにしよう。なんとなく完成が見えてきた。

この記事が気に入ったらサポートをしてみませんか?