哪个做网站公司,河南省十大互联网企业,做网站一定需要主机吗,酒泉网站建设费用最终效果 文章目录最终效果前言1、理解程序生成的核心概念2、种子值的核心作用3、程序生成的实际应用4、主流程序生成技术概览5、选择合适的技术实战1、素材2、生成一面墙变换矩阵数据3、渲染墙壁4、加点随机不同的墙壁效果5、绘制四面墙壁4、在四个角落生成支柱5、生成地板6、…最终效果 文章目录最终效果前言1、理解程序生成的核心概念2、种子值的核心作用3、程序生成的实际应用4、主流程序生成技术概览5、选择合适的技术实战1、素材2、生成一面墙变换矩阵数据3、渲染墙壁4、加点随机不同的墙壁效果5、绘制四面墙壁4、在四个角落生成支柱5、生成地板6、墙壁接缝生成支柱7、为每个实例生成GameObject8、生成碰撞体9、放置随机物品10、设置随机种子源码参考专栏推荐完结前言
1、理解程序生成的核心概念
程序生成(Procedural Generation)常被误解为随机生成但二者有本质区别。程序生成是通过预设规则系统来创造内容虽然结果可能看似不可预测但实际上是确定性的。这种技术让我们能够精确控制输出内容的类型和特征。
2、种子值的核心作用
种子(Seed)是程序生成中的关键概念 作为随机数生成的起点值 保证生成结果的可重复性 相同的种子必然产生相同的输出序列
实际上计算机中的随机函数是伪随机的确定性算法通过对种子值进行数学变换来产生看似随机的序列。这种设计确保了结果的可靠性。
如果使用相同的种子每次都会得到相同的随机值序列这对于创建可重复的输出很有用。
3、程序生成的实际应用
程序生成在内容创作中(特别是游戏开发)有着广泛应用其实现可分为两个层面 数据生成层 - 使用算法创造原始数据 内容呈现层 - 将数据转化为可视/可交互内容
4、主流程序生成技术概览
技术典型应用场景特点基于语法的生成建筑、城市生成使用规则系统定义结构L-Systems植物、树木生成递归应用规则生成复杂形态波函数坍缩地形、图案生成通过约束传播生成连贯结构细胞自动机地形、纹理生成基于简单规则产生复杂行为噪声函数自然地形、纹理产生有机的随机模式二元空间分区室内空间划分递归分割空间创建布局
5、选择合适的技术
没有放之四海而皆准的解决方案开发者应该
广泛了解各种生成技术将其视为工具箱中的不同工具通过经验积累培养技术选型能力
实战
1、素材
https://assetstore.unity.com/packages/3d/environments/dungeons/lite-dungeon-pack-low-poly-3d-art-by-gridness-242692
2、生成一面墙变换矩阵数据
//生成地牢函数
public class GenerateDungeon : MonoBehaviour
{[Header(房间尺寸(X为宽度Y为长度))]public Vector2 roomSize new Vector2(10, 10);[Header(墙壁生成相关)]public Mesh wallMesh; // 普通墙壁网格public Material wallMaterial; // 墙壁材质ListMatrix4x4 _wallMatricesN; // 普通墙壁变换矩阵列表void Start(){CreateWalls();}// 创建墙面物体的函数void CreateWalls(){// 初始化存储墙面变换矩阵的列表_wallMatricesN new ListMatrix4x4();// 计算需要的墙面数量房间宽度除以单面墙宽度至少1面墙int wallCount Mathf.Max(1, (int)(roomSize.x / wallMesh.bounds.size.x));// 计算每面墙的缩放比例使墙面正好填满房间宽度float scale (roomSize.x / wallCount) / wallMesh.bounds.size.x;// 循环生成每面墙的变换矩阵for (int i 0; i wallCount; i){// 计算墙面位置// 1. 从房间左边界开始(-roomSize.x/2)// 2. 加上半面墙宽度(wallSize.x*scale/2)// 3. 加上当前墙的偏移量(i*scale*wallSize.x)// Y轴位置为房间上边界(roomSize.y/2)Vector3 position transform.position new Vector3(-roomSize.x / 2 wallMesh.bounds.size.x * scale / 2 i * scale * wallMesh.bounds.size.x,0,roomSize.y / 2);// 使用父物体的旋转Quaternion rotation transform.rotation;// 设置缩放X轴按计算比例缩放Y和Z轴保持原大小Vector3 scaleVec new Vector3(scale, 1, 1);// 创建变换矩阵(位置/旋转/缩放)Matrix4x4 matrix Matrix4x4.TRS(position, rotation, scaleVec);// 将矩阵添加到列表_wallMatricesN.Add(matrix);}}
}3、渲染墙壁
在 Unity 中Graphics.DrawMeshInstanced 是一种高性能的渲染技术特别适用于需要绘制大量相同或相似网格如地牢墙壁、植被、子弹等的场景。
void Update()
{RenderWalls();
}// 渲染墙面物体的函数
void RenderWalls()
{// 渲染普通墙体if (_wallMatricesN ! null _wallMatricesN.Count 0){Graphics.DrawMeshInstanced(wallMesh,0,wallMaterial,_wallMatricesN.ToArray(),_wallMatricesN.Count);}
}效果
4、加点随机不同的墙壁效果
public Mesh wallMeshB; // 特殊墙壁网格
ListMatrix4x4 _wallMatricesNB; // 特殊墙壁变换矩阵列表void CreateWalls()
{// 初始化列表用于存储不同类型墙壁的变换矩阵_wallMatricesN new ListMatrix4x4(); // 普通墙壁_wallMatricesNB new ListMatrix4x4(); // 类型B墙壁// 计算沿x轴需要放置的墙壁数量至少为1面墙int wallCount Mathf.Max(1, (int)(roomSize.x / wallMesh.bounds.size.x));// 计算墙壁的缩放比例使墙壁正好填满房间的x轴长度float scale (roomSize.x / wallCount) / wallMesh.bounds.size.x;// 循环创建每一面墙壁for (int i 0; i wallCount; i){// 计算墙壁位置从房间左侧开始等间距排列var t transform.position new Vector3(-roomSize.x / 2 wallMesh.bounds.size.x * scale / 2 i * scale * wallMesh.bounds.size.x, // x位置0, // y位置-roomSize.y / 2); // z位置// 使用当前物体的旋转var r transform.rotation;// 设置缩放x轴根据计算的比例缩放y和z保持原样var s new Vector3(scale, 1, 1);// 创建变换矩阵位置、旋转、缩放var mat Matrix4x4.TRS(t, r, s);// 随机决定墙壁类型0或1var rand Random.Range(0, 2);// 根据随机数将墙壁分配到不同的列表if (rand 1){_wallMatricesN.Add(mat); // 类型0 - 普通墙壁}else if (rand 2){_wallMatricesNB.Add(mat); // 类型1 - B类墙壁}}
}// 渲染墙面物体的函数
void RenderWalls()
{// 。。。// 渲染B类型墙体if (_wallMatricesNB ! null _wallMatricesNB.Count 0){Graphics.DrawMeshInstanced(wallMeshB,0,wallMaterial,_wallMatricesNB.ToArray(),_wallMatricesNB.Count);}
}效果
5、绘制四面墙壁
每面墙壁的生成非常类似我们可以把生成单面墙壁进行封装
// 创建所有墙壁
void CreateWalls()
{_wallMatricesN new ListMatrix4x4();_wallMatricesNB new ListMatrix4x4();// 生成四条边的墙体CreateWallEdge(Vector3.forward, roomSize.y / 2, false); // 北墙CreateWallEdge(Vector3.back, roomSize.y / 2, false); // 南墙CreateWallEdge(Vector3.right, roomSize.x / 2, true); // 东墙CreateWallEdge(Vector3.left, roomSize.x / 2, true); // 西墙
}// 创建单边墙体
void CreateWallEdge(Vector3 direction, float offset, bool isVertical)
{// 确定墙体方向Vector3 axis isVertical ? Vector3.up : Vector3.zero;float rotationAngle isVertical ? 90f : 0f;Vector2 size isVertical ? new Vector2(roomSize.y, 0) : new Vector2(roomSize.x, 0);// 计算墙体数量和缩放比例int wallCount Mathf.Max(1, (int)(size.x / wallMesh.bounds.size.x));float scale (size.x / wallCount) / wallMesh.bounds.size.x;// 创建该边的所有墙体for (int i 0; i wallCount; i){// 计算墙体位置Vector3 position transform.position direction * offset;if (isVertical){position Vector3.forward * (-size.x / 2 wallMesh.bounds.size.x * scale / 2 i * scale * wallMesh.bounds.size.x);}else{position Vector3.right * (-size.x / 2 wallMesh.bounds.size.x * scale / 2 i * scale * wallMesh.bounds.size.x);}// 设置旋转和缩放Quaternion rotation transform.rotation * Quaternion.AngleAxis(rotationAngle, axis);Vector3 scaleVec new Vector3(scale, 1, 1);// 创建变换矩阵Matrix4x4 matrix Matrix4x4.TRS(position, rotation, scaleVec);// 随机分配墙体类型if (Random.Range(0, 2) 0)_wallMatricesN.Add(matrix);else_wallMatricesNB.Add(matrix);}
}效果
4、在四个角落生成支柱
#region 支柱生成相关
[Header(支柱生成相关)]
public Mesh pillarMesh; // 支柱网格
public Material pillarMaterial; // 支柱材质
ListMatrix4x4 _pillarMatrices; // 支柱变换矩阵列表//在四个角落添加支柱
void CreatePillars()
{_pillarMatrices new ListMatrix4x4();// 定义房间四个角落的位置Vector3[] corners new Vector3[]{new Vector3(-roomSize.x/2, 0, -roomSize.y/2), // 西南角new Vector3(-roomSize.x/2, 0, roomSize.y/2), // 西北角new Vector3(roomSize.x/2, 0, -roomSize.y/2), // 东南角new Vector3(roomSize.x/2, 0, roomSize.y/2) // 东北角};// 为每个角落创建支柱foreach (Vector3 corner in corners){Vector3 position transform.position corner;Quaternion rotation transform.rotation;Vector3 scale Vector3.one; // 支柱默认缩放Matrix4x4 matrix Matrix4x4.TRS(position, rotation, scale);_pillarMatrices.Add(matrix);}
}// 渲染支柱
void RenderPillars()
{if (_pillarMatrices ! null _pillarMatrices.Count 0){Graphics.DrawMeshInstanced(pillarMesh,0,pillarMaterial,_pillarMatrices.ToArray(),_pillarMatrices.Count);}
}#endregion效果
5、生成地板
生成地板可以使用两种模式——缩放方式和平铺方式具体选择其一即可
#region 地板生成相关
[Header(地板生成相关)]
public Mesh floorMesh; // 地板网格
public Material floorMaterial; // 地板材质
ListMatrix4x4 _floorMatrices; // 地板变换矩阵列表// 创建地板缩放方式
void createFloorScale()
{_floorMatrices new ListMatrix4x4();// 计算地板位置房间中心Vector3 position transform.position;Quaternion rotation transform.rotation;// 根据房间尺寸缩放地板Vector3 scale new Vector3(roomSize.x, 1, roomSize.y);Matrix4x4 matrix Matrix4x4.TRS(position, rotation, scale);_floorMatrices.Add(matrix);
}// 创建地板平铺方式
void CreateFloorTile()
{_floorMatrices new ListMatrix4x4();// 获取地板网格的原始尺寸float floorWidth floorMesh.bounds.size.x;float floorLength floorMesh.bounds.size.z;// 计算X轴和Z轴需要的地板数量int floorCountX Mathf.Max(1, Mathf.CeilToInt(roomSize.x / floorWidth));int floorCountZ Mathf.Max(1, Mathf.CeilToInt(roomSize.y / floorLength)); // 注意RoomSize.y对应的是Z轴长度// 计算实际缩放比例使地板正好铺满房间float scaleX (roomSize.x / floorCountX) / floorWidth;float scaleZ (roomSize.y / floorCountZ) / floorLength;// 起始位置左下角Vector3 startPos transform.position new Vector3(-roomSize.x / 2 floorWidth * scaleX / 2, 0, -roomSize.y / 2 floorLength * scaleZ / 2);// 平铺地板for (int x 0; x floorCountX; x){for (int z 0; z floorCountZ; z){// 计算每块地板的位置Vector3 position startPos new Vector3(x * floorWidth * scaleX,0,z * floorLength * scaleZ);// 保持默认旋转Quaternion rotation transform.rotation;// 设置缩放保持Y轴不变Vector3 scale new Vector3(scaleX, 1, scaleZ);// 创建变换矩阵Matrix4x4 matrix Matrix4x4.TRS(position, rotation, scale);_floorMatrices.Add(matrix);}}
}// 渲染地板
void RenderFloor()
{if (_floorMatrices ! null _floorMatrices.Count 0){Graphics.DrawMeshInstanced(floorMesh,0,floorMaterial,_floorMatrices.ToArray(),_floorMatrices.Count);}
}
#endregion效果
6、墙壁接缝生成支柱
#region 墙壁接缝支柱
[Header(墙壁接缝支柱)]
public Mesh junctionPillarMesh; // 接缝支柱网格
public Material junctionPillarMaterial; // 接缝支柱材质
ListMatrix4x4 _junctionPillarMatrices; // 接缝支柱变换矩阵void AddWallJunctionPillars()
{if (junctionPillarMesh null) return;_junctionPillarMatrices new ListMatrix4x4();// 获取墙体网格尺寸假设所有墙体网格尺寸相同float wallWidth wallMesh.bounds.size.x;// 计算水平和垂直墙体的数量int horizontalWallCount Mathf.Max(1, (int)(roomSize.x / wallWidth));int verticalWallCount Mathf.Max(1, (int)(roomSize.y / wallWidth));// 计算墙体缩放比例float hScale (roomSize.x / horizontalWallCount) / wallWidth;float vScale (roomSize.y / verticalWallCount) / wallWidth;// 添加南北墙的接缝支柱东西走向的墙for (int i 1; i horizontalWallCount; i){// 北墙接缝float xPos -roomSize.x / 2 i * wallWidth * hScale;AddPillar(transform.position new Vector3(xPos, 0, roomSize.y / 2));// 南墙接缝AddPillar(transform.position new Vector3(xPos, 0, -roomSize.y / 2));}// 添加东西墙的接缝支柱南北走向的墙for (int i 1; i verticalWallCount; i){// 东墙接缝float zPos -roomSize.y / 2 i * wallWidth * vScale;AddPillar(transform.position new Vector3(roomSize.x / 2, 0, zPos));// 西墙接缝AddPillar(transform.position new Vector3(-roomSize.x / 2, 0, zPos));}
}// 在指定位置添加支柱
void AddPillar(Vector3 position, float scaleMultiplier 1f)
{Quaternion rotation transform.rotation;Vector3 scale Vector3.one * scaleMultiplier;_junctionPillarMatrices.Add(Matrix4x4.TRS(position, rotation, scale));
}// 渲染接缝支柱
void RenderJunctionPillars()
{if (_junctionPillarMatrices ! null _junctionPillarMatrices.Count 0){Graphics.DrawMeshInstanced(junctionPillarMesh,0,junctionPillarMaterial,_junctionPillarMatrices.ToArray(),_junctionPillarMatrices.Count);}
}
#endregion效果这里我没有特殊支柱的素材就还有用四个角落的支柱代替好了
7、为每个实例生成GameObject
这个一般只在需要的使用如果你想更灵活的操作自己的对象比如实现可破坏的墙壁效果等等这里我以墙体为例其他大家自行扩展即可方法都一样。
#region 为每个实例生成GameObject
// 修改后的创建方法示例以墙体为例
ListGameObject _wallInstances new ListGameObject();void CreateWallsWithColliders()
{// 销毁旧实例如果存在foreach (var wall in _wallInstances) Destroy(wall);_wallInstances.Clear();// 生成带碰撞体的墙体foreach (var matrix in _wallMatricesN){GameObject wall new GameObject(WallInstance);wall.transform.SetPositionAndRotation(matrix.GetPosition(), matrix.rotation);wall.transform.localScale matrix.lossyScale;// 添加网格组件MeshFilter filter wall.AddComponentMeshFilter();filter.mesh wallMesh;MeshRenderer renderer wall.AddComponentMeshRenderer();renderer.material wallMaterial;// 添加碰撞体根据需求选择类型MeshCollider collider wall.AddComponentMeshCollider();collider.sharedMesh wallMesh;collider.convex false; // 对于静态墙体设为false_wallInstances.Add(wall);}
}
#endregion效果
8、生成碰撞体
#region 碰撞体生成
private GameObject _collidersParent;void CreateCombinedCollider(ListMatrix4x4 matrices, Mesh mesh)
{if (mesh null) return;if (_collidersParent null){_collidersParent new GameObject(Colliders);_collidersParent.transform.SetParent(transform);}// 合并所有网格CombineInstance[] combines new CombineInstance[matrices.Count];for (int i 0; i matrices.Count; i){combines[i].mesh mesh;combines[i].transform matrices[i];}Mesh combinedMesh new Mesh();combinedMesh.CombineMeshes(combines);// 创建碰撞体父物体GameObject colliderGameObject new GameObject(mesh.name Colliders);colliderGameObject.transform.SetParent(_collidersParent.transform);MeshCollider collider colliderGameObject.AddComponentMeshCollider();collider.sharedMesh combinedMesh;
}
#endregion效果
9、放置随机物品
放置随机物品就比较复杂了不过我加了很多详细的注释大家可以查看理解。
新增道具配置类
using UnityEngine;// 道具配置类
[System.Serializable]
public class DungeonProp
{public GameObject prefab; // 预制体public enum PositionType { Wall, Corner, Middle, Anywhere }// 放置位置类型public PositionType positionType; // 放置位置类型[Range(0, 1)] public float spawnProbability; // 生成概率0~1public int minCount; // 最小数量public int maxCount; // 最大数量public Vector3 offset; //放置偏移量public bool randomRotation; // 是否随机旋转
}编写道具放置系统逻辑我这里使用了OBB (SAT) 有向包围盒精准的检测物品是否相交且保持良好的性能。 不了解什么是OBB的可以参考【unity知识】unity使用AABB轴对齐包围盒和OBB定向包围盒优化碰撞检测 #region 道具放置系统
[Header(道具放置系统)]
public DungeonProp[] dungeonProps; // 可放置的道具配置
private float _wallThickness; // 墙体一半厚度
private GameObject _propsParent; //父物体// 放置道具
void PlaceProps()
{if (dungeonProps null || dungeonProps.Length 0) return;_wallThickness wallMesh.bounds.extents.z;// 计算实际可用区域考虑墙体厚度float usableWidth roomSize.x - 2 * _wallThickness;float usableHeight roomSize.y - 2 * _wallThickness;// 计算房间边界考虑墙体厚度float minX transform.position.x - usableWidth / 2;float maxX transform.position.x usableWidth / 2;float minZ transform.position.z - usableHeight / 2;float maxZ transform.position.z usableHeight / 2;// 创建道具父物体if (_propsParent null){_propsParent new GameObject(PropsParent);_propsParent.transform.SetParent(transform);} // 存储已放置物体的OBB信息ListOBB placedOBBs new ListOBB();foreach (var prop in dungeonProps){// 计算实际要放置的数量int count Random.Range(prop.minCount, prop.maxCount 1);for (int i 0; i count; i){// 根据概率决定是否生成if (Random.value prop.spawnProbability) continue;Vector3 position Vector3.zero;Quaternion rotation Quaternion.identity;Vector3 halfExtents Vector3.zero;// 根据位置类型确定位置和朝向switch (prop.positionType){case DungeonProp.PositionType.Wall:if (!TryFindWallPosition(prop.prefab, minX, maxX, minZ, maxZ,out position, out rotation, out halfExtents))continue;break;case DungeonProp.PositionType.Corner:if (!TryFindCornerPosition(prop.prefab, minX, maxX, minZ, maxZ,out position, out rotation, out halfExtents))continue;break;case DungeonProp.PositionType.Middle:if (!TryFindMiddlePosition(prop.prefab, minX, maxX, minZ, maxZ,out position, out rotation, out halfExtents))continue;break;case DungeonProp.PositionType.Anywhere:if (!TryFindAnyPosition(prop.prefab, minX, maxX, minZ, maxZ,out position, out rotation, out halfExtents))continue;break;}// 应用随机旋转if (prop.randomRotation){rotation Quaternion.Euler(0, Random.Range(0, 360), 0);}// 检查碰撞并放置道具if (!CheckOverlap(position, halfExtents, rotation, placedOBBs)){GameObject propInstance Instantiate(prop.prefab, position prop.offset, rotation);propInstance.transform.SetParent(_propsParent.transform);placedOBBs.Add(new OBB(){center position,extents halfExtents,rotation rotation});}}}
}// 尝试在墙边放置道具
bool TryFindWallPosition(GameObject prefab, float minX, float maxX, float minZ, float maxZ,out Vector3 position, out Quaternion rotation, out Vector3 halfExtents)
{position Vector3.zero;rotation Quaternion.identity;halfExtents Vector3.zero;// 获取预制体大小Bounds bounds GetPrefabBounds(prefab);if (bounds.size Vector3.zero) return false;//获取包围盒Bounds的半尺寸halfExtents bounds.extents;// 随机选择一面墙int wallIndex Random.Range(0, 4);switch (wallIndex){case 0: // 北墙 (z最大)position new Vector3(Random.Range(minX halfExtents.x, maxX - halfExtents.x),0,maxZ - halfExtents.z);rotation Quaternion.Euler(0, 180, 0); // 面朝南break;case 1: // 南墙 (z最小)position new Vector3(Random.Range(minX halfExtents.x, maxX - halfExtents.x),0,minZ halfExtents.z);rotation Quaternion.identity; // 面朝北break;case 2: // 东墙 (x最大)position new Vector3(maxX - halfExtents.z,0,Random.Range(minZ halfExtents.x, maxZ - halfExtents.x));rotation Quaternion.Euler(0, 270, 0); // 面朝西break;case 3: // 西墙 (x最小)position new Vector3(minX halfExtents.z,0,Random.Range(minZ halfExtents.x, maxZ - halfExtents.x));rotation Quaternion.Euler(0, 90, 0); // 面朝东break;}return true;
}// 尝试在角落放置道具
bool TryFindCornerPosition(GameObject prefab, float minX, float maxX, float minZ, float maxZ,out Vector3 position, out Quaternion rotation, out Vector3 halfExtents)
{position Vector3.zero;rotation Quaternion.identity;halfExtents Vector3.zero;// 获取预制体大小Bounds bounds GetPrefabBounds(prefab);if (bounds.size Vector3.zero) return false;//获取包围盒Bounds的半尺寸halfExtents bounds.extents;// 随机选择一个墙角int cornerIndex Random.Range(0, 4);float cornerOffset _wallThickness Mathf.Max(halfExtents.x, halfExtents.z);switch (cornerIndex){case 0: // 西北角position new Vector3(minX cornerOffset,0,maxZ - cornerOffset);rotation Quaternion.Euler(0, 225, 0); // 面向东南break;case 1: // 东北角position new Vector3(maxX - cornerOffset,0,maxZ - cornerOffset);rotation Quaternion.Euler(0, 315, 0); // 面向西南break;case 2: // 西南角position new Vector3(minX cornerOffset,0,minZ cornerOffset);rotation Quaternion.Euler(0, 135, 0); // 面向东北break;case 3: // 东南角position new Vector3(maxX - cornerOffset,0,minZ cornerOffset);rotation Quaternion.Euler(0, 45, 0); // 面向西北break;}return true;
}// 尝试在中间区域放置道具
bool TryFindMiddlePosition(GameObject prefab, float minX, float maxX, float minZ, float maxZ,out Vector3 position, out Quaternion rotation, out Vector3 halfExtents)
{position Vector3.zero;rotation Quaternion.identity;halfExtents Vector3.zero;// 获取预制体大小Bounds bounds GetPrefabBounds(prefab);if (bounds.size Vector3.zero) return false;//获取包围盒Bounds的半尺寸halfExtents bounds.extents;// 中间区域避开墙边float safeMargin Mathf.Max(halfExtents.x, halfExtents.z) * 2;position new Vector3(Random.Range(minX safeMargin, maxX - safeMargin),0,Random.Range(minZ safeMargin, maxZ - safeMargin));return true;
}// 尝试在任意位置放置道具
bool TryFindAnyPosition(GameObject prefab, float minX, float maxX, float minZ, float maxZ,out Vector3 position, out Quaternion rotation, out Vector3 halfExtents)
{// 50%概率放在墙边25%概率放在墙角25%概率放在中间float rand Random.value;if (rand 0.5f)return TryFindWallPosition(prefab, minX, maxX, minZ, maxZ, out position, out rotation, out halfExtents);else if (rand 0.75f)return TryFindCornerPosition(prefab, minX, maxX, minZ, maxZ, out position, out rotation, out halfExtents);elsereturn TryFindMiddlePosition(prefab, minX, maxX, minZ, maxZ, out position, out rotation, out halfExtents);
}// 获取预制体的包围盒
Bounds GetPrefabBounds(GameObject prefab)
{Renderer renderer prefab.GetComponentInChildrenRenderer();if (renderer ! null) return renderer.bounds;// 如果预制体没有渲染器尝试使用碰撞器Collider collider prefab.GetComponentInChildrenCollider();if (collider ! null) return collider.bounds;// 默认大小return new Bounds(Vector3.zero, Vector3.one * 0.5f);
}/// summary
/// 检查新物体是否与已放置物体发生OBB重叠
/// /summary
/// param nameposition新物体的中心位置/param
/// param namehalfExtents新物体的半尺寸/param
/// param namerotation新物体的旋转/param
/// param nameexistingOBBs已放置物体的OBB列表/param
/// returnstrue表示有重叠false表示无重叠/returns
bool CheckOverlap(Vector3 position, Vector3 halfExtents, Quaternion rotation, ListOBB existingOBBs)
{// 创建新物体的OBBOBB newOBB new OBB(){center position, // 设置中心点extents halfExtents, // 设置半尺寸rotation rotation // 设置旋转};// 遍历所有已放置物体的OBBforeach (var obb in existingOBBs){// 如果与任一已放置物体相交返回trueif (newOBB.Intersects(obb))return true;}// 没有发现重叠return false;
}
#endregion
}
配置数据 效果实现在不同位置按概率生成不同物品
10、设置随机种子
Random.InitState 是 Unity 引擎中用于初始化随机数生成器的方法它的作用是设定随机数生成的种子Seed从而控制随机序列的起始点。它的核心作用是 确定性随机使用相同的种子时Random 产生的随机数序列会完全一致。 Random.InitState(123); // 初始化种子为123
Debug.Log(Random.Range(0, 100)); // 固定输出某个值如42每次运行程序只要种子是 123第一个 Random.Range(0, 100) 必定返回相同的值。 取消真正的随机性默认情况下Unity 使用系统时间作为种子结果不可预测。而 InitState 会覆盖这一行为使随机结果可重现。
所以我们可以给我们的项目添加随机种子功能
[Header(随机种子)]
[SerializeField] private int _seed; // 随机种子
[SerializeField] private bool _useSeed; // 是否使用指定种子void Start()
{// 初始化随机种子if (_useSeed){Random.InitState(_seed); // 使用指定的种子}else{int randomSeed Random.Range(1, 1000000); // 生成随机种子Random.InitState(randomSeed);Debug.Log(randomSeed); // 输出种子以便重现}//...
}我们使用相同的种子一直会生成相同的地牢
源码
https://gitee.com/unity_data/unity6-urpgeneration-dungeon
参考
https://www.youtube.com/watch?vPhLcNhK9aro 专栏推荐
地址【unity游戏开发入门到精通——C#篇】【unity游戏开发入门到精通——unity通用篇】【unity游戏开发入门到精通——unity3D篇】【unity游戏开发入门到精通——unity2D篇】【unity实战】【制作100个Unity游戏】【推荐100个unity插件】【实现100个unity特效】【unity框架/工具集开发】【unity游戏开发——模型篇】【unity游戏开发——InputSystem】【unity游戏开发——Animator动画】【unity游戏开发——UGUI】【unity游戏开发——联网篇】【unity游戏开发——优化篇】【unity游戏开发——shader篇】【unity游戏开发——编辑器扩展】【unity游戏开发——热更新】【unity游戏开发——网络】
完结
好了我是向宇博客地址https://xiangyu.blog.csdn.net如果学习过程中遇到任何问题也欢迎你评论私信找我。
赠人玫瑰手有余香如果文章内容对你有所帮助请不要吝啬你的点赞评论和关注你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法也欢迎评论私信告诉我哦