YKTのどっちがいいかな?

実世界連動地域貢献型すごろくゲームのアプリ開発状況を書いていきます。

冬休みは短い

明けましておめでとうございます。2019年ももう6日経ちましたが皆様いかがお過ごしでしょうか。

私は見事に堕落した生活を送っております。特にここ3日は家から一歩も出ずペルソナ5をずーっとやっておりました。いや、ほんとなんで買ってしまったんやこのゲーム。。。

そんなこんなで、さながら冬休みの宿題をぎりぎりまでため込んでしまった小学生のようにこのブログを書いているわけです。トホホ・・・

そんな私の今年の抱負は『メリハリのある生活』です。はい、早速できてないですね。

以下、本題です。

次の目的地ルーレット

今回は次の目的地を決定する演出部分を作っていきます。前作(前前々回)までは主に技術力の問題で4枚のカードから1枚選ぶようなエフェクトでごまかしていましたが、今作はできるだけ原作の桃てt...う、頭痛がッ...に近づけた見た目にしていきたいと思います。

素材作成

最初に目的地の候補がランダムでピロピロするパネル部分の素材を作っていきましょう。

さて、パネルの裏にあたるイラスト部分ですが、別途画像を用意するのも大変なので、おなじみのこの画像(こっちがいいかな?のポーズ)を使います。

まず『こっちのポーズ』を4行4列の16分割にします。

分割はこのフリーソフトで簡単にできました。

これらをイラストレーターで並べなおして、[効果] - [3D] - [押し出し・ベベル]を選択し、パネルっぽくしましょう。ついでに木枠も作っておくとおしゃれな感じになります。

また、パネル1枚と同じサイズの単色画像も用意します。これはパネルの表(駅名が書いてある方)になります。

Unityにインポート

作成した画像をUnityにインポートします。

書き出した画像をアセットフォルダにD&Dにして、[Inspector]のTexture TypeをSpriteにしておきましょう。

続いてHierarchyタブで[Create] - [UI] - [Image]からイメージオブジェクトを追加し、先ほどの画像をD&Dして反映させます。パネルの表面は駅名のための文字オブジェクトが必要ですので、Buttonオブジェクトとして追加したほうが楽かもしれません。

また、これらの画像は空のオブジェクトのChildとして追加し、YKTパネルの中心がローカル座標の原点となるようにしておきましょう。

ここまでを行うと以下のような状態になると思います。

パネルをピロピロさせる(理論編)

続いてパネルをピロピロさせるためのプログラムを考えていきます。いくつかやり方が思いつきますが、今回は、一つのパネルをランダムで16か所に移動させる方法で実装したいと思います。

まず、下図のように表向きパネルを左下の角に配置します。

この時のパネルの座標を(x, y)とします。このパネルを0~3のどれかになる整数乱数ix, iyを用いて、ほかの位置に動かします。

移動後のパネル位置(nx, ny)は、パネル一つ分の縦横の長さをそれぞれdx, dyとすると、それぞれ

{ \displaystyle nx = x + dx \cdot ix}

{ \displaystyle ny = y + dy \cdot iy}

とすればうまくいきそうですね。

dxdyの値は、図から明らかなように、

{ \displaystyle |x| = 1.5dx}

{ \displaystyle |y| = 1.5dy}

の関係式から求めることができます。正月ボケの頭にちょうど良い体操になりましたね(笑)

パネルをピロピロさせる(実装編)

前節の理論をコーディングしていきましょう。

以下のコードを"TargetPanel.cs"として保存し、パネルオブジェクトにアタッチすれば、いい感じでピロピロします。正直もうお腹いっぱいでしょうから詳細は割愛します(笑)

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

public class TargetPanel : MonoBehaviour {

  public int speed;
  public float upspeed;
  public float upscale;
  private float x,y,dx,dy,x_par,y_par;
  private int rand,ix,iy;
  private int counter = 0;
  private bool stop = false;
  public bool end = false;

  // Use this for initialization
  void Start () {
    //カドの位置と矢印一個分の長さを取得
    x = gameObject.transform.localPosition.x;
    y = gameObject.transform.localPosition.y;
    dx = -2.0f/3.0f*x;
    dy = -2.0f/3.0f*y;
    //親グループの位置を取得
    x_par = gameObject.transform.parent.transform.localPosition.x;
    y_par = gameObject.transform.parent.transform.localPosition.y;
  }
  
  // Update is called once per frame
  void Update () {
    if(!end){
      //タップを検知
      if (Input.GetMouseButtonDown(0)){
        //ルーレットの最中なら止める
        if(!stop){
          stop = true;
          //UI Canvasの(0,0,0)に来るように親オブジェクトの分だけ移動させながら拡大
          //終わったらendフラグを立てる
          gameObject.transform.DOLocalMove(new Vector3(-x_par,-y_par,0),upspeed);
          gameObject.transform.DOScale(Vector3.one*upscale,upspeed).OnComplete(() =>
          {
            end = true;
          });
        }
      }

      //stopじゃなければ乱数を引き続ける
      if(!stop){
        if(counter>=speed){
          //0-15までの乱数を取得
          rand = Random.Range(0,16);
          //4で割った商と余りを計算
          ix = rand / 4;
          iy = rand % 4;
          //パネルの移動
          gameObject.transform.localPosition = new Vector3(x+dx*ix,y+dy*iy,0);
          //駅名の設定
          GC.p.target_station = GC.p.targetlist[rand];
          gameObject.GetComponentInChildren<Text>().text = GC.p.stations[GC.p.target_station].name;
          //counterの初期化
          counter = 0;
        }
        counter++;
      }
    }
  }
}

正月ボケの頭にちょうど良い体操になりましたね(2回目)

読者への配慮と筆者の手抜きにより割愛された作業たち

これ以上の作業説明はお互いlose-loseですので、箇条書きで。

  • ニュース番組っぽい背景画像を作成
  • ニュース番組っぽい机の画像を作成
  • 事前に作ってあった〇〇〇の配置
  • シーンの進行(メッセージ → ルーレット → メッセージ)を制御するスクリプト作成

完成

ということで完成したシーンがこちらになります。

マル秘の部分には本作のナレーション担当のあるキャラクター(前節の〇〇〇に該当)が入ります。当日のお楽しみということでマル秘にしました。

おわりに

ということで、目的地ルーレットのシーンが完成しました。まだ、ゲーム大筋と連動させていないので厳密には完成ではありませんが、だいぶ形になってきました。

近々開催日を決めたいと思います。申し訳ないですが、かくかくしかじかで3月末までに開催しようと思います。寒くてごめんね。

今年もよろしくお願いいたします。