プログラミング初心者がunityでカードゲームを作ってみたい!【その5】〜カードチェンジアニメーション
前回(https://hot-houjitea.hatenablog.com/entry/2021/02/09/140636)の続きです。
カードをめくるようなアニメーションを付けるために、下記の動画を参考にしてスクリプトを変更していきます。
【スポンサーリンク】
GuScriptの変更
前回のGuScriptを開いて、スクリプトを以下のように変更しました。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class GuScript : MonoBehaviour
- {
- public Sprite cardBack;
- // アウトレット接続(インスペクタ上で設定)するcardBackという名前のスプライトの宣言
- public Sprite[] faces;
- // アウトレット接続するfacesという名前のスプライトの配列の宣言
- public GameObject Card;
- // アウトレット接続するCardという名前のゲームオブジェクトの宣言
- public bool show;
- // アウトレット設定するshowという名前のブール値の宣言(初期値はfalse)
- public AnimationCurve scaleCurve;
- // アウトレット接続するscaleCurveという名前のアニメーションカーブの宣言
- public float duration = 0.5f;
- // アウトレット接続するdurationという名前の数値の宣言(初期値は0.5)
- SpriteRenderer spriteRenderer;
- // spriteRendererという名前のスプライトの宣言
- void Awake()
- {
- spriteRenderer = Card.GetComponent<SpriteRenderer>();
- // spriteRendererにCardのスプライトを代入
- }
- // イベントトリガーに設定するために、publicの関数を作成
- IEnumerator ToggleFace()
- {
- // もしカードが裏面(初期値)だったら
- if (show == false)
- {
- Sprite newSprite = faces[Random.Range(0, faces.Length)];
- // newSpriteという名前のスプライトを宣言してfacesのカードをランダムに代入する
- float time = 0f;
- // timeという名前の数値の宣言(初期値は0)
- while(time <= 1f) // timeが1以下の場合は以下を繰り返す
- {
- float scale = scaleCurve.Evaluate(time);
- // scaleという名前の数値を宣言してcaleCurveのEvaluate(数値)を代入する
- time = time + Time.deltaTime / duration;
- // timeに前フレームからの経過時間を0.5で割った数値を代入する
- Vector3 localScale = Card.transform.localScale;
- // localScaleという名前のVector3(位置情報)にCardオブジェクトの位置情報を代入する
- localScale.x = scale;
- // localScaleのx軸の値にscaleの値を代入する
- Card.transform.localScale = localScale;
- // Cardの位置情報にlocalScaleの値を代入する
- if (time >= 0.5f) // timeが0.5以上の場合は
- {
- spriteRenderer.sprite = newSprite;
- // CardのスプライトにnewSpriteを代入する
- }
- yield return new WaitForFixedUpdate();
- // ここまでの結果を返して一定時間処理を停止する
- }
- show = true;
- // 再度グーをタップした時にcardが変化しないように、showをtrueにしておく
- }
- }
- public void Flip()
- {
- StartCoroutine(ToggleFace());
- // ToggleFace()のコルーチンを実行する
- }
- }
簡単に言うと、画像のx軸の値を徐々に小さくし、0ギリギリのところで新しい画像にチェンジし、徐々に大きく戻すという処理に変更します。
上から順に解説していきます。
アニメーションカーブの設定
- public AnimationCurve scaleCurve;
- // アウトレット接続するscaleCurveという名前のアニメーションカーブの宣言
- public float duration = 0.5f;
- // アウトレット接続するdurationという名前の数値の宣言(初期値は0.5)
まず宣言を2つ増やしました。
1.カードをめくる動作を滑らかに見せるために、AnimationCurveを宣言しています。アニメーションカーブとはunityのリファレンス (UnityEngine.AnimationCurve - Unity スクリプトリファレンス)によると、Unityの機能として備わっているクラスで、設定したカーブから出した数値を利用することができるようです。この後カーブを設定します。
2.duration (持続時間)という名前の数値を設定しています。カードをめくるアニメーションの速度調整に使います。初期値を0.5にしています。
スクリプトをセーブして、Guのインスペクタビューに戻ります。GuSucriptのところに先程宣言したアウトレット接続の欄ができています。durationは初期値をすでに設定してあるので変更の必要はありません。scaleCurveの右側の四角をクリックすると、アニメーションカーブの編集画面が出てきます。
まず左下のカーブ例一覧の中から、一番左にある横一直線のものを選択します。編集画面に表示されたら、横メモリの0.5のところで右クリックするとAdd Keyが出てくるのでクリックします。この点からカーブを自由に動かせるようになるので、shiftを押しながら下へドラッグし、0に行かないくらいのぎりぎりのところで離します。shiftを押さないと点が横軸にずれてしまいます。このカーブは画像のx軸幅の変化に使用するため、0になると画像が消える瞬間ができてしまうことからギリギリのところで止めています。
カーブが出来たら編集画面を閉じて、スクリプトに戻ります。
【スポンサーリンク】
ToggleFaceメソッドをコルーチンに変更
- IEnumerator ToggleFace()
次に「public void ToggleFace()」(関数)だったものを「IEmulator ToggleFace()」(コルーチン)に変更しています。コルーチンという言葉は今回私も初めて知りましたが、なぜこの形にしなければならないのかは、公式リファレンスの説明が一番分かりやすかったです。
関数の場合1フレームで処理を完結させようとするので、今回のように徐々に画像のx軸の値を変えてアニメーションにするという処理が困難で、時間と共に少しずつ変更する処理を書いても最終結果のみの表示になってしまうようです。(アニメーションなく画像が切り替わるのみ)
コルーチンは自分の好きなタイミングで一旦処理を中断し、そこまでの結果を返すことができる便利な機能です。つまりフレームごとにx軸の値を変更して画像に反映させることができるため、アニメーションの処理が可能になるというわけです。
コルーチンの書き方は「IEmulator 名前()」の中身を関数のように記載し、結果を返したいタイミングで「return〜」を書けば良いようです。
そこで中身を以下のように書き変えました。
スプライトのx軸幅を徐々に変更
- Sprite newSprite = faces[Random.Range(0, faces.Length)];
- // newSpriteという名前のスプライトを宣言してfacesのカードをランダムに代入する
- float time = 0f;
- // timeという名前の数値の宣言(初期値は0)
まずグー・チョキ・パーからランダムに取得するスプライトを最初に宣言しました。
また、時間経過を入れるための数値timeを初期値0で宣言しました。
- while(time <= 1f) // timeが1以下の場合は以下を繰り返す
- {
- float scale = scaleCurve.Evaluate(time);
- // scaleという名前の数値を宣言してcaleCurveのEvaluate(数値)を代入する
- time = time + Time.deltaTime / duration;
- // timeに前フレームからの経過時間を0.5で割った数値を代入する
- Vector3 localScale = Card.transform.localScale;
- // localScaleという名前のVector3(位置情報)にCardオブジェクトの位置情報を代入する
- localScale.x = scale;
- // localScaleのx軸の値にscaleの値を代入する
- Card.transform.localScale = localScale;
- // Cardの位置情報にlocalScaleの値を代入する
- if (time >= 0.5f) // timeが0.5以上の場合は
- {
- spriteRenderer.sprite = newSprite;
- // CardのスプライトにnewSpriteを代入する
- }
- yield return new WaitForFixedUpdate();
- // ここまでの結果を返して一定時間処理を停止する
- }
whileは()の中身が真である限り処理を繰り返すものです。
scaleという名前の数値に、作成したアニメーションカーブ(scaleCurve)のEvaluate関数(引数time (時間)で指定された時間のカーブの値を返してくれます)で、timeの値を引数に入れてx軸の値を代入します。
次に経過した時間をtimeに足すために、timeの値にUnityの機能であるTimeクラスのdeltatime変数 (前フレームからの経過時間)をduration (0.5)で割った数値をプラスして代入しています。
0.5で割る、ということは2倍にしているということです。なぜかと言うとアニメーションカーブの1単位を1で設定しているため、そのままdeltatimeを足すとカードをめくるアニメーションに1秒かかるということになります。1秒は少し遅すぎるため、2掛けにしてあります。durationはpublicで宣言しているため、後で速度を調整したい場合はインスペクタビューから数値を変更できます。
x軸の値を得ることができたので、早速 Cardオブジェクトのスプライトのx軸に代入します。
まず Cardオブジェクトの位置を得るためにlocalScaleという名前のVecter3 (位置情報)を宣言し、 CardオブジェクトのtranceformクラスのlocalScale変数 (Vecter3型の位置情報)を代入します。
このlocalScaleのx軸に先程得たx軸の値(scale)を代入し、このVecter3情報をまた CardオブジェクトのtranceformクラスのlocalScale変数に代入する、という三段階でx軸の値を変更しました。
また、timeの値が0.5 (x軸が限りなく0に近づいた時)を超えると、スプライトを先程宣言した新しいものに変えて、あたかもカードをめくったかのように見せています。
yield returnはIEmulator内で使えるもので、普通の関数では使用できません。普通のreturn文が結果を返してそのまま終了するのに対して、yield return文は処理を返したらそのまま続きを処理できます。これがコルーチンの特徴と言えるところだと思います。
yield returnのあとにnew WaitFor〜を付けることで、値を返してから一定時間処理を中断できます。FixedUpdateとは端末に依存せず一定時間に呼び出されるメソッドなので、環境に依存せず滑らかにアニメーションを動かすことができるようです。以下を参考にしました。
UpdateメソッドとFixedUpdateメソッドの使い分け【Unity】 | BioTech ラボ・ノート
これで繰り返しの処理によるアニメーション動作を作ることができました。ただ、GuオブジェクトのインスペクタビューのイベントトリガーでこのIEmulatorを設定しようとすると、リストに現れません。どうやら関数しか設定できないようです。なので、コルーチンを作動させる関数を書いていきます。
- public void Flip()
- {
- StartCoroutine(ToggleFace());
- // ToggleFace()のコルーチンを実行する
- }
新しくFlipという名前の関数を作成します。StartCoroutine (IEnumerator)でコルーチンを開始できます。
【スポンサーリンク】
イベントトリガーに設定
イベントトリガーにFlip関数を設定して実行ボタンを押すと、、、
カードをめくるアニメーションが出ました!
でもちょっと違和感があります。軸がカードの左端になっているので、左端に向かってカードが小さくなっていきます。軸を真ん中に設定し直そうと思います。
プロジェクトビューのカードをクリックし、インスペクタビューからsliceをクリックして編集画面を出します。sliceをクリックして画像のように設定し直します。最初に設定したときと違うのは、pivotをcenterに変えたことだけです。
Applyを押して画面に戻ると・・・今度はカードの位置がズレてしまいました。カード、グーー、チョキ、パーのオブジェクトのインスペクタのtranceformから良い位置に設定し直し、グーのコライダーがズレている場合は一度右上の点からRemove Componentでコライダーを消し、新たにボックスコライダーをアタッチします。
再度実行ボタンを押すと・・・
やっと上手くアニメーションができました・・・!!
次はじゃんけんの勝敗を判断して画面中央に結果を表示する方法を考えていきたいと思います。
じゃんけんゲーム作成の流れ→初心者がiPhoneアプリを作ってみる〜じゃんけんゲーム〜 - 社畜主婦の挑戦
【スポンサーリンク】