ゲームではエネミーとのエンカウントやアイテムドロップ、あるいはガチャ結果など、指定した確率に基づいた処理を行うような場面が多々あります。そこで今回はエネミーとのエンカウント処理を例に、指定した確率に基づいて結果が変わるような処理を行う方法について説明したいと思います。
準備
イメージ
今回は前述のようにエネミーとのエンカウント処理を想定します。イメージとしては下図のように「探索」ボタンを押した際、予め指定した確率に基づいた処理を行い、テキストオブジェクトに「スライムとエンカウント」「ゴブリンとエンカウント」「魔物と遭遇しなかった」の3種類から結果に応じた表示をさせるものとします。
各オブジェクトの配置
イメージが掴めたところで、実際に各オブジェクトを配置してみましょう。詳細は省略しますが、私は以下のようにボタン・イメージおよびテキストを配置しました。
上図におけるオブジェクトの名前と役割は以下の通りです。
ResultText:「探索」ボタンが押された時の結果(確率処理結果)を表示するテキストExploreButton:確率処理のトリガーとなる(ボタンを押すことで確率処理を実施する)ボタン
指定した確率に基づいた処理を行う方法
必要な準備が整ったので、指定した確率に基づいた処理を行う方法について説明していきます。
STEP1:処理内容と確率を設定する
先ずは処理内容と確率を設定します。
今回は下記のように設定します。
処理内容①(スライムとのエンカウント):「スライムとエンカウント」とテキストオブジェクトに表示させる
処理内容①の発生確率:30%
処理内容②(ゴブリンとのエンカウント):「ゴブリンとエンカウント」とテキストオブジェクトに表示させる
処理内容②の発生確率:20%
処理内容③(エンカウントなし):「魔物と遭遇しなかった」とテキストオブジェクトに表示させる
処理内容③の発生確率:50%
STEP2:確率処理を実装するためのスクリプト作成する
次に確率処理(エンカウント計算処理)を実装するためのスクリプト作成します。今回は下図のようにEncountManagerという名前でスクリプトを作成しました。
STEP3:操作対象とするオブジェクトをスクリプトに認識させる
続いて操作対象とするオブジェクトを先ほど作成したスクリプトに認識させます。ここで言う操作対象とは今回の例では探索(エンカウント処理)結果を表示するReultTextオブジェクトです。
実際のスクリプトを示すと下記となります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//Text型を扱うために導入
public class EncountManager : MonoBehaviour
{
[SerializeField] Text ResultText = null;//エンカウント処理結果を表示させるTextオブジェクトとの連携のために導入
}
上記スクリプトに関しての説明は下記の通りです。
7行目:EncountManagerクラスに、エンカウント処理結果を表示させるTextオブジェクトを認識させるための構文。
STEP4:スクリプトをオブジェクトへアタッチする
上記STEP3で記述したスクリプトではまだ処理としては未完成ですが、説明のため、先ずは作成したスクリプトをオブジェクトにアタッチします。EncountManagerスクリプトのアタッチ先オブジェクトとして空のオブジェクトを作成し、その名前をEncountMangerObjectとしました。そしてこのEncountMangerObjectにEncountManagerスクリプトをアタッチします。(下図参照)
STEP5:スクリプトに操作したいオブジェクトを認識させる
STEP4でEncountMangerObjectにスクリプトをアタッチすると下図のように、インスペクターウィンドウ上でResultTextという箱ができます。そこにエンカウント計算結果を表示させたいResultTextオブジェクトをドラッグ&ドロップします。
STEP6:スクリプトを編集し、確率計算処理を実装する
STEP5の作業により操作対象となるオブジェクトをスクリプトが認識できたので、続いて実際に操作したい内容をスクリプトに記述します。ですが、その前に今回用いる確率計算の考え方を説明をします。
今回用いる確率計算の考え方
今回の例で考えた場合、下図のように100個の空箱を考えたとき、そのうち30個の箱に「スライムとエンカウント」という札を入れ、同様に「ゴブリンとエンカウント」という札を20個の箱に、「魔物と遭遇しない」という札を50個の箱に入れたとします。このときランダムに100個の箱から1つを選んで開けたとき、箱から出てきた札の内容は30%の確率で「スライムとエンカウント」、20%の確率で「ゴブリンとエンカウント」、50%の確率で「魔物と遭遇しない」になるはずです。したがって、この空箱を配列変数(encount[100])に、札を事象を表す変数(resultID)に置き換えることでプログラムとして実現できるというわけです。
例:20.1%であれば1000個のうち201個に札を入れる。
以上の考え方を使って作成したスクリプトは下記のような内容となります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//Text型を扱うために導入
public class EncountManager : MonoBehaviour
{
[SerializeField] Text ResultText = null;//エンカウント処理結果を表示させるTextオブジェクトとの連携のために導入
int[] resultID = new int[3];//処理結果を表すID resultID[0]:スライムとのエンカウント resultID[1]:ゴブリンとのエンカウント resultID[2]:エンカウントなしを表すものとする
float[] resultAR = new float[3];//各resultIDの出現率(resutAR[resultID]がresultIDが示す事象の出現確率を示す)
//エネミーとのエンカウント計算処理を行うメソッド
public void EncountCulc()
{
int result;//エンカウント計算結果のresultIDを格納する作業用変数
int count = 0;//作業用変数
int[] encount = new int[100];//各確率に応じた数のresultIDを格納する変数(今回の場合は出現率は整数であるとして場合は配列の大きさを100とする)
//変数の初期化処理///////////////////////////
for (int i = 0; i < 3; i++)
{
resultID[i] = i;
}
resultAR[0] = 0.30f;//スライムとのエンカウント率を30%に設定
resultAR[1] = 0.20f;//ゴブリンとのエンカウント率を20%に設定
resultAR[2] = 0.50f;//魔物と遭遇しない確率を50%に設定
//変数の初期化処理終了////////////////////////
//encount配列の中身を0に初期化
for (int i = 0; i < encount.Length; i++)
{
encount[i] = 0;
}
//resultIDの出現率に従ってresultIDを配列変数encount格納する処理
for(int i = 0; i < resultID.Length; i++)
{
if (resultAR[i] != 0)
{
for (int j = 0; j < Mathf.Floor(resultAR[i] * 100); j++)//出現率がfloatなので100倍して整数にする。小数点以下の誤差対策として切り捨て処理(Mathf.Floor)を使用する
{
encount[count] = resultID[i];
count++;
}
}
}
//ランダム関数を使用するので、先にランダムシードを時刻により初期化して、ランダム性を上げる
Random.InitState(System.DateTime.Now.Millisecond);
//ランダムインデックスを計算
int randomIndex = Random.Range(0, encount.Length);
//確率に応じたにエンカウント計算結果を変数result
result= encount[randomIndex];
//計算結果に応じてテキストオブジェクトの表示を変更する処理
if (result == resultID[0])
{
ResultText.text = "スライムとエンカウント";
}
else if (result == resultID[1])
{
ResultText.text = "ゴブリンとエンカウント";
}
else
{
ResultText.text = "魔物と遭遇しなかった";
}
}
}
上記スクリプトに関しての説明は下記の通りです。ただし、4,7行目は前述と変わりありませんので、説明は省略しています。
resultID[0]:スライムとのエンカウント
resultID[1]:ゴブリンとのエンカウント
resultID[2]:エンカウントなし
を表すものとする
9行目:各resultIDの出現率(resutAR[resultID]がresultIDが示す事象の出現確率を示す)を表す変数の定義
resutAR[0]:スライムとのエンカウント率
resutAR[1]:ゴブリンとのエンカウント率
resutAR[2]:エンカウントなしとなる確率11~61行目:エネミーとのエンカウント計算処理を行うメソッド
13行目:エンカウント計算結果のresultIDを格納する作業用変数を定義
14行目:作業用変数を定義
15行目:各確率に応じた数のresultIDを格納する配列変数(今回の場合は出現率は整数であるとして場合は配列の大きさを100とする)を定義
17~20行目:ID変数の初期化
21~23行目:各事象の出現確率を設定
26~30行目:encount配列のを0に初期化
31~41行目:resultIDの出現率に従ってresultIDを配列変数encount格納する処理(空箱に出現率に応じた数の札を入れる処理)
43行目~47行目:配列変数encountからランダムに選ばれた配列の内容を1つ取り出す処理(札が入った箱からランダムに1つだけ札を取り出す処理)
43行目:ランダム関数を用いるときに使用するシード(種)の設定
補足:決まった数をシードに設定した場合、予め決められた順番で同じ結果が出てしまうため、それを防ぐ目的で時刻により初期化し、ランダム性を上げている
45行目:変数randomIndexを0~99の範囲でランダムに計算する処理(何番目の箱から札を取り出すかを決める処理)
47行目:配列変数encountからrandomIndex番目の内容を取り出し、resultに一時格納する処理(上で決めた箱から実際に札を取り出す処理)
49行目~61行目:変数resultの内容に応じてResultTextが表すテキストオブジェクトの表示内容を変更する処理
STEP7:ボタンオブジェクトのクリック時動作を設定する
最後にボタンクリック時の動作をボタンオブジェクトに認識させます。設定を行うには具体的には下図のようにExploreButtonオブジェクトのOnClick()に先に実装したEncountCulcメソッドを設定します。

機能テスト
ここまでできたらPlayボタンを押してゲームを起動させ、処理が想定どおりに行われているか確認しましょう。実際に起動させてみた結果、下図のようにボタンを押す毎にエンカウントの計算がされ、結果に応じて結果表示用テキストの内容が変更されました。
今回は指定した確率に基づいた処理を行う方法について説明しましたがいかがだったでしょうか。冒頭述べたように、今回のような確率処理はアイテムドロップ、あるいはガチャ結果などにも利用でき、様々な場面で使える内容であると思います。また、今回の内容とUnity基礎9の内容を組み合わせて、CSVでエネミーの名前や出現率などを指定し、ボタンを押した時に出現率に応じて表示されるエネミーの画像やHPなどを変更するといった、より実際のゲーム制作に近いこともできると思います。今回の内容が皆さんのゲーム制作に少しでもお役に立てれば幸いです。
以上。
コメント