最近在做飛行棋項目,實現了一個投擲骰子的小功能。其中使用的Uniy自帶的物理碰撞產生隨機點數的功能。
設計要點:
- 利用Unity自帶的物理系統進行投擲,與周圍環境進行碰撞,增加隨機性。
- 利用觸發器判斷點數。
模型結構:
- 骰子模型,掛上剛體組件(Rigidbody),掛上碰撞體(BoxCollider),創建掛載TouZi.cs腳本。
- 在骰子六個面分別放置一個空物體,掛上觸發器(BoxCollider 勾選上 IsTrigger 選項,PS:我用的SphereCollider),創建掛載TouZi_Point.cs 腳本。
- 設置牆壁和地面約束骰子的位置,防止亂跳。
部分腳本:
TouZi_Point.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum PointType
{
One = 1, Two, Three, Four, Five, Six, None
}
public class TouZi_Point : MonoBehaviour
{
public PointType selfType;
public PointType OppositeType = PointType.None;
private void OnTriggerStay(Collider other)
{
if (other.tag == "Ground")
{
OppositeType = 7 - selfType;
}
}
private void OnTriggerExit(Collider other)
{
OppositeType = PointType.None;
}
public void ResetOpposite()
{
OppositeType = PointType.None;
}
/// <summary>
/// 數學計算的方式
/// </summary>
/// <returns></returns>
public float GetDot()
{
//Debug.Log(Vector3.Dot(Vector3.up, transform.up)+" "+ selfType);
return Vector3.Dot(Vector3.up, transform.up);
}
}
比較簡單不作介紹了
TouZi.cs
using System;
using System.Collections;
using System.Collections.Generic;
using HLCFrame_TouZi;
using UnityEngine;
public class TouZi : MonoBehaviour
{
/// <summary>
/// 當骰子靜止時,調用的回調。
/// </summary>
public event EventHandler<PointType> PointTypeCallback;
[Range(10, 20)]
public float explosionForce = 1;
[Range(1, 10)]
public float torque = 1;
private bool waiting = false;
private Rigidbody rig;
[SerializeField]
private PointType currentType = PointType.None;
[SerializeField]
private List<TouZi_Point> touZi_Points = new List<TouZi_Point>();
private void Awake()
{
rig = GetComponent<Rigidbody>();
}
IEnumerator WaitSelfStop()
{
yield return new WaitForSeconds(1);
while (rig.velocity.sqrMagnitude > 0.001)
{
yield return null;
}
PointTypeCallback?.Invoke(this, GetCurrentType());
StopAllCoroutines();
waiting = false;
}
public void Throw()
{
if (waiting)
{
return;
}
waiting = true;
for (int i = 0; i < touZi_Points.Count; i++)
{
touZi_Points[i].ResetOpposite();
}
currentType = PointType.None;
rig.AddExplosionForce(explosionForce, transform.position - new Vector3(0, 0.5f, 0), 1, 0, ForceMode.Impulse);
rig.AddTorque(UnityEngine.Random.onUnitSphere * torque, ForceMode.Impulse);
StartCoroutine("WaitSelfStop");
}
public PointType GetCurrentType()
{
for (int i = 0; i < touZi_Points.Count; i++)
{
if (touZi_Points[i].OppositeType != PointType.None)
{
currentType = touZi_Points[i].OppositeType;
break;
}
}
return currentType;
}
////////
///以下是 數學計算的方式,正確率 得到了提升 而且不消耗性能
////////
//float dot = 0, dot1 = 0;
//private PointType GetCurrentType()
//{
// dot = touZi_Points[0].GetDot();
// currentType = touZi_Points[0].selfType;
// for (int i = 0; i < touZi_Points.Count - 1; i++)
// {
// dot1 = touZi_Points[i + 1].GetDot();
// if (dot < dot1)
// {
// dot = dot1;
// currentType = touZi_Points[i + 1].selfType;
// }
// }
// return currentType;
// }
}
其中,
- 外界給 PointTypeCallback 註冊回調。
- Throw方法供外界調用,開始拋骰子,在骰子模型下方加一個爆炸力,把它炸起來,同時給一個隨機扭矩,讓它朝着一個隨機方向旋轉起來。
- 開啓協程等待骰子停下來。
- 執行回調,GetCurrentType() 方法遍歷子物體,找到朝上的一面的點數,將結果傳給註冊的回調。
還有一種方法判斷骰子朝上的點數,就是判斷骰子xyz三個軸和世界的xyz三個軸的數學關係。經過測試,在效率和容錯上,是優於物理碰撞檢測的。有興趣的可以自己研究一下。還可以省掉TouZiPoint這個腳本。
數學計算的方式我已經添加進去了,但是,我沒有省掉 TouZiPoint 這個腳本,各個面上的空物體的 Y軸正方向,向上垂直自己附屬的面,這樣計算 自己的UP 和 Vector3.Up 的Dot 就行,最後判斷各個 Dot 的大小,越大的 就越接近,直接取這個 touzipoint 的selfType 屬性就行了.