Unityでストップウォッチを作る その9 リセットのアニメーション


今回は「RESET」の文字をアニメーションさせるのに加えて、画面全体の背景もタップしたときにアニメーションさせようと思います。そんなにこったものではなく、フェードさせたり回転させたりするくらいのものです。

「RESET」を表示するのに使っているTextMeshは、テキストにHTMLのようなタグを付けて1文字単位で色を変えたり装飾をしたりする事が出来ます。ですが、今回は文字列全体にしか色を指定しませんし、フェードさせたりもしたいので、文字列全体にタグをつける処理をするスクリプトを作ります。

新たに「ColoredText.cs」という名前でC#スクリプトをProjectウィンドウの「Script」フォルダに作成し、以下のコードをコピーしてください。

//
// ColoredText.cs
//
using UnityEngine;
using System.Collections;
[ExecuteInEditMode()]
[RequireComponent(typeof(TextMesh))]
[RequireComponent(typeof(MeshRenderer))]
public class ColoredText : MonoBehaviour {
 
 public string text = "empty";
 public Color color = Color.white;
 
 string prevText = null;
 Color prevColor = Color.clear;
 
 float duration = 0;
 bool animating = false;
 float fromAlpha;
 float toAlpha;
 float time = 0;
 
 TextMesh textMesh;
 
 // Use this for initialization
 void Start () {
   UpdateText();
 }
 
 // Update is called once per frame
 void Update () {
   
   bool needsUpdate = false;
   
   if (animating) {
     
     time += Time.deltaTime;
     float t = time / duration;
     
     if (t >= 1.0f) {
       t = 1.0f;
       animating = false;
     }
     
     float alpha = Mathf.Lerp(fromAlpha, toAlpha, t);
     color.a = alpha;
     
     needsUpdate = true;
     
   } else if (IsPropertyChanged()) {
     
     needsUpdate = true;
     
   }
     
   if (needsUpdate) {
     UpdateText();
   }
 }
 
 bool IsPropertyChanged() {
   if (text != prevText ||
     color != prevColor) {
     return true;
   }
   return false;
 }
 
 void UpdateText() {
   
   if (textMesh == null) {
     textMesh = GetComponent<TextMesh>();
     textMesh.richText = true;
   }
   
   textMesh.text = HtmlColorText(text, color);
   
   prevText = text;
   prevColor = color;
 }
 
 public void SetAlpha(float alpha, float duration = 0.0f) {
   
   if (duration <= 0.0f) {
     
     animating = false;
     color.a = alpha;
     UpdateText();
     
   } else {
     
     fromAlpha = color.a;
     toAlpha = alpha;
     animating = true;
     time = 0;
     this.duration = duration;
   }
 }
 
 static private string HtmlColorText(string text, Color col) {
   
   int redValue = (int)(col.r * 255.0f);
   int greenValue = (int)(col.g * 255.0f);
   int blueValue = (int)(col.b * 255.0f);
   int alphaValue = (int)(col.a * 255.0f);
   string colorText = string.Format("#{0:x2}{1:x2}{2:x2}{3:x2}", redValue, greenValue, blueValue, alphaValue);
   return string.Format("<color={0}>{1}</color>", colorText, text);
   
 }
}

出来たら「ResetText」オブジェクトに追加してください。この「ColoredText」にTextMeshで表示したいテキストと色を渡す事で、わざわざタグを書かなくても色をつける事が出来ます。また、時間を指定してフェードイン・アウトをする事も出来ます。

unitysw_9_1.png

BigStopWatchでは「RESET」を表示・非表示するときにグルっと回転させています。これも実現したいので「ResetButtonRoot」オブジェクトのComponentに「AngleAnimation.cs」を追加してください。

unitysw_9_2.png

背景の色もタップしたら光るようなイメージで時間をかけて変えたりしたいと思います。新たに「BackgroundColor.cs」というスクリプトをProjectウィンドウの「Script」フォルダに作成して、以下のコードをコピーしてください。

//
// BackgoundColor.cs
//
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class BackgroundColor : MonoBehaviour {
 
 public Color[] colors;
 public Color offColor = Color.black;
 float flashDuration = 1.0f;
 
 Color prevOffColor = Color.clear;
 
 bool animating = false;
 float duration;
 int colorIndex;
 float time;
 
 // Use this for initialization
 void Start () {
   
   if (camera != null) {
     
     camera.backgroundColor = offColor;
     
   }
   
 }
 
 // Update is called once per frame
 void Update () {
   
   if (camera == null) return;
   
   if (animating) {
     
     time += Time.deltaTime;
     float t = time / flashDuration;
     
     if (t >= 1.0f) {
       t = 1.0f;
       animating = false;
     }
     
     camera.backgroundColor = Color.Lerp(colors[colorIndex], offColor, t);
     
   } else if (IsPropertyChanged()) {
     
     camera.backgroundColor = offColor;
     
     prevOffColor = offColor;
     
   }
 }
 
 public bool IsPropertyChanged() {
   
   if (offColor != prevOffColor) {
     
     return true;
   }
   
   return false;
   
 }
 
 public void Flash(int colorIndex) {
   
   if (colorIndex < 0 || colors.Length <= colorIndex) {
     return;
   }
   
   this.colorIndex = colorIndex;
   animating = true;
   time = 0.0f;
   
 }
}

作成したら、「Main Camera」オブジェクトへ追加してください。

unitysw_9_3.png

タップした場所によって光る背景の色を変えたいので、「BackgroundColor」では複数の色を持つようにしています。インスペクタにある「offColor」は待機状態の単独の色、「colors」はタップしたときに使う複数の色の配列となっています。

「BackgroundColor」の「Off Color」は黒、「Colors」の各色は以下のような感じにしてみてください。

unitysw_9_4.png

表示する側の準備はできたので、コントロールする側のコードを追加します。「Stopwatch.cs」を以下の様に修正してください。

//
// Stopwatch.cs
//
using UnityEngine;
using System;
using System.Collections;
public class Stopwatch : MonoBehaviour {
 
 enum StopwatchState {
   Zero,
   Play,
   Pause
 }
 
 public TextMesh timeText;
 public TimeCircleController timeCircleController;
 // ここから追加 ->
 public AngleAnimation resetButtonAngleAnimation;
 public ColoredText resetButtonText;
 public float buttonAnimDuration = 0.2f;
 
 public BackgroundColor bgColor;
 // <- ここまで追加
 StopwatchState state = StopwatchState.Zero;
 TimeSpan lastStopTimeSpan;
 DateTime startDateTime;
 
 // ここから追加 ->
 void Start () {
   float alpha = (state == StopwatchState.Pause) ? 1.0f : 0.0f;
   resetButtonText.SetAlpha(alpha, 0.0f);
 }
 // <- ここまで追加
 
 void Update () {
   
   bool circleAnim = false;
   
   if (Input.GetMouseButtonDown(0)) {
     ChangeState(ref circleAnim);
   }
   
   UpdateTime(circleAnim);
 }
 
 void ChangeState(ref bool circleAnim) {
   
   ButtonType buttonType = ButtonType.Background;
     
   Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
   RaycastHit hit;
   
   if (Physics.Raycast(ray, out hit, 2.0f)) {
     Button button = hit.collider.gameObject.GetComponent<Button>();
     if (button != null) {
       buttonType = button.type;
     }
   }
   
   if (buttonType == ButtonType.Reset && state == StopwatchState.Pause) {
     
     lastStopTimeSpan = new TimeSpan(0);
     startDateTime = DateTime.UtcNow;
     
     state = StopwatchState.Zero;
     
     circleAnim = true;
     
     // ここから追加 ->
     SetVisibleResetButton(false);
     FlashBackground(1);
     // ここまで追加
     
   } else if (state == StopwatchState.Play) {
     
     TimeSpan ts = DateTime.UtcNow - startDateTime;
     lastStopTimeSpan = ts + lastStopTimeSpan;
     
     state = StopwatchState.Pause;
     
     // ここから追加 ->
     SetVisibleResetButton(true);
     FlashBackground(0);
     // ここまで追加
     
   } else {
     
     startDateTime = DateTime.UtcNow;
     
     state = StopwatchState.Play;
     
     // ここから追加 ->
     SetVisibleResetButton(false);
     FlashBackground(0);
     // ここまで追加
     
   }
   
 }
 
 void UpdateTime(bool circleAnim) {
   
   TimeSpan currentTs;
   
   if (state == StopwatchState.Play) {
     
     TimeSpan ts = DateTime.UtcNow - startDateTime;
     currentTs = ts + lastStopTimeSpan;
     
   } else {
     
     currentTs = lastStopTimeSpan;
     
   }
   
   if (timeText != null) {
     
     timeText.text = ConvertTimeSpanToString(currentTs);
     
   }
   
   if (timeCircleController != null) {
     
     timeCircleController.SetTime(currentTs, circleAnim);
     
   }
   
 }
 
 // ここから追加 ->
 void SetVisibleResetButton(bool visible) {
   
   if (resetButtonText != null) {
     
     float alpha = visible ? 1.0f : 0.0f;
     resetButtonText.SetAlpha(alpha, buttonAnimDuration);
     
   }
   
   if (resetButtonAngleAnimation != null) {
     
     float fromAngle = visible ? 360.0f : 0.0f;
     float toAngle = visible ? 0.0f : -360.0f;
     resetButtonAngleAnimation.SetAngle(fromAngle, toAngle, true, buttonAnimDuration, false);
     
   }
   
 }
 
 void FlashBackground(int colorIndex) {
   
   if (bgColor != null) {
     
     bgColor.Flash(colorIndex);
     
   }
 }
 // <- ここまで追加
 
 static public string ConvertTimeSpanToString(TimeSpan ts) {
   
   if (ts.Hours > 0 || ts.Days > 0) {
     return string.Format("{0}:{1:D2}:{2:D2}.{3}", ts.Days * 24 + ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds.ToString("000").Substring(0, 2));
   } else {
     return string.Format("{0}:{1:D2}.{2}", ts.Minutes, ts.Seconds, ts.Milliseconds.ToString("000").Substring(0, 2));
   }
 }
}

「Stopwatch」オブジェクトのインスペクタに「Reset Button Angle Animation」と「Reset Button Text」と「Bg Color」という項目が追加されていますのでそれぞれ以下のようにアサインしてください。

unitysw_9_5.png

これで「RESET」は計測のポーズ中だけ現れ、画面をタップしたときには画面全体が光るような感じになっていると思います。

今回のWebPlayerビルド

今回は以上です。次回はとりあえず放っておいていたシェーダーを差し替えたいと思います。

前へ | 次へ

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です