每个程序生成级别的无限循环 (C# Unity3D)
Infinite loop for each procedurally generated level (C# Unity3D)
我正在尝试在我的 Unity 项目中随机位置生成固定数量的建筑物。但是,我得到了一个无限循环。
这是我的代码 (C#):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GenerateMap : MonoBehaviour {
public GameObject plane;
public LayerMask unwalkableMask;
Node[,] Map;
public Vector2 gridWorldSize;
GameObject thisBuilding;
public float nodeRadius;
float nodeDiameter;
int gridSizeX, gridSizeY;
int scale;
public int numBuildings = 5;
public int numPrefabs;
public List<Vector3> positions = new List<Vector3> ();
public List<GameObject> buildingPrefabs = new List<GameObject>();
void Awake(){
plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
scale = 15; // scaling the plane gives an 5*scale x 5*scale (x-axis x z-axis) plane, set to 50
plane.transform.localScale = new Vector3 (scale, 1, scale); //scales only in x and z dimensions
}
// Use this for initialization
void Start () {
//GameObject building1 = Resources.Load("Buildings/building1") as GameObject;
//GameObject building2 = (GameObject)Resources.Load ("Buildings/building2");
//GameObject building3 = (GameObject)Resources.Load ("Buildings/building3");
nodeDiameter = nodeRadius*2;
gridSizeX = Mathf.RoundToInt(gridWorldSize.x/nodeDiameter);
gridSizeY = Mathf.RoundToInt(gridWorldSize.y/nodeDiameter);
Generate();
}
//int i = 0;
void Generate(){
for(int i =0; i<numBuildings; i++){
//while(i < numBuildings){
CreateGrid();
List<Node> unwalkables = getUnwalkables();
thisBuilding =(GameObject)InstantiatePrefab(); i++;
CreateGrid();
List<Node> unwalkables2 = getUnwalkables(thisBuilding);
foreach(Node n in unwalkables){
//Debug.Log(n.worldPosition);
bool breaking = false;
foreach(Node m in unwalkables2){
if(n.worldPosition==m.worldPosition){
Destroy(thisBuilding);
i=i-1;
Debug.Log("i after fail " + i);
breaking = true;
break;
}
if(breaking)
break;
}
}
}
}
Object InstantiatePrefab() {
int number = Random.Range (0, numPrefabs);
Vector3 position = new Vector3 (Random.Range (-scale*5, scale*5), 0, Random.Range (-scale*5, scale*5)); //random position in the x,z-plane
positions.Add (position);
position.y = buildingPrefabs [number].transform.position.y; //make sure they spawn on top of the plane instead of y=0 w.r.t. their pivot point
Object building;
if (number != 2) {
building = Instantiate (buildingPrefabs [number], position, Quaternion.Euler (-90f, 0f, 0f));
} else {
building = Instantiate (buildingPrefabs [number], position, Quaternion.identity);
}
return building;
}
void CreateGrid(){
Map = new Node[gridSizeX, gridSizeY];
Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x/2 - Vector3.forward * gridWorldSize.y/2;
for(int x=0; x<gridSizeX; x++){
for(int y=0; y<gridSizeX; y++){
Vector3 worldPoint = worldBottomLeft + Vector3.right * (x*nodeDiameter + nodeRadius) + Vector3.forward * (y*nodeDiameter+ nodeRadius);
bool walkable = !(Physics.CheckSphere(worldPoint, nodeRadius, unwalkableMask));
Map[x,y] = new Node(walkable, worldPoint, x, y);
}
}
}
void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(gridWorldSize.x, 1, gridWorldSize.y));
if (Map != null)
{
foreach (Node n in Map)
{
Gizmos.color = (n.walkable)?Color.white:Color.red;
Gizmos.DrawCube(n.worldPosition, new Vector3(nodeDiameter-.1f, nodeDiameter*0.5f, nodeDiameter-.1f));
//Vector3.one * (nodeDiameter-.1f));
}
}
}
List<Node> getUnwalkables(){
List<Node> unwalk = new List<Node>();
foreach(Node n in Map){
if(!n.walkable)
unwalk.Add(n);
}
return unwalk;
}
List<Node> getUnwalkables(GameObject obj){
List<Node> unwalk = new List<Node>();
int borderWidth = 8; //x-dir
int borderHeight = 7; //z-dir
float RightBorder = obj.transform.position.x+nodeRadius+borderWidth*nodeDiameter;
float LeftBorder = obj.transform.position.x-nodeRadius-borderWidth*nodeDiameter;
float TopBorder = obj.transform.position.z+nodeRadius+borderHeight*nodeDiameter;
float DownBorder = obj.transform.position.z-nodeRadius-borderHeight*nodeDiameter;
//Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x/2 - Vector3.forward * gridWorldSize.y/2;
foreach(Node n in Map){
if(n.worldPosition.x<RightBorder && n.worldPosition.x>LeftBorder
&& n.worldPosition.z>DownBorder && n.worldPosition.z<TopBorder)
unwalk.Add(n);
}
return unwalk;
}
}
这个方法好像出错了:
void Generate(){
for(int i =0; i<numBuildings; i++){
//while(i < numBuildings){
CreateGrid();
List<Node> unwalkables = getUnwalkables();
thisBuilding =(GameObject)InstantiatePrefab(); i++;
CreateGrid();
List<Node> unwalkables2 = getUnwalkables(thisBuilding);
foreach(Node n in unwalkables){
//Debug.Log(n.worldPosition);
bool breaking = false;
foreach(Node m in unwalkables2){
if(n.worldPosition==m.worldPosition){
Destroy(thisBuilding);
i=i-1;
Debug.Log("i after fail " + i);
breaking = true;
break;
}
if(breaking)
break;
}
}
}
}
如果我删除行:i=i-1;我没有得到无限循环,但我最终会拥有比预期更少的建筑物。此行导致 for 循环再重复一次,因此它将始终生成至少 'numBuildings' 数量的建筑物。
我在 'foreach' 循环中添加了中断语句,这样 "i" 就不会在多次满足条件时继续减少。但这似乎不起作用,一旦 numBuildings 大约为 5 或更多,它就会创建一个无限循环。发生这种情况时,我无法再退出,除非强制结束任务。
有谁知道我做错了什么,或者是否有更好的解决方案来始终生成多个建筑物?
如有必要,我随时准备回答有关 code/scene 的任何问题。
事情是这样的:Destroy
功能无法立即运行。据我了解你的代码(我没有深入了解所有细节),一般来说它会做这样的事情:
- 创建一些建筑
- 找到所有 'Unwalkable' 的建筑物并摧毁它们(通过测试建筑物周围的自由区域来完成)
- 重复直到您拥有所需的建筑数量
无限循环的发生是因为 Destroy
实际上并没有破坏任何东西——它只是在帧的末尾标记要破坏的对象。问题是整个循环是 运行ning 在单帧中,所以真正的破坏永远不会发生。这样 'Unwalkables' 的数量就会随着每次迭代而不断增长。
有很多 快速方法 可以解决这个问题,还有 好的方法 可以解决这个问题,选择权在你.
快速方法是:
使用DestroyImmediate
:http://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html。只要确保你知道自己在做什么。
将循环变成运行跨多个帧的协程,这样Destroy
就有时间实际运行,你会看到你的场景实时生成
good的方法是想一个算法的替代实现。不要放置建筑物然后摧毁位置不佳的建筑物,而是尝试为建筑物找到一个好的开始位置,然后才放置它。最好这样做,因为即使您克服了 Destroy
函数的问题,您的算法仍然存在逻辑问题:如果您根本没有足够的 space 来建造所有建筑物怎么办?您不知道何时停止当前的实施。你只会在无限循环中不断生成和破坏建筑物。
我正在尝试在我的 Unity 项目中随机位置生成固定数量的建筑物。但是,我得到了一个无限循环。
这是我的代码 (C#):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GenerateMap : MonoBehaviour {
public GameObject plane;
public LayerMask unwalkableMask;
Node[,] Map;
public Vector2 gridWorldSize;
GameObject thisBuilding;
public float nodeRadius;
float nodeDiameter;
int gridSizeX, gridSizeY;
int scale;
public int numBuildings = 5;
public int numPrefabs;
public List<Vector3> positions = new List<Vector3> ();
public List<GameObject> buildingPrefabs = new List<GameObject>();
void Awake(){
plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
scale = 15; // scaling the plane gives an 5*scale x 5*scale (x-axis x z-axis) plane, set to 50
plane.transform.localScale = new Vector3 (scale, 1, scale); //scales only in x and z dimensions
}
// Use this for initialization
void Start () {
//GameObject building1 = Resources.Load("Buildings/building1") as GameObject;
//GameObject building2 = (GameObject)Resources.Load ("Buildings/building2");
//GameObject building3 = (GameObject)Resources.Load ("Buildings/building3");
nodeDiameter = nodeRadius*2;
gridSizeX = Mathf.RoundToInt(gridWorldSize.x/nodeDiameter);
gridSizeY = Mathf.RoundToInt(gridWorldSize.y/nodeDiameter);
Generate();
}
//int i = 0;
void Generate(){
for(int i =0; i<numBuildings; i++){
//while(i < numBuildings){
CreateGrid();
List<Node> unwalkables = getUnwalkables();
thisBuilding =(GameObject)InstantiatePrefab(); i++;
CreateGrid();
List<Node> unwalkables2 = getUnwalkables(thisBuilding);
foreach(Node n in unwalkables){
//Debug.Log(n.worldPosition);
bool breaking = false;
foreach(Node m in unwalkables2){
if(n.worldPosition==m.worldPosition){
Destroy(thisBuilding);
i=i-1;
Debug.Log("i after fail " + i);
breaking = true;
break;
}
if(breaking)
break;
}
}
}
}
Object InstantiatePrefab() {
int number = Random.Range (0, numPrefabs);
Vector3 position = new Vector3 (Random.Range (-scale*5, scale*5), 0, Random.Range (-scale*5, scale*5)); //random position in the x,z-plane
positions.Add (position);
position.y = buildingPrefabs [number].transform.position.y; //make sure they spawn on top of the plane instead of y=0 w.r.t. their pivot point
Object building;
if (number != 2) {
building = Instantiate (buildingPrefabs [number], position, Quaternion.Euler (-90f, 0f, 0f));
} else {
building = Instantiate (buildingPrefabs [number], position, Quaternion.identity);
}
return building;
}
void CreateGrid(){
Map = new Node[gridSizeX, gridSizeY];
Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x/2 - Vector3.forward * gridWorldSize.y/2;
for(int x=0; x<gridSizeX; x++){
for(int y=0; y<gridSizeX; y++){
Vector3 worldPoint = worldBottomLeft + Vector3.right * (x*nodeDiameter + nodeRadius) + Vector3.forward * (y*nodeDiameter+ nodeRadius);
bool walkable = !(Physics.CheckSphere(worldPoint, nodeRadius, unwalkableMask));
Map[x,y] = new Node(walkable, worldPoint, x, y);
}
}
}
void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(gridWorldSize.x, 1, gridWorldSize.y));
if (Map != null)
{
foreach (Node n in Map)
{
Gizmos.color = (n.walkable)?Color.white:Color.red;
Gizmos.DrawCube(n.worldPosition, new Vector3(nodeDiameter-.1f, nodeDiameter*0.5f, nodeDiameter-.1f));
//Vector3.one * (nodeDiameter-.1f));
}
}
}
List<Node> getUnwalkables(){
List<Node> unwalk = new List<Node>();
foreach(Node n in Map){
if(!n.walkable)
unwalk.Add(n);
}
return unwalk;
}
List<Node> getUnwalkables(GameObject obj){
List<Node> unwalk = new List<Node>();
int borderWidth = 8; //x-dir
int borderHeight = 7; //z-dir
float RightBorder = obj.transform.position.x+nodeRadius+borderWidth*nodeDiameter;
float LeftBorder = obj.transform.position.x-nodeRadius-borderWidth*nodeDiameter;
float TopBorder = obj.transform.position.z+nodeRadius+borderHeight*nodeDiameter;
float DownBorder = obj.transform.position.z-nodeRadius-borderHeight*nodeDiameter;
//Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x/2 - Vector3.forward * gridWorldSize.y/2;
foreach(Node n in Map){
if(n.worldPosition.x<RightBorder && n.worldPosition.x>LeftBorder
&& n.worldPosition.z>DownBorder && n.worldPosition.z<TopBorder)
unwalk.Add(n);
}
return unwalk;
}
}
这个方法好像出错了:
void Generate(){
for(int i =0; i<numBuildings; i++){
//while(i < numBuildings){
CreateGrid();
List<Node> unwalkables = getUnwalkables();
thisBuilding =(GameObject)InstantiatePrefab(); i++;
CreateGrid();
List<Node> unwalkables2 = getUnwalkables(thisBuilding);
foreach(Node n in unwalkables){
//Debug.Log(n.worldPosition);
bool breaking = false;
foreach(Node m in unwalkables2){
if(n.worldPosition==m.worldPosition){
Destroy(thisBuilding);
i=i-1;
Debug.Log("i after fail " + i);
breaking = true;
break;
}
if(breaking)
break;
}
}
}
}
如果我删除行:i=i-1;我没有得到无限循环,但我最终会拥有比预期更少的建筑物。此行导致 for 循环再重复一次,因此它将始终生成至少 'numBuildings' 数量的建筑物。
我在 'foreach' 循环中添加了中断语句,这样 "i" 就不会在多次满足条件时继续减少。但这似乎不起作用,一旦 numBuildings 大约为 5 或更多,它就会创建一个无限循环。发生这种情况时,我无法再退出,除非强制结束任务。
有谁知道我做错了什么,或者是否有更好的解决方案来始终生成多个建筑物?
如有必要,我随时准备回答有关 code/scene 的任何问题。
事情是这样的:Destroy
功能无法立即运行。据我了解你的代码(我没有深入了解所有细节),一般来说它会做这样的事情:
- 创建一些建筑
- 找到所有 'Unwalkable' 的建筑物并摧毁它们(通过测试建筑物周围的自由区域来完成)
- 重复直到您拥有所需的建筑数量
无限循环的发生是因为 Destroy
实际上并没有破坏任何东西——它只是在帧的末尾标记要破坏的对象。问题是整个循环是 运行ning 在单帧中,所以真正的破坏永远不会发生。这样 'Unwalkables' 的数量就会随着每次迭代而不断增长。
有很多 快速方法 可以解决这个问题,还有 好的方法 可以解决这个问题,选择权在你.
快速方法是:
使用
DestroyImmediate
:http://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html。只要确保你知道自己在做什么。将循环变成运行跨多个帧的协程,这样
Destroy
就有时间实际运行,你会看到你的场景实时生成
good的方法是想一个算法的替代实现。不要放置建筑物然后摧毁位置不佳的建筑物,而是尝试为建筑物找到一个好的开始位置,然后才放置它。最好这样做,因为即使您克服了 Destroy
函数的问题,您的算法仍然存在逻辑问题:如果您根本没有足够的 space 来建造所有建筑物怎么办?您不知道何时停止当前的实施。你只会在无限循环中不断生成和破坏建筑物。