游戏仿真实验Unity仿真蒲丰投针实验,丢针计算圆周率,丢了一百万次针得出的结果是...
Posted 林新发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏仿真实验Unity仿真蒲丰投针实验,丢针计算圆周率,丢了一百万次针得出的结果是...相关的知识,希望对你有一定的参考价值。
文章目录
一、前言
嗨,大家好,我是新发。
我有个后端同事去面试的时候被问到了 蒲丰投针问题,可能有少部分同学没听过蒲丰投针问题,我这里简单科普一下。
假设桌面上画满间隔均为
D
D
D的平行直线,向桌面任意投放一根长为
L
L
L(
L
<
D
L<D
L<D)的针,可以通过几何概型的计算得出:针与某直线相交的概率为:
P
=
2
L
π
D
P = \\frac{2L}{πD}
P=πD2L
当
L
=
0.5
D
L = 0.5D
L=0.5D时,
P
=
2
L
π
D
=
D
π
D
=
1
π
P = \\frac{2L}{πD} = \\frac{D}{πD} = \\frac{1}{π} \\quad\\quad
P=πD2L=πDD=π1
也就是说,在
L
=
0.5
D
L = 0.5D
L=0.5D的情况下,我们总共投了
M
M
M根针,有
N
N
N根针与平行线相交,则
1
P
≈
M
N
≈
π
\\frac{1}{P} \\approx \\frac{M}{N} \\approx π \\quad\\quad
P1≈NM≈π
证明过程网上有很多,这里我就不展开了,我今天要做的,就是使用Unity
来对这个实验进行仿真,看看实验结果是不是与理论一致,我在网上没有看到有人使用Unity
做过蒲丰投针的仿真实验,那我就来做全网第一人吧~
二、制作场景
1、制作针模型
Unity
创建一个工程,在Hiererchy
面板空白处点击鼠标右键,点击菜单3D Object / Cylinder
,创建一个圆柱体,
如下
调整Scale
缩放,让它变成一个很细的圆柱体,
如下:
创建个材质球,调为红色,赋给它,这样看起来显眼一点,效果如下:
2、桌面制作
创建一个Cube
,
如下
调整Scale
的x
和z
,让它成为一个大桌面,
3、平行线制作
也是使用圆柱体,拉长,然后等间距排列即可,确保间距是针的长度的两倍,效果如下:
三、物理仿真
1、桌面无反弹
我希望针掉落到桌面时不要有弹跳,我们制作一个物理材质,
在Project
面板空白处右键鼠标,点击菜单Create / Physic Material
,
设置动态阻力和静态阻力为100
,弹性为0
,Friction Combine
和Bounce Combine
都设置为Minimum
,
把物理材质赋值给桌面的碰撞体组件的Material
属性,如下,
2、针掉落
给针添加Rigidbody
组件,
这样针就会收到重力作用而向下掉落,如下:
3、针滚动问题
我们看到针掉到桌面时,会滚动,
这是因为帧本身的角阻力太小,我们选中针物体,把它的Rigidbody
的角阻力设置为100
,
可以看到掉到桌面后不会滚动了,不过躺平时也会受到角阻力作用,
没关系,我们确保阵掉落的姿势与桌面平行就好了,
4、针架到平行线上的问题
我们看到,当帧落到平行线上时,会架在平行线上,
只需要把平行线弄成触发器就可以了,
效果如下:
5、针与针相互影响的问题
当一根针掉落在另一根针上时,会相互影响,比如架在别的针身上,影响与平行线的检测,比如这样子,
这个问题怎么解决呢?我们给针单独做一个层,
添加一个needle
层,
把针设置为needle
层,
然后打开Project Settings
,进入Physics
,把碰撞矩阵里的needle
勾选去掉,
可以看到,针与针可以相互穿透,互不影响了,
四、UI界面
使用UGUI
简单做一下界面,如下
五、编写代码
1、针脚本
创建一个Needle.cs
脚本,编写碰撞检测逻辑,逻辑很简单,我都写了注释,大家应该能看懂,
using UnityEngine;
using System;
public class Needle : MonoBehaviour
{
/// <summary>
/// 碰撞到平行线回调
/// </summary>
public Action hitLineCb;
/// <summary>
/// 碰撞到地面回调
/// </summary>
public Action hitPlaneCb;
private Rigidbody rig;
/// <summary>
/// 延迟回收
/// </summary>
private float delayHideTimer;
/// <summary>
/// 是否碰到了平行线
/// </summary>
private bool isHitedLine = false;
private void Awake()
{
rig = GetComponent<Rigidbody>();
}
private void OnEnable()
{
delayHideTimer = 0f;
rig.isKinematic = false;
isHitedLine = false;
}
public void OnTriggerEnter(Collider collision)
{
if (isHitedLine) return;
// 碰到了平行线
if("line" == collision.gameObject.tag)
{
isHitedLine = true;
hitLineCb?.Invoke();
}
}
public void OnCollisionEnter(Collision collision)
{
// 碰到了桌面
if("plane" == collision.gameObject.tag)
{
// 1秒后回收
delayHideTimer = 1f;
}
}
private void Update()
{
if (delayHideTimer > 0)
{
delayHideTimer -= Time.deltaTime;
if (delayHideTimer <= 0)
{
hitPlaneCb?.Invoke();
}
}
}
}
把Needle.cs
脚本挂到针物体上,
设置桌面的tag
为plane
,
设置平行线的tag
为line
,
2、对象池
因为要很高频地丢针,如果每次都创建销毁会比较浪费CPU
,所以我们写个对象池脚本:NeedlePool.cs
,代码如下:
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 对象池
/// </summary>
public class NeedlePool
{
/// <summary>
/// 入池
/// </summary>
public void Enqueue(GameObject obj)
{
objPool.Enqueue(obj);
}
/// <summary>
/// 出池
/// </summary>
public GameObject Dequeue()
{
if (objPool.Count == 0) return null;
return objPool.Dequeue();
}
private Queue<GameObject> objPool = new Queue<GameObject>();
}
3、入口脚本
创建Main.cs
作为入口脚本,实现交互和控制,代码如下:
using UnityEngine;
using UnityEngine.UI;
public class Main : MonoBehaviour
{
public Text totalCntText;
public Text hitedCntText;
public Text resultText;
public GameObject needleObj;
public Button startBtn;
public Text btnStateText;
private int totalCnt;
private int hitedCnt;
private float timer;
private bool isStarted = false;
// 对象池
private NeedlePool pool = new NeedlePool();
private void Awake()
{
needleObj.SetActive(false);
}
private void Start()
{
startBtn.onClick.AddListener(() =>
{
isStarted = !isStarted;
btnStateText.text = isStarted ? "停止" : "开始";
});
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// 鼠标点击,生成针
GenNeedle();
UpdateText();
}
if (isStarted)
{
// 自动生成针
timer += Time.deltaTime;
if (timer > 0.03f)
{
// 提高丢针数量,加快速度
for (int i = 0; i < 7; ++i)
{
GenNeedle();
}
UpdateText();
timer = 0;
}
}
}
/// <summary>
/// 生成针
/// </summary>
private void GenNeedle()
{
++totalCnt;
var obj = pool.Dequeue();
if(null == obj)
obj = Instantiate(needleObj);
var x = Random.Range(-100f, 100f);
var y = 0.1f;
var z = Random.Range(-60f, 60f);
obj.transform.position = new Vector3(x, y, z);
obj.transform.rotation = Quaternion.Euler(new Vector3(90, Random.Range(-360f, 360f), 0));
obj.SetActive(true);
var needle = obj.GetComponent<Needle>();
needle.hitLineCb = () =>
{
++hitedCnt;
UpdateText();
};
needle.hitPlaneCb = () =>
{
obj.SetActive(false);
pool.Enqueue(obj);
};
}
/// <summary>
/// 更新UI
/// </summary>
private void UpdateText()
{
totalCntText.text = totalCnt.ToString();
hitedCntText.text = hitedCnt.ToString();
if(0 != hitedCnt)
resultText.text = ((float)totalCnt / hitedCnt).ToString("#0.00000");
}
}
把Main.cs
脚本挂到MainPanel
上,并赋值成员对象,如下:
六、运行测试
运行Unity
,测试效果如下:
投了十万多针,
继续投针,投了一百万针,
与π
的理论值很接近啦,实验成功~
七、工程源码
本文工程我已上传到CODE CHINA
,感兴趣的同学可自行下载学习,
地址:https://codechina.csdn.net/linxinfa/UnityBuffonNeedlesGame
注:我使用的Unity
版本为Unity 2021.1.9f1c1 (64-bit)
八、完毕
好了,今天就到这里吧。
我是林新发:https://blog.csdn.net/linxinfa
原创不易,若转载请注明出处,感谢大家~
喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信,拜拜~
以上是关于游戏仿真实验Unity仿真蒲丰投针实验,丢针计算圆周率,丢了一百万次针得出的结果是...的主要内容,如果未能解决你的问题,请参考以下文章
游戏仿真实验使用Unity仿真电视机光学三原色显示画面,致敬袁隆平爷爷
游戏仿真实验使用Unity仿真电视机光学三原色显示画面,我是要成为海贼王的男人