本日はUnity枠です。
現在Osaka MR Hackathonに参加しており、SugorokuXRを開発中です。
Sugorokuと名の付く通りすごろくを再現していますが、サイコロを振る挙動とサイコロのマス目の大きさを判定する機能の実装を行いましたので紹介します。
〇さいころ
さいころは次のように簡単にモデリングを行いました。
色がないのは現在機能を開発するほぼホワイトボックスであるためです。

〇さいころをなげる
サイコロを投げるスクリプトは以下のブログさんを参考にさせていただきました。
public void DiseAciton()
{
//自身にアタッチされているRigidBodyの重力をオンにする
rb.useGravity = true;
initialized = false;
//転がす向き
Vector3 forceDirection = new Vector3(0f, 1.0f, 1.0f);
//転がす強さ
float forceMagnitude = 10.0f;
Vector3 force = forceMagnitude * forceDirection;
rb.AddForce(force, ForceMode.Impulse);
//目の判定 後述
StartCoroutine(DiseResult());
}
コードの中身はほぼ参考ブログのままなのでここでの解説は控えます。
今回は外部のアクションでDiseAction()を呼び出す形で使用を想定しています。
〇さいころの目判定
次にサイコロのでた目判定を行います。
今回は各目に座標取得用のからのゲームオブジェクトをアタッチして、それぞれのオブジェクトのY座標(高さ)を比較して一番高いオブジェクトを判定に使用します。
サイコロのオブジェクトに6つの空オブジェクトをアタッチします。

例えば1というからオブジェクトはサイコロの1の目の面の中央に当たる場所に配置します。

次にスクリプトは次のようになります。
IEnumerator DiseResult()
{
//5秒後に実行
yield return new WaitForSeconds(5f);
//表示テキストのUIをサイコロに合わせる
_UI.transform.position = this.transform.position;
for (int i = 0; i < 6; i++)
{
//_diseNumber[i]は1~6の判定用からオブジェクト
_disePosY[i] = _diseNumber[i].transform.position.y;
}
float maxPos = _disePosY.Max();
for (int i = 0; i < 6; i++)
{
if (_diseNumber[i].transform.position.y == maxPos)
{
_diseResult = i + 1;
text.text = _diseResult.ToString();
break;
}
}
}
サイコロを振るDiseAction()が実行されてから目が出るまでに時間が必要なためコルーチンとしてyield return new WaitForSeconds(5f)で5秒後に判定を実行するようにしています。
最初のfor文では1~6の各からオブジェクトのY座標をfloat[] _disePosYに格納しています。
for (int i = 0; i < 6; i++)
{
_disePosY[i] = _diseNumber[i].transform.position.y;
}
maxPosには_disePosYの中で最大値を返しています。
これはpublic static float Max()を使用しています。
次のfor文では再度1~6までの_disePosを比較して先に格納している最大値と一致するところで値を返しています。
for (int i = 0; i < 6; i++)
{
if (_diseNumber[i].transform.position.y == maxPos)
{
_diseResult = i + 1;
//UI表示用
text.text = _diseResult.ToString();
break;
}
}
〇実機で確認
実機で確認すると次のようになります。
〇コード全文
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine;
using TMPro;
public class Dise : MonoBehaviour
{
[SerializeField] private GameObject _UI;
[SerializeField] private TextMeshPro text;
private bool initialized =true;
public int _diseResult;
[SerializeField] private Transform[] _diseNumber;
[SerializeField] private float[] _disePosY = new float[6];
[SerializeField] private Rigidbody rb;
[SerializeField] private bool _debug;
private void Start()
{
_disePosY = new float[6];
rb.useGravity = false;
}
void Update()
{
if (initialized)
{
transform.Rotate(new Vector3(0.1f, 0.3f, 0.5f));
}
}
public void DiseAciton()
{
rb.useGravity = true;
initialized = false;
Vector3 forceDirection = new Vector3(0f, 1.0f, 1.0f);
float forceMagnitude = 10.0f;
Vector3 force = forceMagnitude * forceDirection;
rb.AddForce(force, ForceMode.Impulse);
StartCoroutine(DiseResult());
}
IEnumerator DiseResult()
{
yield return new WaitForSeconds(5f);
_UI.transform.position = this.transform.position;
for (int i = 0; i < 6; i++)
{
_disePosY[i] = _diseNumber[i].transform.position.y;
Debug.Log(_disePosY[i]);
}
float maxPos = _disePosY.Max();
for (int i = 0; i < 6; i++)
{
if (_diseNumber[i].transform.position.y == maxPos)
{
_diseResult = i + 1;
text.text = _diseResult.ToString();
break;
}
}
}
}