22
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Unity】Unityで爆速でルーレットを実装する

Last updated at Posted at 2019-03-04

今回はUnityで爆速でルーレットを作ったので手順を記述します!

aaaaaa.gif

##ルーレット作成方針(ざっくり)

・白い円の画像を1枚用意する

・選択肢の数だけ白い円の画像が貼られたImageを生成して、それぞれ色を変える

・ImageのImageTypeをFilledにし、Fill Amountを割合ごとに変える

・それぞれにTextを配置する

ざっくりですがこんな感じで作ろうと思います。他に何か方法あれば教えてください!

##作成手順

まずはルーレットの画像のPrefabを用意します。

1: 背景が透明の白い円の画像をUnityにImportし、TextureTypeをSpriteに変更したら、Imageに貼り付けます。

スクリーンショット 2019-03-04 0.56.35.png

2: ImageのImage TypeをFilledにし、
Fill MethodをRadial360に、
Fill OriginをTopにしておきます。
ついでにRaycastTargetもチェック外しちゃいましょう。

スクリーンショット 2019-03-04 1.01.49.png ↑画像の通りになるようにしてください

3: Imageの子供に、空のゲームオブジェクトを配置します。
Textを回転させるためにあとで使います。名前をRotateTextとしておきます。

nvnv.png

↑もしTransformのPositionやRotationが0以外になってたら0にしておいてください!

4: RotateTextの子供に、Textを配置します。
今回、Textを縦書きで使いたかったので、以下の記事のRotateText.csをお借りしました!

UnityのuGUIで縦書きテキスト表示

Textを縦書きにできたら、中央の上の方に配置してください!

スクリーンショット 2019-03-04 1.22.18.png こんな感じです!

5: ここまでできたら、このImageをPrefab化します!
スクリーンショット 2019-03-04 1.24.28.png
↑こんな感じの階層になってるはずです

###これで準備完了です!

6: Canvasの配下にRouletteという名前の空のゲームオブジェクトを配置します。
また、ここでも必ずTransformのPositionとRotationを0にしておいてください!

スクリーンショット 2019-03-04 1.39.42.png

7: 以下のスクリプト(RouletteMaker.cs)を作成して、空のゲームオブジェクトにAddします。

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

public class RouletteMaker : MonoBehaviour {
    [SerializeField] private Transform imageParentTransform;
    public List<string> choices;
    [SerializeField] private List<Color> rouletteColors;
    [SerializeField] private Image rouletteImage;
    private void Start () {
        float ratePerRoulette = 1 / (float) choices.Count;
        float rotatePerRoulette = 360 / (float) (choices.Count);
        for (int i = 0; i < choices.Count; i++) {
            var obj = Instantiate (rouletteImage, imageParentTransform);
            obj.color = rouletteColors[(choices.Count - 1 - i)];
            obj.fillAmount = ratePerRoulette * (choices.Count - i);
            obj.GetComponentInChildren<Text> ().text = choices[(choices.Count - 1 - i)];
            obj.transform.GetChild (0).transform.rotation = Quaternion.Euler (0, 0, ((rotatePerRoulette / 2) + rotatePerRoulette * i));
        }
    }
}

その後Inspectorで必要な項目を設定します。

スクリーンショット 2019-03-04 1.56.15.png

画像の通りImageParentTransformとRouletteImageの項目にオブジェクトをアタッチしてあげ、Choicesにルーレットの選択肢、
またRouletteColorsにそれぞれの選択肢の色を指定しましょう。

またこの時、InspectorでColorを指定する時デフォルトでalpha値が0になってしまっているかと思いますので、直してあげてください。(alpha値が0だと透明になってしまいます)

スクリーンショット 2019-03-04 2.02.41.png

↑再生したらきちんとルーレットが生成されるかと思います!

##作成したルーレットを回して判定

さて、ルーレットが作れたのであとは回して判定するだけです。

Canvas配下に、3つのボタン

・StartButton
・StopButton
・RetryButton

それと、結果を表示するようのText(ResultText)を作成し、

また矢印の画像も良い感じの位置に配置しちゃいましょう。
スクリーンショット 2019-03-04 22.16.17.png
↑Canvas配下はこんな感じ。

ここまでできたら以下のスクリプト(RouletteController.cs)を作成して、空のゲームオブジェクトにAddします。

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

public class RouletteController : MonoBehaviour {
    [HideInInspector] public GameObject roulette;
    [HideInInspector] public float rotatePerRoulette;
    [HideInInspector] public RouletteMaker rMaker;
    private string result;
    private float rouletteSpeed;
    private float slowDownSpeed;
    private int frameCount;
    private bool isPlaying;
    private bool isStop;
    [SerializeField] private Text resultText;
    [SerializeField] private Button startButton;
    [SerializeField] private Button stopButton;
    [SerializeField] private Button retryButton;

    public void SetRoulette () {
        isPlaying = false;
        isStop = false;
        startButton.gameObject.SetActive (true);
        stopButton.gameObject.SetActive (false);
        retryButton.gameObject.SetActive(false);
        startButton.onClick.AddListener (StartOnClick);
        stopButton.onClick.AddListener (StopOnClick);
        retryButton.onClick.AddListener (RetryOnClick);
    }

    private void Update () {
        if (!isPlaying) return;
        roulette.transform.Rotate (0, 0, rouletteSpeed);
        frameCount++;
        if (isStop && frameCount > 3) {
            rouletteSpeed *= slowDownSpeed;
            slowDownSpeed -= 0.25f * Time.deltaTime;
            frameCount = 0;
        }
        if (rouletteSpeed < 0.05f) {
            isPlaying = false;
            ShowResult (roulette.transform.eulerAngles.z);
        }
    }

    private void StartOnClick () {
        rouletteSpeed = 14f;
        startButton.gameObject.SetActive (false);
        Invoke ("ShowStopButton", 1.5f);
        isPlaying = true;
    }

    private void StopOnClick () {
        slowDownSpeed = Random.Range (0.92f, 0.98f);
        isStop = true;
        stopButton.gameObject.SetActive (false);
    }

    private void RetryOnClick(){
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }

    private void ShowStopButton () {
        stopButton.gameObject.SetActive (true);
    }

    private void ShowResult (float x) {
        for (int i = 1; i <= rMaker.choices.Count; i++) {
            if (((rotatePerRoulette * (i - 1) <= x) && x <= (rotatePerRoulette * i)) ||
                (-(360 - ((i - 1) * rotatePerRoulette)) >= x && x >= -(360 - (i * rotatePerRoulette)))) {
                result = rMaker.choices[i - 1];
            }
        }
        resultText.text = result + "\nが当たったよ!";
        retryButton.gameObject.SetActive(true);
    }
}

また、先ほど作成したRouletteMaker.csを、
以下のように編集します。

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

public class RouletteMaker : MonoBehaviour {
    [SerializeField] private Transform imageParentTransform;
    public List<string> choices;
    [SerializeField] private List<Color> rouletteColors;
    [SerializeField] private Image rouletteImage;
    [SerializeField] private RouletteController rController;
    private void Start () {
        float ratePerRoulette = 1 / (float) choices.Count;
        float rotatePerRoulette = 360 / (float) (choices.Count);
        for (int i = 0; i < choices.Count; i++) {
            var obj = Instantiate (rouletteImage, imageParentTransform);
            obj.color = rouletteColors[(choices.Count - 1 - i)];
            obj.fillAmount = ratePerRoulette * (choices.Count - i);
            obj.GetComponentInChildren<Text> ().text = choices[(choices.Count - 1 - i)];
            obj.transform.GetChild (0).transform.rotation = Quaternion.Euler (0, 0, ((rotatePerRoulette / 2) + rotatePerRoulette * i));
        }
        rController.SetRoulette();
        rController.rMaker = this;
        rController.rotatePerRoulette = rotatePerRoulette;
        rController.roulette = imageParentTransform.gameObject;
    }
}

あとは、Inspectorで必要なオブジェクトをアタッチしてあげてください!

RouletteMakerの↓ここ
スクリーンショット 2019-03-04 22.27.29.png

RouletteControllerの↓赤枠の部分です
スクリーンショット 2019-03-04 22.30.02.png

できたら再生して確認してください。
止まった場所の文字がResultText内に表示されるかと思います!

aaaaaa.gif

以上です!

##おまけ 露骨なイカサマルーレット

おまけと書いてますが、今回自分はこれが作りたくてルーレットをUnityで作りました笑

ikasama.gif

どこで止めても絶対に指定した場所まで最後にぶっ飛ぶ、、というルーレットです笑
指定した場所に自然に止まったらそのままです。
(露骨すぎますが盛り上がれば良し。)

###実装方法
神アセットDoTweenを拝借して、ルーレットの速度が遅くなったらDoRotateでルーレットをぶん回すという感じです。
もし、**自然な動きのイカサマルーレットが作りたい!**という方がいれば、まだルーレットの速度が速い時にRotateすれば良いのではないでしょうか!!

DoTweenをAssetStoreからProjectにimportして、

RouletteController.csを以下のように書き換えます。

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

public class RouletteController : MonoBehaviour {
    [HideInInspector] public GameObject roulette;
    [HideInInspector] public float rotatePerRoulette;
    [HideInInspector] public RouletteMaker rMaker;
    [SerializeField] private bool isIkasama;
    [SerializeField] private int ikasamaID;
    private string result;
    private float rouletteSpeed;
    private float slowDownSpeed;
    private int frameCount;
    private bool isPlaying;
    private bool isStop;
    [SerializeField] private Text resultText;
    [SerializeField] private Button startButton;
    [SerializeField] private Button stopButton;
    [SerializeField] private Button retryButton;

    public void SetRoulette () {
        isPlaying = false;
        isStop = false;
        startButton.gameObject.SetActive (true);
        stopButton.gameObject.SetActive (false);
        retryButton.gameObject.SetActive(false);
        startButton.onClick.AddListener (StartOnClick);
        stopButton.onClick.AddListener (StopOnClick);
        retryButton.onClick.AddListener (RetryOnClick);
    }

    private void Update () {
        if (!isPlaying) return;
        roulette.transform.Rotate (0, 0, rouletteSpeed);
        frameCount++;
        if (isStop && frameCount > 3) {
            rouletteSpeed *= slowDownSpeed;
            slowDownSpeed -= 0.25f * Time.deltaTime;
            frameCount = 0;
        }
        if (rouletteSpeed < 0.05f) {
            isPlaying = false;
            ShowResult (roulette.transform.eulerAngles.z);
        }
    }

    private void StartOnClick () {
        rouletteSpeed = 14f;
        startButton.gameObject.SetActive (false);
        Invoke ("ShowStopButton", 1.5f);
        isPlaying = true;
    }

    private void StopOnClick () {
        slowDownSpeed = Random.Range (0.92f, 0.98f);
        isStop = true;
        stopButton.gameObject.SetActive (false);
    }

    private void RetryOnClick(){
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }

    private void ShowStopButton () {
        stopButton.gameObject.SetActive (true);
    }

    private void ShowResult (float x) {
        for (int i = 1; i <= rMaker.choices.Count; i++) {
            if (((rotatePerRoulette * (i - 1) <= x) && x <= (rotatePerRoulette * i)) ||
                (-(360 - ((i - 1) * rotatePerRoulette)) >= x && x >= -(360 - (i * rotatePerRoulette)))) {
                result = rMaker.choices[i - 1];
            }
        }
        if(isIkasama && result != rMaker.choices[ikasamaID]){
            StartCoroutine(Buttobi());
            return;
        }
        resultText.text = result + "\nが当たったよ!";
        retryButton.gameObject.SetActive(true);
    }

     private IEnumerator Buttobi(){
         yield return roulette.transform.DORotate(
             new Vector3(0,0,rotatePerRoulette*((float)ikasamaID + 0.5f)),
             1.0f,
             RotateMode.FastBeyond360
         ).WaitForCompletion();
        resultText.text = rMaker.choices[ikasamaID] + "\nが当たったよ!";
        retryButton.gameObject.SetActive(true);
     }
}

このイカサマが使いたい時だけ、
↓InspectorのisIkasamaにチェックを入れ、選択肢配列の何番目を当たりにするかを指定してあげてください!(IDは0から始まるので注意)
スクリーンショット 2019-03-04 22.49.31.png

private IEnumerator Buttobi(){
         yield return roulette.transform.DORotate(
             new Vector3(0,0,rotatePerRoulette*((float)ikasamaID + 0.5f)),
             1.0f,
             RotateMode.FastBeyond360
         ).WaitForCompletion();
        resultText.text = rMaker.choices[ikasamaID] + "\nが当たったよ!";
        retryButton.gameObject.SetActive(true);
     }

↑この部分で回転を実装しています。
第2引数で秒数を指定しているので、もっと早く・遅くしたいなどあれば変えてください。

他にもスクリプト内の数字をいじって自分だけのルーレットにしてください!!

##GitHub

今回のサンプルプロジェクトをあげておきます。

GitHub

少しでも参考になれば嬉しいです。以上です!

22
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?