【Unity】コマンドルーレットの作り方

2021年3月24日水曜日

C# Unity Unity2D

この記事をシェアする
  • B!


 
  Unityのゲーム開発で、こんなコマンドルーレットを作ろうとしたのですが、ネットサーフィンしてもそれっぽいやつが見つかりませんでした。
 結局自作したので、個人的な備忘録として、また同じ悩みを持った誰かの一助になるものとして、作り方をネット空間に残しておこうと思います。

 なお、"Unity2D"とタグつけてますし、特に3Dにする理由もなかったので私は2Dで作成しましたが、コマンドルーレット自体は3Dだろうが2Dだろうが使えるUIなので安心してください。




ブログランキング・にほんブログ村へ にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ Unityへ にほんブログ村 IT技術ブログ C#へ

実行環境について

Unityバージョン:2019.4.18f1
Visual Studio Community 2019バージョン:16.9.1

UIの配置

①Hierarchy(ヒエラルキー)の"+"からUI→Canvasと進みCanvasを設置

シーン画面で編集しやすいように、Canvasを全て画面に収まるように調整だとか、Render Mode弄ってカメラの範囲にCanvasがおさまるように調整だとか、その辺お好みで。

②Canvasの下に、空オブジェクト一つ作成

空オブジェクト(以下、空オブ)のInspector(インスペクター)いって、Rect Transformのコンポーネントでサイズ調整とかは各自。(私は下の画像の通りにしましたが、たぶん皆さんと画面比全然違うので参考にならないんじゃないでしょうか…)

③空オブに"Vertical Layout Group(垂直レイアウトグループ)"のコンポーネントを追加

空オブのインスペクター一番下の"Add Component"から、"Vertical Layout Group"を検索、追加。

"Vertical Layout Group"は垂直に均等にオブジェクトを並べてくれるコンポーネント。皆さんが作りたいルーレットのコマンド数が、私と同じ7個とは限らないわけですが、これさえあればその下に何個オブジェクトつけても均等に配置してくれるので、その辺の問題を解決してくれて便利。

写真のように設定して、次は一つ一つのコマンドに当たるUIを作っていきます。


④空オブの下に、Image、さらにその下にTextのUI作成

このTextにコマンドを記入する感じですね。Imageは特に何もしなくていいですが、TextのRect Transformは下画像みたいに弄って、親オブジェクトとサイズ一致させておいてください。


Textのコンポーネントも適当に弄って、文字の大きさとか配置とか設定してください。

…さきほど、「Imageは特に何もしなくていい」と書いた直後に恐縮ですが、ここで一工夫、Imageのソース画像に適当なフリー素材を入れると見映えがよくなったり。


私のはこんな感じ。
下記のサイトから借りてきました!クレジット代わりにリンクを貼っておきます。

⑤Imageを好きなだけ複製

見映えもよくなったコマンドが完成したら、あとは増産するだけ。
Imageにカーソル合わせてマウス右クリック、Duplicate(複製)を、お求めの数だけしてください。
③でも説明しましたが、どれだけ増産しても勝手にサイズ調整されるはず。

これでUIの準備は完了です。

スクリプト作成


ProjectのAssetsで右クリック、create(作成する)からC#scriptへ、というおなじみの流れはもう省略しますが、いよいよスクリプトです。

難しいスクリプトではありませんが、基本的にはこんな感じでしょうか。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; //UI弄るときに必須

public class RoulettEasy : MonoBehaviour
{
    [SerializeField] Image[] commandlist;
    private float countTime;
    private int lastTime;
    private float speed = 10;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        countTime += Time.deltaTime * speed;
        if (countTime > commandlist.Length)
        {
            countTime = 0f;
        }
        if (lastTime != (int)countTime)
        {
            foreach (var command in commandlist)
            {
                command.color = new Color(1, 1, 1);
            }
            lastTime = (int)countTime;
            commandlist[(int)countTime].color = new Color(1, 0, 0);
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            speed = 0;
        }
    }

}

ちょこちょこ説明いれてますが、だいたいこんな感じですね。

あとはこのスクリプトを空オブにアタッチして、インスペクターから[SerializeField]の事項を登録するだけですね。
私の場合、コマンドが7個の設定だったので、こんな感じ。


Sizeのところに7と記入してEnter、出てくるElementたちにImageをドラッグして登録してはい、完成!
実際に動かしたら、ちゃんとルーレット動くと思います。
Spaceキー押したら止まるはずなので、それも確認してください。

ちょっと記事長くなってるので、一回切ります…。
あとは応用編が続くのですが、もしよかったらお付き合いください…。
(公開直後確認更新:なぜ「追記に挿入」してるのに一回切れないんだ…)

ブログランキング・にほんブログ村へ にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ Unityへ にほんブログ村 IT技術ブログ C#へ




【応用その①】他スクリプトにアクセスしやすく改造

さて、では続きです。
一応の完成を見たコマンドルーレット。この子は特性上、ルーレットで得た情報が次の行動につながるというようなシチュエーションで使うことが多いのではないでしょうか。
すなわち、他のスクリプトへ情報を送るのが前提での使用が予想されます。ということで、お節介やもしれませんが少しだけ、他スクリプトへアクセスしやすいように付け足しておきます。


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

public class RoulettEasy : MonoBehaviour
{
    [SerializeField] Image[] commandlist;
    private float countTime;
    private int lastTime;
    private float speed = 10;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        countTime += Time.deltaTime * speed;
        if (countTime > commandlist.Length)
        {
            countTime = 0f;
        }
        if (lastTime != (int)countTime)
        {
            foreach (var command in commandlist)
            {
                command.color = new Color(1, 1, 1);
            }
            lastTime = (int)countTime;
            commandlist[(int)countTime].color = new Color(1, 0, 0);
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            speed = 0;
            //ここで(int)countTimeを他スクリプトに送信
        }
    }

    public void StartEasyRoulett()
    {
        speed = 10;
        countTime = 0;
    }
}


この場合、他スクリプトからコマンドルーレットを開始させる時は、そのスクリプトにこのRoulettEasyスクリプトをアクセスしたうえで([SerializeField]が楽じゃないかな…)、StartEasyRoulett()を呼び出せばいいし。

他スクリプトに結果を流す場合は、このスクリプトに目的のスクリプトをアクセスしたうえで(int)countTimeを送るプログラムを書けばいいと。

まぁこんな感じですね~。

【応用その②】はらはらドキドキ(?)コマンドルーレット

そもそも、これを説明するのが凄く難しい…。
ということで、こちらをご覧ください!


…私が青春の時間を捧げた思い出深いゲームですが…。
こういう感じで、押したらすぐ止まるのではなく、少々間があって(しかもその間がランダム)止まる、みたいなルーレットを作ってみたいと思います。

…これ、誰かの参考になるんでしょうかね…?


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

public class RoulettControll : MonoBehaviour
{
    [SerializeField] Image[] commandlist;
    private float countTime;
    private int lastTime;
    private float fireTime;
    private float speed = 10;
    bool isStop = false;
    private float lottery;
    bool justOnce = true;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //タイマーA
        countTime += Time.deltaTime * speed;
        if (countTime > commandlist.Length)
        {
            countTime = 0f;
        }
        //タイマーB
        fireTime += Time.deltaTime;
        if (lastTime != (int)countTime)
        {
            fireTime = 0f;
            foreach (var command in commandlist)
            {
                command.color = new Color(1, 1, 1);
            }
            lastTime = (int)countTime;
            commandlist[(int)countTime].color = new Color(1, 0, 0);
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            isStop = true;
            lottery = Random.Range(990, 997) * 0.001f;
        }
        if (isStop)
        {
            speed *= lottery;
        }
        if (fireTime >= 2.5 && justOnce)
        {
            fireTime = 0;
            justOnce = false;
            //(int)countTimeを他スクリプトに送信
        }
    }

    public void startRoulett()
    {
        isStop = false;
        speed = 10;
        justOnce = true;
        countTime = 0;
        fireTime = 0;
    }
}
いわゆるタイマーを二つも使うという荒技で困難を為しました…。
私はこちらが真なるコマンドルーレットだと信じているので、スクリプトの名前も変えてますが、土台となるところは同じです。

最後に

ここまで読んでくださり、感謝の至りです…。
最後なんて完全に趣味に走ってましたが、皆さんのお役に立っていたなら幸いです( ´∀` )

ブログランキング・にほんブログ村へ にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ Unityへ にほんブログ村 IT技術ブログ C#へ

自己紹介

自分の写真
大学では文学部日本文学科出身という生粋の文系。チャットゲームの運営の在宅業務に携わったことでプログラミングに興味を持ち、2020年末、SAMURAI ENGINEERに入塾。2021年4月末に卒塾。 ※お問い合わせある方は、ブログのお問い合わせページからどうぞ~

ページビューの合計

応援をお願いします!

もしよかったらTwitterのフォローと応援ポチをよろしくです~
PVアクセスランキング にほんブログ村
ブログランキング・にほんブログ村へにほんブログ村 IT技術ブログへにほんブログ村 IT技術ブログ Unityへ
にほんブログ村 IT技術ブログ C#へ

QooQ