【Unity】ストップウォッチを作ってみる

前回、経過時間の取得を記事にしました。
折角なので、時間を使った物を作ってみたいと思います。

時間を使った物と言えば、ストップウォッチやタイマーでしょうか。

今回は、ストップウォッチを作ってみたいと思います。

ストップウォッチと言っても数値を表示するだけになりそうなので、
アナログとデジタルの両方を考えてみたいと思います。


Unityのセッティングをします。
プラットホームはandroidで、キャンバスの設置と白背景をセットします。

背景ができた所でパーツになるオブジェクトを配置します。
StopWatch①.jpg
UIをまとめる為にキャンバスを追加して、CanvasUIにリネーム レイヤーを1にします。

パーツとしては、アナログディスプレイ、デジタルディスプレイ、ボタン類のペアレントを用意します。

アナログディスプレイは、メイン文字盤とメイン針をセットして秒に対応させます。
メイン文字盤の中にある赤いサブ文字盤は分計となります。

針は、支点を下端にする必要があるので、セットする際にYピボットを0にします。
StopWatch②.jpg
これで、回転のz軸の角度を変えると針の下端を中心に回ってくれます。

サブの分計の針もYピボットを0にします。

アナログのパーツができたので、デジタルのパーツを用意します。

デジタルのパーツは、色付き背景とテキストを用意すれば問題ないかと思います。
デジタルの表示は、時:分:秒の表示にします。
秒は、小数点以下2位までの表示とします。

最後は、ボタン類です。
スタート、ストップ、リセットの3つを用意します。

エディターの準備ができたので、スクリプトを組んで行きます。

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

public class StopWatchManager : MonoBehaviour
{
public GameObject MaineNeedle; //秒針オブジェクト
public GameObject SubNeedle; //分針オブジェクト
public GameObject TimeText; //デジタル表示

private Transform mainNeedleangle; //秒針のtransform取得用変数
private Transform subNeedleangle; //分針のtransform取得用変数
private Text timeText; //デジタルテキストの取得用変数

private float movetime; //稼働時間用変数
private float mainangle, subangle; //各針の角度用変数

private bool moving; //ストップウォッチ稼働フラグ

・UnityEngine.UI  ←テキストオブジェクト操作のため
・System  ←TimeSpanを使用するため
NameSpaceに二つの宣言をしておきます。

各種のオブジェクト取得用の変数を用意します。
Updateで動かすので、各種Component取得用の変数も用意します。

あとは、時間経過を保持する変数、針の角度代入変数、ボタン操作用のフラグを用意。

// Start is called before the first frame update
void Start()
{
//各針とデジタルテキストのComponent取得
mainNeedleangle = MaineNeedle.GetComponent<Transform>();
subNeedleangle = SubNeedle.GetComponent<Transform>();
timeText = TimeText.GetComponent<Text>();
}

// Update is called once per frame
void Update()
{
//スタートボタンを押したら稼働する
if (moving)
{
needleMove();
}
}

StartとUpdateは、こんな感じでっす。

続いて、ストップウォッチの機能を作ります。

//各針の動きとデジタルテキストの表示
void needleMove()
{
//ストップウォッチ稼働時間
movetime += Time.deltaTime;

//稼働時間から針の角度を算出
mainangle = movetime * -360 / 60;
subangle = movetime * -360 / 3600;

//各針の移動
mainNeedleangle.localEulerAngles = new Vector3(0, 0, mainangle);
subNeedleangle.localEulerAngles = new Vector3(0, 0, subangle);

//デジタル表示用のテキストを取得
string timetext = TimeSet();
//デジタルテキスト表示
timeText.text = timetext;
}

説明すると、
movetimeは、稼働時の経過時間を格納します。
mainangleは、稼働時間からメイン針の角度を算出しています。
subangleは、同じく稼働時間から分単位の角度を算出しています。

算出方法は、
360度を60秒で一周させるので、360度/60秒で秒当たりの角度が出せます。

deltaTimeは、秒で出力されるので、deltaTime×360/60で
フレーム毎の角度が算出できます。

ちなみに、360度をマイナスにしているのは、360を掛けた場合、針が逆回転するためです。

分計の場合、360度/60分です。

算出する場合、deltaTimeは秒なので、60分も秒に換算する必要があります。
なので、秒換算3600秒で割っています。


続いて、オブジェクトの回転についてですが、
transform.RotateとlocalEulerAnglesのどちらかで回転させる事ができます。

transform.Rotateの場合、オブジェクトの現角度から何度動かすかを指定します。
localEulerAnglesの場合、原点(0度)から何度動かすかを指定します。

deltaTime毎に角度を計算して、transform.Rotateで動かす場合、
deltaTimeの合計と比較すると差が出ます。

これは、deltaTime毎に角度を算出した場合、微妙な誤差が生じます。
誤差を加算していくと、針とデジタルの動きに大きな差ができるので、
deltaTimeの合計から角度を割り出して、EulerAnglesで回転させています。

deltaTime毎の計算で針を動かすと、10分ほどで目で見て分かるくらい
針が進みました。(^.^; オホホホ


デジタルの表示は、時・分・秒を60単位にするので、
新しくメソッドを組んで呼び出します。

//時・分・秒の表示方法を指定
string TimeSet()
{
//各種変数
string timetext = null;
int provtime = 0;
int twodecimal = 0;

//稼働時間から小数点以下を取り出して二桁の整数にする
provtime = Mathf.FloorToInt(movetime);
twodecimal = Mathf.FloorToInt((movetime - provtime) * 100);

//TimeSpanを使って 00:00:00. の文字列を作る
TimeSpan ts = new TimeSpan(0, 0, provtime);
timetext = ts.ToString(@"hh\:mm\:ss\.");

//切り離した小数点以下を文字化する
timetext += twodecimal.ToString("D2");

return timetext;
}

稼働時間(秒)から 時・分・秒の文字列を作ります。
表示は、00:00:00.00 の形にします。

秒からこの形にする時は、TimeSpanを使うと変換できます。
ただ、TimeSpanは整数型らしく、float型を代入できないようです。
C#自体は、小数点以下も指定できるようですが、Unityではエラーがでます。

なので、小数点以下だけ分離して最後に足しています。

TimeSpanは、24時間以上経つと1日と判断するので、
24時間までしかカウントできません。
24時間を過ぎると時間の表示が00に戻ります。

ストップウォッチで24時間以上計る事もないので、
このままで行きます。(;^o^) \(ToT )あんたほんとにそれでいいの


これで時間の表示形式が出来たので、ストップウォッチの機能
測定ボタンの処理を用意します。


//ストップウォッチのスタート
public void WatchStart()
{
moving = true;
}
//ストップウォッチのストップ
public void WatchStop()
{
moving = false;
}
//ストップウォッチのリセット
public void TimeReset()
{
//稼働中はリセットを無効にする
if (moving)
{
return;
}

//各パラメータの初期化
mainNeedleangle.localEulerAngles = new Vector3(0, 0, 0);
subNeedleangle.localEulerAngles = new Vector3(0, 0, 0);
movetime = 0;
timeText.text = "00:00:00.00";
}

スタートとストップは、用意したフラグをtrue・falseすれば機能します。
リセットについては、針が動いている間は、リセットできないようにしたいので、
フラグがtrueならReturnするようにしています。

停止中なら要素を全て初期化します。

これでストップウォッチが完成しました。

後は、各変数にゲームオブジェクトをアタッチして、
各ボタンにメソッドを割り当てて、PLAYでっす。

稼働状況の動画ですが、スタート・ストップ
稼働時と停止時のリセット状況
分の表示が切り替わる所をまとめてみました。

しかし、針とデジタルの誤差がありますね~ (- .-)ヾ ポリポリ
これは文字盤の精度によるものでっす。

アナログ盤は、精度が高くないと見た目で分かっちゃいますね。(;^_^A

一応、ストップウォッチを作る事ができました。
余りゲームと直結しないので、作ってもどうなの?って声は無視して、
ダメダコリャ!( ~っ~)/ σ(^_^;)

何かの参考になればと思います。

最後に完成版のスクリプトを

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

public class StopWatchManager : MonoBehaviour
{
public GameObject MaineNeedle; //秒針オブジェクト
public GameObject SubNeedle; //分針オブジェクト
public GameObject TimeText; //デジタル表示

private Transform mainNeedleangle; //秒針のtransform取得用変数
private Transform subNeedleangle; //分針のtransform取得用変数
private Text timeText; //デジタルテキストの取得用変数

private float movetime; //稼働時間用変数
private float mainangle, subangle; //各針の角度用変数

private bool moving; //ストップウォッチ稼働フラグ


// Start is called before the first frame update
void Start()
{
//各針とデジタルテキストのComponent取得
mainNeedleangle = MaineNeedle.GetComponent<Transform>();
subNeedleangle = SubNeedle.GetComponent<Transform>();
timeText = TimeText.GetComponent<Text>();
}

// Update is called once per frame
void Update()
{
//スタートボタンを押したら稼働する
if (moving)
{
needleMove();
}
}


//各針の動きとデジタルテキストの表示
void needleMove()
{
//ストップウォッチ稼働時間
movetime += Time.deltaTime;

//稼働時間から針の角度を算出
mainangle = movetime * -360 / 60;
subangle = movetime * -360 / 3600;

//各針の移動
mainNeedleangle.localEulerAngles = new Vector3(0, 0, mainangle);
subNeedleangle.localEulerAngles = new Vector3(0, 0, subangle);

//デジタル表示用のテキストを取得
string timetext = TimeSet();
//デジタルテキスト表示
timeText.text = timetext;
}


//各ボタンの機能
//ストップウォッチのスタート
public void WatchStart()
{
moving = true;
}
//ストップウォッチのストップ
public void WatchStop()
{
moving = false;
}
//ストップウォッチのリセット
public void TimeReset()
{
//稼働中はリセットを無効にする
if (moving)
{
return;
}

//各パラメータの初期化
mainNeedleangle.localEulerAngles = new Vector3(0, 0, 0);
subNeedleangle.localEulerAngles = new Vector3(0, 0, 0);
movetime = 0;
timeText.text = "00:00:00.00";
}


//時・分・秒の表示方法を指定
string TimeSet()
{
//各種変数
string timetext = null;
int provtime = 0;
int twodecimal = 0;

//稼働時間から小数点以下を取り出して二桁の整数にする
provtime = Mathf.FloorToInt(movetime);
twodecimal = Mathf.FloorToInt((movetime - provtime) * 100);

//TimeSpanを使って 00:00:00. の文字列を作る
TimeSpan ts = new TimeSpan(0, 0, provtime);
timetext = ts.ToString(@"hh\:mm\:ss\.");

//切り離した小数点以下を文字化する
timetext += twodecimal.ToString("D2");

return timetext;
}
}

次回はタイマーを作ってみたいと思います。
今回はこのへんで( ^ 0 ^ )/~~~~see you again

この記事へのコメント