游戏关卡意外地相互叠加
Game levels are unexpectedly generating on top of each other
我一直在研究程序生成关卡。
我已经创建了根据打开方式生成的盒子。
如果一个盒子有一个左边的重生点。左边的出生点会知道它需要在右边创建至少一扇门。
这似乎行得通,但出于某种原因,过了一会儿,房间开始堆叠在一起。即使我的代码不允许这样做?
这会不会是因为墙壁没有完全对称?因为我想要更广泛和不同类型的关卡,我认为只有重生点对齐就足够了吗?
这就是关卡启动 4 条不同路径的方式。
仍然很好
还不错
如您所见,起始层的所有入口都已被封锁。
在此之后,他们不断堆叠 op 彼此顶部,从而永无止境地生成关卡。
房间生成器
public class RoomSpawner : MonoBehaviour
{
public int openingDirection;
// 1 --> need bottom door
// 2 --> need top door
// 3 --> need left door
// 4 --> need right door
private RoomTemplates templates;
private int rand;
private bool spawned = false;
void Start(){
templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.5f);
}
void Spawn(){
if(spawned == false){
if(openingDirection == 1){
// Need to spawn a room with a BOTTOM door.
rand = Random.Range(0, templates.bottomRooms.Length);
Instantiate(templates.bottomRooms[rand], transform.position, templates.bottomRooms[rand].transform.rotation);
} else if(openingDirection == 2){
// Need to spawn a room with a TOP door.
rand = Random.Range(0, templates.topRooms.Length);
Instantiate(templates.topRooms[rand], transform.position, templates.topRooms[rand].transform.rotation);
} else if(openingDirection == 3){
// Need to spawn a room with a LEFT door.
rand = Random.Range(0, templates.leftRooms.Length);
Instantiate(templates.leftRooms[rand], transform.position, templates.leftRooms[rand].transform.rotation);
} else if(openingDirection == 4){
// Need to spawn a room with a RIGHT door.
rand = Random.Range(0, templates.rightRooms.Length);
Instantiate(templates.rightRooms[rand], transform.position, templates.rightRooms[rand].transform.rotation);
}
spawned = true;
}
void OnTriggerEnter2D(Collider2D other){
if(other.CompareTag("SpawnPoint")){
if(other.GetComponent<RoomSpawner>().spawned == false && spawned == false){
// spawns walls blocking off any opening !
Instantiate(templates.closedRoom, transform.position, Quaternion.identity);
Destroy(gameObject);
}
spawned = true;
}
}
}
}
毁灭者标签
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Destroyer : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other ){
Destroy(other.gameObject);
}
}
房间模板
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour
{
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public GameObject closedRoom;
public List<GameObject> rooms;
}
所以我想出了以下解决方案:
(限制 - 如果一个房间已经生成,它仍然有可能被其他生成房间包围,因此它的门被挡住)
具有适当的枚举标志类型
#if UNITY_EDITOR // exclude this from a build
using Unity.Editor;
#endif
[Flags]
public enum DoorType
{
Top = 0x01,
Right = 0x02,
Bottom = 0x04,
Left = 0x08
}
public class EnumFlagsAttribute : PropertyAttribute
{
public EnumFlagsAttribute() { }
}
#if UNITY_EDITOR // exclude this from a build
[CustomPropertyDrawer(typeof(EnumFlagsAttribute))]
public class EnumFlagsAttributeDrawer : PropertyDrawer
{
public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
{
_property.intValue = EditorGUI.MaskField(_position, _label, _property.intValue, _property.enumNames);
}
}
#endif
这允许您通过检查器从标志中选择一个或多个值。
将您的 RoomTemplate
脚本更改为
public class RoomTemplates : MonoBehaviour
{
public RoomSpawner[] bottomRooms;
public RoomSpawner[] topRooms;
public RoomSpawner[] leftRooms;
public RoomSpawner[] rightRooms;
[Space]
public RoomSpawner closedRoomTop;
public RoomSpawner closedRoomRight;
public RoomSpawner closedRoomBottom;
public RoomSpawner closedRoomLeft;
[Space]
public List<GameObject> rooms;
}
这可以直接访问预制件上 RoomSpawner
的值。
使用标志而不是整数来定义预制件上的隔壁方向。
然后每次生成一个新房间时,它都会将自己的位置广告到 occupiedPositions
,这样就不能再在这里生成其他房间了。
另外检查要添加的房间甚至可以去哪些方向,并且只能使用 Linq Where
从该列表中随机选择一个房间。
如果没有办法去,请改用封闭的房间。 (当然,如果您还想要随机选择封闭房间的可能性,您仍然可以将其添加到预制列表中)
public class RoomSpawner : MonoBehaviour
{
[EnumFlags] public DoorType openingDirections;
// Keep track of already used positions
private static List<Vector2Int> occupiedPositions = new List<Vector2Int>();
// store own room position
private Vector2Int roomFieldPosition;
private RoomTemplates templates;
private bool spawned = false;
private void Start()
{
templates = FindObjectOfType<RoomTemplates>();
roomFieldPosition = new Vector2Int(Mathf.RoundToInt(transform.localPosition.x), Mathf.RoundToInt(transform.localPosition.z));
occupiedPositions.Add(roomFieldPosition);
Invoke("Spawn", 0.5f);
}
private static DoorType GetPossibleDirections(Vector2Int position)
{
DoorType output = 0;
if (!occupiedPositions.Contains(new Vector2Int(position.x, position.y + 1))) output |= DoorType.Top;
if (!occupiedPositions.Contains(new Vector2Int(position.x, position.y - 1))) output |= DoorType.Bottom;
if (!occupiedPositions.Contains(new Vector2Int(position.x + 1, position.y))) output |= DoorType.Right;
if (!occupiedPositions.Contains(new Vector2Int(position.x - 1, position.y))) output |= DoorType.Left;
return output;
}
private void SpawnRoom(DoorType type)
{
Vector2Int nextPosition;
RoomSpawner[] templateArray;
RoomSpawner closedRoom;
switch (type)
{
case DoorType.Top:
nextPosition = new Vector2Int(roomFieldPosition.x, roomFieldPosition.y + 1);
templateArray = templates.topRooms;
closedRoom = templates.closedRoomTop;
break;
case DoorType.Right:
nextPosition = new Vector2Int(roomFieldPosition.x + 1, roomFieldPosition.y);
templateArray = templates.rightRooms;
closedRoom = templates.closedRoomRight;
break;
case DoorType.Bottom:
nextPosition = new Vector2Int(roomFieldPosition.x, roomFieldPosition.y - 1);
templateArray = templates.bottomRooms;
closedRoom = templates.closedRoomBottom;
break;
case DoorType.Left:
nextPosition = new Vector2Int(roomFieldPosition.x - 1, roomFieldPosition.y);
templateArray = templates.leftRooms;
closedRoom = templates.closedRoomLeft;
break;
default:
return;
}
if (occupiedPositions.Contains(nextPosition)) return;
var directions = GetPossibleDirections(nextPosition);
var prefabs = new List<RoomSpawner>();
foreach (var doorType in (DoorType[])Enum.GetValues(typeof(DoorType)))
{
if (!directions.HasFlag(doorType)) continue;
prefabs.AddRange(templateArray.Where(r => r.openingDirections.HasFlag(doorType)));
}
if (prefabs.Count == 0)
{
prefabs.Add(closedRoom);
}
// Need to spawn a room with a BOTTOM door.
var rand = Random.Range(0, prefabs.Count);
Instantiate(prefabs[rand], new Vector3(nextPosition.x, 0, nextPosition.y), Quaternion.identity);
}
private void Spawn()
{
if (spawned) return;
if (openingDirections.HasFlag(DoorType.Top)) SpawnRoom(DoorType.Top);
if (openingDirections.HasFlag(DoorType.Bottom)) SpawnRoom(DoorType.Bottom);
if (openingDirections.HasFlag(DoorType.Right)) SpawnRoom(DoorType.Right);
if (openingDirections.HasFlag(DoorType.Left)) SpawnRoom(DoorType.Left);
spawned = true;
}
}
我一直在研究程序生成关卡。 我已经创建了根据打开方式生成的盒子。
如果一个盒子有一个左边的重生点。左边的出生点会知道它需要在右边创建至少一扇门。 这似乎行得通,但出于某种原因,过了一会儿,房间开始堆叠在一起。即使我的代码不允许这样做?
这会不会是因为墙壁没有完全对称?因为我想要更广泛和不同类型的关卡,我认为只有重生点对齐就足够了吗?
这就是关卡启动 4 条不同路径的方式。
仍然很好
还不错
如您所见,起始层的所有入口都已被封锁。
在此之后,他们不断堆叠 op 彼此顶部,从而永无止境地生成关卡。
房间生成器
public class RoomSpawner : MonoBehaviour
{
public int openingDirection;
// 1 --> need bottom door
// 2 --> need top door
// 3 --> need left door
// 4 --> need right door
private RoomTemplates templates;
private int rand;
private bool spawned = false;
void Start(){
templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.5f);
}
void Spawn(){
if(spawned == false){
if(openingDirection == 1){
// Need to spawn a room with a BOTTOM door.
rand = Random.Range(0, templates.bottomRooms.Length);
Instantiate(templates.bottomRooms[rand], transform.position, templates.bottomRooms[rand].transform.rotation);
} else if(openingDirection == 2){
// Need to spawn a room with a TOP door.
rand = Random.Range(0, templates.topRooms.Length);
Instantiate(templates.topRooms[rand], transform.position, templates.topRooms[rand].transform.rotation);
} else if(openingDirection == 3){
// Need to spawn a room with a LEFT door.
rand = Random.Range(0, templates.leftRooms.Length);
Instantiate(templates.leftRooms[rand], transform.position, templates.leftRooms[rand].transform.rotation);
} else if(openingDirection == 4){
// Need to spawn a room with a RIGHT door.
rand = Random.Range(0, templates.rightRooms.Length);
Instantiate(templates.rightRooms[rand], transform.position, templates.rightRooms[rand].transform.rotation);
}
spawned = true;
}
void OnTriggerEnter2D(Collider2D other){
if(other.CompareTag("SpawnPoint")){
if(other.GetComponent<RoomSpawner>().spawned == false && spawned == false){
// spawns walls blocking off any opening !
Instantiate(templates.closedRoom, transform.position, Quaternion.identity);
Destroy(gameObject);
}
spawned = true;
}
}
}
}
毁灭者标签
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Destroyer : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other ){
Destroy(other.gameObject);
}
}
房间模板
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour
{
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public GameObject closedRoom;
public List<GameObject> rooms;
}
所以我想出了以下解决方案:
(限制 - 如果一个房间已经生成,它仍然有可能被其他生成房间包围,因此它的门被挡住)
具有适当的枚举标志类型
#if UNITY_EDITOR // exclude this from a build using Unity.Editor; #endif [Flags] public enum DoorType { Top = 0x01, Right = 0x02, Bottom = 0x04, Left = 0x08 } public class EnumFlagsAttribute : PropertyAttribute { public EnumFlagsAttribute() { } } #if UNITY_EDITOR // exclude this from a build [CustomPropertyDrawer(typeof(EnumFlagsAttribute))] public class EnumFlagsAttributeDrawer : PropertyDrawer { public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label) { _property.intValue = EditorGUI.MaskField(_position, _label, _property.intValue, _property.enumNames); } } #endif
这允许您通过检查器从标志中选择一个或多个值。
将您的
RoomTemplate
脚本更改为public class RoomTemplates : MonoBehaviour { public RoomSpawner[] bottomRooms; public RoomSpawner[] topRooms; public RoomSpawner[] leftRooms; public RoomSpawner[] rightRooms; [Space] public RoomSpawner closedRoomTop; public RoomSpawner closedRoomRight; public RoomSpawner closedRoomBottom; public RoomSpawner closedRoomLeft; [Space] public List<GameObject> rooms; }
这可以直接访问预制件上
RoomSpawner
的值。使用标志而不是整数来定义预制件上的隔壁方向。
然后每次生成一个新房间时,它都会将自己的位置广告到
occupiedPositions
,这样就不能再在这里生成其他房间了。另外检查要添加的房间甚至可以去哪些方向,并且只能使用
Linq Where
从该列表中随机选择一个房间。如果没有办法去,请改用封闭的房间。 (当然,如果您还想要随机选择封闭房间的可能性,您仍然可以将其添加到预制列表中)
public class RoomSpawner : MonoBehaviour { [EnumFlags] public DoorType openingDirections; // Keep track of already used positions private static List<Vector2Int> occupiedPositions = new List<Vector2Int>(); // store own room position private Vector2Int roomFieldPosition; private RoomTemplates templates; private bool spawned = false; private void Start() { templates = FindObjectOfType<RoomTemplates>(); roomFieldPosition = new Vector2Int(Mathf.RoundToInt(transform.localPosition.x), Mathf.RoundToInt(transform.localPosition.z)); occupiedPositions.Add(roomFieldPosition); Invoke("Spawn", 0.5f); } private static DoorType GetPossibleDirections(Vector2Int position) { DoorType output = 0; if (!occupiedPositions.Contains(new Vector2Int(position.x, position.y + 1))) output |= DoorType.Top; if (!occupiedPositions.Contains(new Vector2Int(position.x, position.y - 1))) output |= DoorType.Bottom; if (!occupiedPositions.Contains(new Vector2Int(position.x + 1, position.y))) output |= DoorType.Right; if (!occupiedPositions.Contains(new Vector2Int(position.x - 1, position.y))) output |= DoorType.Left; return output; } private void SpawnRoom(DoorType type) { Vector2Int nextPosition; RoomSpawner[] templateArray; RoomSpawner closedRoom; switch (type) { case DoorType.Top: nextPosition = new Vector2Int(roomFieldPosition.x, roomFieldPosition.y + 1); templateArray = templates.topRooms; closedRoom = templates.closedRoomTop; break; case DoorType.Right: nextPosition = new Vector2Int(roomFieldPosition.x + 1, roomFieldPosition.y); templateArray = templates.rightRooms; closedRoom = templates.closedRoomRight; break; case DoorType.Bottom: nextPosition = new Vector2Int(roomFieldPosition.x, roomFieldPosition.y - 1); templateArray = templates.bottomRooms; closedRoom = templates.closedRoomBottom; break; case DoorType.Left: nextPosition = new Vector2Int(roomFieldPosition.x - 1, roomFieldPosition.y); templateArray = templates.leftRooms; closedRoom = templates.closedRoomLeft; break; default: return; } if (occupiedPositions.Contains(nextPosition)) return; var directions = GetPossibleDirections(nextPosition); var prefabs = new List<RoomSpawner>(); foreach (var doorType in (DoorType[])Enum.GetValues(typeof(DoorType))) { if (!directions.HasFlag(doorType)) continue; prefabs.AddRange(templateArray.Where(r => r.openingDirections.HasFlag(doorType))); } if (prefabs.Count == 0) { prefabs.Add(closedRoom); } // Need to spawn a room with a BOTTOM door. var rand = Random.Range(0, prefabs.Count); Instantiate(prefabs[rand], new Vector3(nextPosition.x, 0, nextPosition.y), Quaternion.identity); } private void Spawn() { if (spawned) return; if (openingDirections.HasFlag(DoorType.Top)) SpawnRoom(DoorType.Top); if (openingDirections.HasFlag(DoorType.Bottom)) SpawnRoom(DoorType.Bottom); if (openingDirections.HasFlag(DoorType.Right)) SpawnRoom(DoorType.Right); if (openingDirections.HasFlag(DoorType.Left)) SpawnRoom(DoorType.Left); spawned = true; } }