Unity 波函数坍缩算法随机地图生成
- 波函数
- 波函数基本概念
- 位置空间波函数
- 动量空间波函数
- 两种波函数之间的关系
- 波函数的本征值和本征态
- 波函数坍缩
- 熵是什么
- 熵作为状态函数
- 时间之箭
- 实现原理
- 举个例子:2D迷宫地图生成
- Unity 如何实现
- 前期准备
- 单元格代码
- 瓦片地图代码
- 波函数代码
- 熵检查方法
- 坍缩方法
- 有效性方法
- 完整代码
- 脚本搭载
- 实现 效果
- 可扩展效果
波函数
波函数基本概念
在量子力学里,量子系统的量子态可以用波函数(英语:Wave function)来描述。薛定谔方程设定波函数如何随着时间流逝而演化。
波函数 ψ(r,t) 是一种复值函数,表示粒子在位置 r 、时间 t 的概率幅,它的绝对值平方 |ψ(r,t)|² 是在位置 r 、时间 t 找到粒子的概率密度。以另一种角度诠释,波函数 ψ(r,t) 是“在某时间、某位置发生相互作用的概率幅”。
设想经典力学里的谐振子系统(A-B),一条弹簧的一端固定不动,另一端有一个带质量圆球;
在量子力学里,(C-H)展示出同样系统的薛定谔方程的六个波函数解。
横轴坐标表示位置,竖轴坐标表示波函数概率幅的实部(蓝色)或虚部(红色)。
(C-F)是定态,(G、H)不是定态。定态的能量为驻波振动频率与约化普朗克常数的乘积。
这里简单了解一下就行。
位置空间波函数
我们主要用到的就是这个位置空间波函数,不过也不用管,了解基本概念就行
假设一个自旋为零的粒子移动于一维空间。这粒子的量子态以波函数表示为 ψ(r,t) ;其中,x 是位置,是时间。波函数是复值函数。测量粒子位置所得到的结果不是决定性的,而是概率性的。粒子的位置 x 在区间 [a,b](即 a ≤ x ≤b )的概率为 Pa ≤ Px ≤ Pb 为
其中,** t** 是对于粒子位置做测量的时间。
换句话说 |ψ(r,t)|² 是粒子在位置 x 、时间 t 的概率密度。
这导致归一化条件:在位置空间的任意位置找到粒子的概率为 100%
好了终于打完了,真不好写。简单来说就是你想在什么时候吃饭就会在什么时候吃饭。
由于世界的不确定性,当你决定做什么的时候,就证明你已经做了什么事情。
完了,还解释不清楚了。
也可以这么理解,当你喝咖啡的时候你喝奶茶的概率就变成 0,你喝咖啡的概率就变成 1。
就先这样理解吧,这个还真不好解释。
动量空间波函数
这个算是扩展,看不看都行
在动量空间,粒子的波函数便是为 ψ(p,t) ;其中 p 是一维动量,值域从 ** -∞ ** 到 +∞。测量粒子动量所得到的结果不是决定性的,而是概率性的。粒子的动量 p 在区间 [a,b] 即 (a ≤ p ≤ b) 的概率为
动量空间波函数的归一条件是:
两种波函数之间的关系
位置空间波函数与动量空间波函数彼此是对方的傅里叶变换。他们各自拥有的信息相同,任何一种波函数都可以用来计算粒子的相关性质。两种波函数之间的关系为
波函数的本征值和本征态
在量子力学中,,态就意味着函数,因为量子力学的状态是用波函数来描述的,因此只要是态,,就是波函数。
本征函数定义:如果一个算符A作用在一个函数上,等于一个常数a乘以这个函数, 就说该函数是这个算符本征值为a的本征函数。
如果是非简并的本征态,,本征值和本征态存在着一 一对应的关系。 量子力学中属于不同本征值的本征态一定相互正交(厄米算符性质)
如果是简并的本征态,属于同一本征值的本征态的线性组合依然是该算符的本征态, 不再存在着一一对应的关系,但依然可以组合成相互正交的本征函数。
本征值和本征态
波函数坍缩
波函数坍缩(wave function collapse)指的是某些量子力学体系与外界发生某些作用后波函数发生突变,变为其中一个本征态或有限个具有相同本征值的本征态的线性组合的现象。波函数坍缩可以用来解释为何在单次测量中被测定的物理量的值是确定的,尽管多次测量中每次测量值可能都不同。
双缝干涉实验
熵是什么
熵是一种测量在动力学方面不能做功的能量总数,也就是当总体的熵增加,其做功能力也下降熵的量度正是能量退化的指标。熵亦被用于计算一个系统中的失序现象,也就是计算该系统混乱的程度。
熵是一个描述系统状态的函数,但是经常用熵的参考值和变化量进行分析比较它在控制论、概率论、数论、天体物理、生命科学等领域都有重要应用在不同的学科中也有引申出的更为具体的定义,是各领域十分重要的参量。
熵作为状态函数
首先,想象一个可逆过程,如果将系统从一个平衡状态A转移到另一个平衡状态B。假如再经过一个任何可逆过程将系统带回状态A,结果是熵的绝对变化等于零。这意味着在第一个过程中,熵的变化仅仅取决于初始与终结状态.由此可以定义一个系统的任何平衡状态的熵。选择一个参照状态R,定义它的熵为,任何平衡状态X的熵为:
因为这个积分式与热转移过程无关,所以当作为熵的定义。
时间之箭
熵是在物理学领域中似乎暗示只朝向一个特定行进方向的量,有时被称为时间之箭。随着时间的推移,热力学第二定律:孤立系统的熵状态永远只会增加,不会减少。因此,从这个角度看,熵的测量被看作是一种时钟。
实现原理
我们假定你有图案的一些“局部信息 A ”,也就是说,图案中的一小块应该长成什么样子,以及这些小块之间的一些重叠关系能否出现。图案中的一小块我们用一个固定大小的小图片来表示。如果你还知道这些小块之间出现几率的比例就更好了。当然不知道也没有关系,我们可以假定它们的出现几率差不多。那么我们的目标就是生成一个图片,使得其所有的局部(也就是固定大小的小区域)都是在之前的“局部信息”中出现过的,并且出现的几率和之前给出的“局部信息”的几率尽量相近。
最小不确定性原则:
在每次迭代中,选择具有最小可能图案数的格子进行确定性坍缩,这有助于减少错误和回溯的可能性。
兼容性检查:
当一个格子的波函数坍缩时,必须检查它与相邻格子的兼容性,以确保生成的地图符合局部规则。
概率分布:
波函数的坍缩基于概率分布,这意味着算法可以生成具有随机性的地图,同时保持整体的连贯性和规则性。
示例1:切片A 是已经确定的地图,那么连接处 瓦片地图出现的概率就是下面这张图
示例2:切片A 是已经确定的地图,那么连接处 瓦片地图出现的概率就是下面这张图
这里考虑的是四方,可以自己进行扩展。
举个例子:2D迷宫地图生成
假设我们要用波函数坍缩算法在Unity中生成一个2D迷宫地图,这个地图由墙壁和路径组成。
1. 定义基本单元:
墙壁瓦片:表示迷宫中的墙壁,不可通过。
路径瓦片:表示迷宫中的开放路径,可以通行。
2. 初始化波函数:
我们有一个10x10的网格,每个格子的波函数包含墙壁和路径两种可能的状态。
3. 建立局部规则:
规则定义了路径瓦片周围不能有4个连续的墙壁瓦片,以避免形成死胡同或封闭区域。
墙壁瓦片则可以与任何类型的瓦片相邻。
4. 确定性坍缩:
假设我们从左上角开始,选择一个格子进行坍缩,由于没有其他信息,这个格子有相等的机会成为墙壁或路径。
假设我们随机选择路径瓦片,那么这个格子的波函数就坍缩为路径瓦片。
5. 传播更新:
坍缩后,我们需要更新相邻格子的波函数。例如,如果选择了路径瓦片
那么它的直接邻居不能全部是墙壁瓦片,以避免形成死胡同。
6. 迭代过程:
接下来,我们选择下一个熵最小的格子进行坍缩,重复这个过程,直到整个网格都被填充。
7. 生成地图:
最终,我们根据每个格子坍缩后的状态,实例化对应的Unity预制体。
例如,路径瓦片可能是一个可通行的平面,而墙壁瓦片可能是一个不可通行的障碍物。
假设我们的迷宫网格如下:
[墙壁, 路径, 墙壁, ..., 路径]
[路径, 路径, 路径, ..., 墙壁]
[墙壁, 墙壁, 路径, ..., 路径]
...
[路径, 墙壁, 路径, ..., 墙壁]
现在,我们从左上角开始,第一个格子坍缩为路径瓦片,然后根据规则更新相邻格子的波函数。
比如,第一个格子的右边和下边的格子不能同时是墙壁瓦片。
其实还挺好理解的。
Unity 如何实现
前期准备
1. 新建一个工程,根据自己使用的创建就行,我这里使用的是 2022.30
2. 准备 自己喜欢并且可以连续的网片地图
3. 最好写一个实现流程或者思维导图(不写也行,写的话会更有条理一些)
单元格代码
根据你想要实现的效果,设置单元地图预制体。
这个代码的作用就是地图生成缓存
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 单元格
/// </summary>
public class Cell_ZH : MonoBehaviour
{
/// <summary>
/// 坍缩布尔
/// </summary>
public bool _Collapsed;
/// <summary>
/// 瓦片地图
/// </summary>
public Tile_ZH[] _Tileoptions;
/// <summary>
/// 创建单元格
/// </summary>
/// <param name="_CollapseState"></param>
/// <param name="_Tiles"></param>
public void CreateCell(bool _CollapseState, Tile_ZH[] _Tiles)
{
_Collapsed = _CollapseState;
_Tileoptions = _Tiles;
}
/// <summary>
/// 重建 单元
/// </summary>
/// <param name="_Tiles"></param>
public void RecreateCell(Tile_ZH[] _Tiles)
{
_Tileoptions = _Tiles;
}
}
脚本搭载以及预制体
瓦片地图代码
就是生成瓦片地图相关的概率分布,没坍缩之前都是均等的。
当然也可以扩展成八方和 3D 的。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 瓦片地图
/// </summary>
public class Tile_ZH : MonoBehaviour
{
//可增加 瓦片类型 比如左上角 右上角 左下角 右下角等
[Header("上方 相邻数组")]
public Tile_ZH[] _UpNeighbours;
[Header("右边 相邻数组")]
public Tile_ZH[] _RigihtNeighbours;
[Header("下方 相邻数组")]
public Tile_ZH[] _DownNeighbours;
[Header("左边 相邻数组")]
public Tile_ZH[] _LeftNeighbours;
}
脚本搭载以及预制体
波函数代码
熵检查方法
主要作用就是:
创建一个临时网格列表,移除已坍缩的单元格,按照瓦片选项数组长度对单元格排序
优先处理选项最少的单元格。
/// <summary>
/// 检查熵
/// 临时网格获取
/// </summary>
/// <returns></returns>
private IEnumerator CheckEntropy()
{
// 临时网格数组,用于存储当前未坍缩的单元格
List<Cell_ZH> _TempGrid = new List<Cell_ZH>(_GridCompoents);
// 从临时网格中移除所有已经坍缩的单元格
_TempGrid.RemoveAll(c => c._Collapsed);
// 按照 _Tileoptions 数组的长度进行排序,长度最小的单元格优先处理
_TempGrid.Sort((a, b) => { return a._Tileoptions.Length - b._Tileoptions.Length; });
// 获取最小的 _Tileoptions 数组的长度
int _ArrLenght = _TempGrid[0]._Tileoptions.Length;
int _StopIndex = default;
// 查找最小长度的单元格群组,并获取停止索引
for (int i = 0; i < _TempGrid.Count; i++)
{
if (_TempGrid[i]._Tileoptions.Length > _ArrLenght)
{
_StopIndex = i;
break;
}
}
// 如果找到了停止索引,就移除后续的单元格,只保留熵最低的一组
if (_StopIndex > 0)
{
_TempGrid.RemoveRange(_StopIndex, _TempGrid.Count - _StopIndex);
}
// 等待一段时间后开始坍缩单元格
yield return new WaitForSeconds(0.01f);
// 调用 CollapseCell 方法,对选中的单元格群组进行坍缩处理
CollapseCell(_TempGrid);
}
坍缩方法
主要作用就是:
随机选择一个单元格进行坍缩,设置坍缩状态并选择一个瓦片进行实例化
调用 UpdateGeneration() 方法更新网格。
/// <summary>
/// 单元格 坍缩
/// 获取当前单元格 确定变量
/// </summary>
/// <param 临时网格="_TempGrid"></param>
private void CollapseCell(List<Cell_ZH> _TempGrid)
{
//随机瓦片数组
int _RandIndex = UnityEngine.Random.Range(0, _TempGrid.Count);
//获取当前随机的地图
Cell_ZH _CellToCollapse = _TempGrid[_RandIndex];
//坍缩布尔设置
_CellToCollapse._Collapsed = true;
//获取当前单元格坍缩值
//print(UnityEngine.Random.Range(0, _CellToCollapse._Tileoptions.Length));
if (_CellToCollapse._Tileoptions != null && _CellToCollapse._Tileoptions.Length > 0)
{
Tile_ZH _SelectedTile = _CellToCollapse._Tileoptions[UnityEngine.Random.Range(0, _CellToCollapse._Tileoptions.Length)];
_CellToCollapse._Tileoptions = new Tile_ZH[] { _SelectedTile };
_CellToCollapse._Tileoptions = new Tile_ZH[] { _SelectedTile };
Tile_ZH _FoundTile = _CellToCollapse._Tileoptions[0];
//实例化当前地图
Instantiate(_FoundTile, _CellToCollapse.transform.position * 4.73f, Quaternion.identity, GameObject.Find("Model").transform);
//网格更新
UpdateGeneration();
}
}
有效性方法
主要作用就是:这个方法比较重要
确保当前单元格的瓦片选项在有效选项列表中
如果没有有效选项,添加一个默认选项。
/// <summary>
/// 有效性 检查
/// </summary>
/// <param 功能单元格列表="_OptionList"></param>
/// <param 有效性单元格列表="_ValidOptions"></param>
private void CheckValidity(List<Tile_ZH> _OptionList, List<Tile_ZH> _ValidOptions)
{
// 有效性检查 布尔
bool _HasValidOptions = false;
// 单元格列表比对
for (int x = _OptionList.Count - 1; x >= 0; x--)
{
var _Element = _OptionList[x];
// 检查有效性单元格列表是否包含当前单元格数据
if (!_ValidOptions.Contains(_Element))
{
// 如果不包含就进行移除处理
_OptionList.RemoveAt(x);
}
else
{
_HasValidOptions = true;
}
}
// 如果没有找到任何有效选项,采取措施
if (!_HasValidOptions)
{
// 保留一个默认的选项(这里可以根据具体情况设定)
if (_OptionList.Count == 0 && _ValidOptions.Count > 0)
{
// 添加一个有效性选项中的第一个作为默认选项
_OptionList.Add(_ValidOptions[0]);
}
else if (_OptionList.Count == 0)
{
// 如果确实没有任何有效选项,则添加一个兜底选项(可以是一个最常用或最不影响整体的瓦片)
// 假设第一个瓦片是兜底选项
_OptionList.Add(_TileObjects[0]);
}
Debug.Log("没有有效选项,回退操作");
}
}
完整代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary>
/// 波函数
/// </summary>
public class WaveFunctiopn_ZH : MonoBehaviour
{
[Header("地图尺寸")]
public int _Dimensions;
[Header("平铺对象数组")]
public Tile_ZH[] _TileObjects;
[Header("网格数组")]
public List<Cell_ZH> _GridCompoents;
[Header("单元格")]
public Cell_ZH _CellObj;
//迭代
int _Iterations = 0;
private void Awake()
{
_GridCompoents = new List<Cell_ZH>();
InitializeGrid();
}
/// <summary>
/// 网格初始化
/// </summary>
void InitializeGrid()
{
for (int y = 0; y < _Dimensions; y++)
{
for (int x = 0; x < _Dimensions; x++)
{
//根据地图大小 生成单元格
Cell_ZH _NewCell = Instantiate(_CellObj, new Vector2(x, y), Quaternion.identity, GameObject.Find("Model").transform);
//单元格创建
_NewCell.CreateCell(false, _TileObjects);
//网格数组添加
_GridCompoents.Add(_NewCell);
}
}
StartCoroutine(CheckEntropy());
}
/// <summary>
/// 检查熵
/// 临时网格获取
/// </summary>
/// <returns></returns>
private IEnumerator CheckEntropy()
{
// 临时网格数组,用于存储当前未坍缩的单元格
List<Cell_ZH> _TempGrid = new List<Cell_ZH>(_GridCompoents);
// 从临时网格中移除所有已经坍缩的单元格
_TempGrid.RemoveAll(c => c._Collapsed);
// 按照 _Tileoptions 数组的长度进行排序,长度最小的单元格优先处理
_TempGrid.Sort((a, b) => { return a._Tileoptions.Length - b._Tileoptions.Length; });
// 获取最小的 _Tileoptions 数组的长度
int _ArrLenght = _TempGrid[0]._Tileoptions.Length;
int _StopIndex = default;
// 查找最小长度的单元格群组,并获取停止索引
for (int i = 0; i < _TempGrid.Count; i++)
{
if (_TempGrid[i]._Tileoptions.Length > _ArrLenght)
{
_StopIndex = i;
break;
}
}
// 如果找到了停止索引,就移除后续的单元格,只保留熵最低的一组
if (_StopIndex > 0)
{
_TempGrid.RemoveRange(_StopIndex, _TempGrid.Count - _StopIndex);
}
// 等待一段时间后开始坍缩单元格
yield return new WaitForSeconds(0.01f);
// 调用 CollapseCell 方法,对选中的单元格群组进行坍缩处理
CollapseCell(_TempGrid);
}
/// <summary>
/// 单元格 坍缩
/// 获取当前单元格 确定变量
/// </summary>
/// <param 临时网格="_TempGrid"></param>
private void CollapseCell(List<Cell_ZH> _TempGrid)
{
//随机瓦片数组
int _RandIndex = UnityEngine.Random.Range(0, _TempGrid.Count);
//获取当前随机的地图
Cell_ZH _CellToCollapse = _TempGrid[_RandIndex];
//坍缩布尔设置
_CellToCollapse._Collapsed = true;
//获取当前单元格坍缩值
//print(UnityEngine.Random.Range(0, _CellToCollapse._Tileoptions.Length));
if (_CellToCollapse._Tileoptions != null && _CellToCollapse._Tileoptions.Length > 0)
{
Tile_ZH _SelectedTile = _CellToCollapse._Tileoptions[UnityEngine.Random.Range(0, _CellToCollapse._Tileoptions.Length)];
_CellToCollapse._Tileoptions = new Tile_ZH[] { _SelectedTile };
_CellToCollapse._Tileoptions = new Tile_ZH[] { _SelectedTile };
Tile_ZH _FoundTile = _CellToCollapse._Tileoptions[0];
//实例化当前地图
Instantiate(_FoundTile, _CellToCollapse.transform.position * 4.73f, Quaternion.identity, GameObject.Find("Model").transform);
//网格更新
UpdateGeneration();
}
}
/// <summary>
/// 网格迭代更新
/// </summary>
private void UpdateGeneration()
{
//缓存单元格数组
List<Cell_ZH> _NewGeneraTionCell = new List<Cell_ZH>(_GridCompoents);
for (int y = 0; y < _Dimensions; y++)
{
for (int x = 0; x < _Dimensions; x++)
{
//快查算法
var _Index = x + y * _Dimensions;
//如果当前单元格已经坍缩 就直接 break
if (_GridCompoents[_Index]._Collapsed)
{
_NewGeneraTionCell[_Index] = _GridCompoents[_Index];
}
//如果没有坍缩
else
{
//创建瓦片地图索引
List<Tile_ZH> _Options = new List<Tile_ZH>();
foreach (var item in _TileObjects)
{
_Options.Add(item);
}
//四方检测
//上方单元格检测
if (y>0)
{
//索引查找
Cell_ZH _Up = _GridCompoents[x + (y - 1) * _Dimensions];
List<Tile_ZH> _ValidOptions= new List<Tile_ZH>();
foreach (Tile_ZH _PossibleOptions in _Up._Tileoptions)
{
var _ValOptions = Array.FindIndex(_TileObjects, _Obj => _Obj == _PossibleOptions);
var _Valid = _TileObjects[_ValOptions]._UpNeighbours;
_ValidOptions = _ValidOptions.Concat(_Valid).ToList();
}
//单元格有效性检查
CheckValidity(_Options,_ValidOptions);
}
//右侧单元格检测
if (x<_Dimensions-1)
{
Cell_ZH _Right = _GridCompoents[x +1+y * _Dimensions].GetComponent<Cell_ZH>();
List<Tile_ZH> _ValidOptions = new List<Tile_ZH>();
foreach (Tile_ZH _PossibleOptions in _Right._Tileoptions)
{
var _ValOptions = Array.FindIndex(_TileObjects, _Obj => _Obj == _PossibleOptions);
var _Valid = _TileObjects[_ValOptions]._LeftNeighbours;
_ValidOptions = _ValidOptions.Concat(_Valid).ToList();
}
//单元格有效性检查
CheckValidity(_Options, _ValidOptions);
}
//下方 单元格检测
if (y<_Dimensions-1)
{
Cell_ZH _Down = _GridCompoents[x + (y+1) * _Dimensions].GetComponent<Cell_ZH>();
List<Tile_ZH> _ValidOptions = new List<Tile_ZH>();
foreach (Tile_ZH _PossibleOptions in _Down._Tileoptions)
{
var _ValOptions = Array.FindIndex(_TileObjects, _Obj => _Obj == _PossibleOptions);
var _Valid = _TileObjects[_ValOptions]._DownNeighbours;
_ValidOptions = _ValidOptions.Concat(_Valid).ToList();
}
//单元格有效性检查
CheckValidity(_Options, _ValidOptions);
}
//左侧 单元格检测
if (x>0)
{
Cell_ZH _Left = _GridCompoents[x -1+y * _Dimensions].GetComponent<Cell_ZH>();
List<Tile_ZH> _ValidOptions = new List<Tile_ZH>();
foreach (Tile_ZH _PossibleOptions in _Left._Tileoptions)
{
var _ValOptions = Array.FindIndex(_TileObjects, _Obj => _Obj == _PossibleOptions);
var _Valid = _TileObjects[_ValOptions]._RigihtNeighbours;
_ValidOptions = _ValidOptions.Concat(_Valid).ToList();
}
//单元格有效性检查
CheckValidity(_Options,_ValidOptions);
}
//瓦片地图编号
//数组位置等于该瓦片位置
Tile_ZH[] _NewTileList = new Tile_ZH[_Options.Count];
for (int i = 0; i < _Options.Count; i++)
{
_NewTileList[i] = _Options[i];
}
//瓦片地图创建
_NewGeneraTionCell[_Index].RecreateCell(_NewTileList);
}
}
}
//瓦片地图 通用组件判定
_GridCompoents = _NewGeneraTionCell;
//迭代次数 增加
_Iterations++;
//如果迭代次数 小于设置的瓦片地图大小
if (_Iterations<_Dimensions*_Dimensions)
{
//就再次进行熵 检查
StartCoroutine(CheckEntropy());
}
}
/// <summary>
/// 有效性 检查
/// </summary>
/// <param 功能单元格列表="_OptionList"></param>
/// <param 有效性单元格列表="_ValidOptions"></param>
private void CheckValidity(List<Tile_ZH> _OptionList, List<Tile_ZH> _ValidOptions)
{
// 有效性检查 布尔
bool _HasValidOptions = false;
// 单元格列表比对
for (int x = _OptionList.Count - 1; x >= 0; x--)
{
var _Element = _OptionList[x];
// 检查有效性单元格列表是否包含当前单元格数据
if (!_ValidOptions.Contains(_Element))
{
// 如果不包含就进行移除处理
_OptionList.RemoveAt(x);
}
else
{
_HasValidOptions = true;
}
}
// 如果没有找到任何有效选项,采取措施
if (!_HasValidOptions)
{
// 保留一个默认的选项(这里可以根据具体情况设定)
if (_OptionList.Count == 0 && _ValidOptions.Count > 0)
{
// 添加一个有效性选项中的第一个作为默认选项
_OptionList.Add(_ValidOptions[0]);
}
else if (_OptionList.Count == 0)
{
// 如果确实没有任何有效选项,则添加一个兜底选项(可以是一个最常用或最不影响整体的瓦片)
// 假设第一个瓦片是兜底选项
_OptionList.Add(_TileObjects[0]);
}
Debug.Log("没有有效选项,回退操作");
}
}
}
脚本搭载
实现 效果
波函数坍缩算法示例工程: 示例工程
可扩展效果
希望这些信息能够进一步满足您对Untiy 波函数坍缩算法 随机地图生成的需求。
如果您有任何特定的问题或需要更深入的讨论,请随时提出。
路漫漫其修远兮,与君共勉。