最近在做飞行棋项目,实现了一个投掷骰子的小功能。其中使用的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 属性就行了.