【Unity】ガチャ(抽選)を実装してみた

Unity

Unity初心者です。

初心者のため色々調べながら対応していますが、今回はガチャを実装してみたので備忘録として記載をしてみたいと思います。

ぶっちゃけ、乱数を作成して確率に照らし合わせていくだけなので、大した内容ではないですが、一応工夫した点としては、メンテナンス性なども考慮してscriptableobjectを使用してみました。

Unityで作ったガチャの内容

まず作ったガチャの内容ですが、以下の要件で作成を行いました。

  • レアリティは固定でNormal、Rare、SuperRareの3種類
  • 確率はフェスなどを意識して簡単に変更できるものとする
  • ガチャで出るキャラのデータも簡単に変更できるものとする
  • ガチャを引くためのお金を引数で渡して、お金が足りなかったら引かせない

こんな感じにしました。

レアリティの個数は固定としてしまったのですが、ここがゲーム内で変更になることは少ないかなあと思ったためです。(レアリティが途中で追加されるゲームって少ないと思ったので)

一方で確率やキャラのデータは結構変更することもあると思うので、scriptableobjectで管理をすることにしました。

あと、よくある機能としては

  • ピックアップ(特定のキャラだけ確率が上がる)
  • 10連保証(10連引くと高レアが1枚確定など)
  • 天井(100回引くと、目玉のキャラが確定で入手)
  • ボックスガチャ(1度引いたキャラは出現しないガチャ)
  • ステップアップガチャ(引く度に高レアの確率アップ)

とかですかね。

自分はガチでソシャゲを作っているわけではなくて、ゲーム内でカードパックを開けるみたいな機能を実装したかっただけなので、今回上記は除外しました。

余裕があったら、これらにもチャレンジしたいですね。

ScriptableObjectを使ってガチャの内容を定義

ガチャの内容をどこに持たせるかですが、ガチのソシャゲだとDBのテーブルだと思います。

自分はローカルでできる自作ゲームの配布をしているので、DBのテーブルは除外。

  • CSV
  • XML
  • Json
  • ソース内にハードコーディング

などいろいろ考えましたが、メンテナンスやデバッグの容易さからUnityのScriptableObjectを使用することにしました。

これに、後から変更になりそうな項目を追加しておきます。

(今回の場合は確率とキャラのIDリスト)

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

[CreateAssetMenu( menuName = "Create GachaTable", fileName = "GachaTableEntity" )]
public class GachaTable : ScriptableObject
{
    public new string name; // レアリティの名称(Normal 、Rareなど)
    public int probability; // 当たる確率
    public new List<string> cards; // 当たるアイテムのリスト
}

自分はカードゲームを作っているのでアイテムのリストを「cards」という変数名にしました。

↓こんな感じでResources/GachaTableフォルダの中に3つ作った

ガチャを引いた際のソース

次にガチャを実際に引いたときのプログラムソースを作成しました。

作成した結果が以下のとおりです。

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

public class test : MonoBehaviour
{

    private GachaTable[] gachatable = new GachaTable[3];
    private System.Random random;

    const int value = 100; // ガチャの値段
    const int maxIndex = 3; // レアリティの個数(Normal、Rare、SuperRareで3つ)
    const int countPerPack = 10; // 1回に引ける個数

    void Start()
    {
        // 抽選リストをロード
        for (int i = 0 ; i < maxIndex ; i++) {
            gachatable[i] =  Resources.Load<GachaTable>("GachaTable/" + (i + 1).ToString());
        }
        random = new System.Random((int)DateTime.Now.Ticks);
    }

    public List<string> packOpen(int money)
    {
        List<string> resultList = new List<string>();
        int totalProbability = 0;
        
        if(money < value)
        {
       // お金が足りなかったらnullを返却
            return null;
        }
        
        for (int i = 0 ; i < maxIndex ; i++) 
        {
            // レアリティの確率を足し合わせる
            totalProbability += gachatable[i].probability;
        }
        
        resultList = new List<string>(); // 抽選結果格納用変数
        for (int i = 0 ; i < countPerPack ; i++) 
        {
       // 抽選を行う
            string card = getCard(totalProbability);
            resultList.Add(card);    
        }

        return resultList;
    }

    private string getCard(int _allProbability)
    {
        // ガチャ全体の確率を足し合わせた数値から乱数を作成 (レアリティの抽選)
        int randomValue = getRamdom(_allProbability);
        int totalProbability = 0;

        for (int i = 0 ; i < maxIndex ; i++) 
        {
            totalProbability += gachatable[i].probability;
            if(totalProbability >= randomValue) 
            {
         // そのレアに含まれるキャラ数から乱数を作成 (キャラの抽選)
                string id = getRamdom(gachatable[i].cards);
                return id;
            }
        }
        return null;
    }

    private int getRamdom(int _max)
    {
        return random.Next(0, _max);
    }

    private string getRamdom(List<string> _list)
    {
        return _list[random.Next(0, _list.Count)];
    }
}

抽選する方法は2種類あるかなと思っていまして

  1. まずはレアリティを決定する抽選を行う→個別キャラの抽選を行う
  2. レアリティの確率を基に個別キャラの確率を計算する→個別キャラの抽選を行う

どちらでも良いと思ったのですが、今回は1の方法を採用しました。

仮にNormalの抽選確率が70%、Rareの抽選確率が20%、SuperRareの抽選確率が20%、各レアのキャラ数が10人の場合

まず1~100のどれかを返す乱数を作成して、

  • 70以下ならNormal
  • 90以下ならRare
  • 90以上ならSuperRare

とすればよいです。

90という数字は自分より低いレアの確率をすべて足していけば求められます。

その後、キャラ数が10人なので1~10のどれかを返す乱数を作成して、その結果をもとに結果を返せばよいです。

コメント

タイトルとURLをコピーしました