#c# #unity3d #spawn #procedural-generation
#c# #unity3d #порождение #процедурная генерация
Вопрос:
Я немного новичок в программировании, поэтому извините, если я упускаю что-то очевидное.
Я работаю над процедурной генерацией свалки конечных границ. Я использовал учебное пособие по 2D Unity (часть 1 из 3, найденная здесь: https://www.youtube.com/watch?v=qAf9axsyijYamp;ab_channel=Blackthornprod ), чтобы сделать скелет для моего кода, но у меня были проблемы с перекрытием основных плиток. Я попробовал несколько исправлений в Интернете, но ничего не делает работу полностью.
Физика.Перекрытие сделало работу кода намного лучше, но все еще есть регулярные перекрытия. Я подозреваю, что перекрывающиеся плитки могут появляться в одном и том же экземпляре, что заставляет команду Overlap ошибочно возвращать 0 коллайдеров, но я понятия не имею, как это проверить или избежать.
Вот мой код. Это почти прямая копия с видео выше.:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spawnTile : MonoBehaviour
{
public int openingDirection;
//1 = N
//2 = E
//3 = S
//4 = W
private GroundTemplates templates;
//Stores tiles to spawn
private int rand;
private bool spawned = false;
private bool IsInBounds = false;
public LayerMask checkBox;
//Layermask to check if there is already ground at spawn point
public Vector3 chunkScale;
//size of check area for dup spawns
void Start()
{
templates = GameObject.FindGameObjectWithTag("GroundTiles").GetComponent<GroundTemplates>();
StartCoroutine(Spawn());
}
public IEnumerator Spawn()
{
Collider[] hitColliders = Physics.OverlapBox(transform.position, chunkScale , Quaternion.identity, checkBox); // makes an array of objects inside chunk with layer mask checkBox. Used for checking if ground is present
yield return new WaitForSeconds(.2f);
Debug.Log("colliders: " hitColliders.Length);
if (spawned == false )
{
if (openingDirection == 1 amp;amp; IsInBounds == true amp;amp; hitColliders.Length == 0)
{
rand = Random.Range(0, templates.NorthRooms.Length);
Instantiate(templates.NorthRooms[rand], transform.position, templates.NorthRooms[rand].transform.rotation);
}
else if (openingDirection == 2 amp;amp; IsInBounds == true amp;amp; hitColliders.Length == 0)
{
rand = Random.Range(0, templates.EastRooms.Length);
Instantiate(templates.EastRooms[rand], transform.position, templates.EastRooms[rand].transform.rotation);
}
else if (openingDirection == 3 amp;amp; IsInBounds == true amp;amp; hitColliders.Length == 0)
{
rand = Random.Range(0, templates.SouthRooms.Length);
Instantiate(templates.SouthRooms[rand], transform.position, templates.SouthRooms[rand].transform.rotation);
}
else if (openingDirection == 4 amp;amp; IsInBounds == true amp;amp; hitColliders.Length == 0)
{
rand = Random.Range(0, templates.WestRooms.Length);
Instantiate(templates.WestRooms[rand], transform.position, templates.WestRooms[rand].transform.rotation);
}
spawned = true;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Spawn") amp;amp; (other.GetComponent<spawnTile>().spawned == true))
{
Destroy(gameObject);
Debug.Log("Destroyed");
}
else if (other.CompareTag("InBounds"))
{
IsInBounds = true;
}
}
}
Несколько фотографий выходных данных:
сборный элемент точки возрождения, к которому прикреплен код
пример сборного сборного листа
Я предполагаю, что в коде есть некоторые избыточности из-за нескольких попыток отладки, так что lmk, если я смогу упростить ситуацию.
Ответ №1:
Быстрый просмотр видеороликов не показал никаких признаков того, что первоначальный автор использовал сопрограмму для создания новой плитки.
Для каждой генерируемой плитки вы проверяете, есть ли перекрытие, затем ждете 0,2 секунды. Это ожидание там, которое может очень легко позволить другой плитке также проверять то же пространство и не обнаруживать перекрывающихся плиток. Таким образом, два или более фрагмента не будут видеть перекрытий, и все они записываются в одну и ту же позицию.
Могу ли я предложить очистить код, удалить эту принудительную задержку и попробовать это вместо:
void Start ( )
{
templates = GameObject.FindGameObjectWithTag ( "GroundTiles" ).GetComponent<GroundTemplates> ( );
var hitColliders = Physics.OverlapBox ( transform.position, chunkScale, Quaternion.identity, checkBox ); // makes an array of objects inside chunk with layer mask checkBox. Used for checking if ground is present
Debug.Log ( "colliders: " hitColliders.Length );
if ( !spawned )
{
if ( IsInBounds amp;amp; hitColliders.Length == 0 )
{
GameObject [ ] rooms = null;
switch ( openingDirection )
{
case 1: rooms = templates.NorthRooms; break;
case 2: rooms = templates.EastRooms; break;
case 3: rooms = templates.SouthRooms; break;
case 4: rooms = templates.WestRooms; break;
default: return;
}
var room = rooms [ UnityEngine.Random.Range ( 0, rooms.Length ) ];
Instantiate ( room, transform.position, room.transform.rotation );
}
spawned = true;
}
}
Комментарии:
1. Спасибо за ответ. Я ввел сопрограмму и задержку, чтобы отслеживать, что создатель делал при отладке. Я пытался использовать ваш код, но он говорит, что у меня не может быть итератора в Start(). Есть ли для этого обходной путь?
2. Извините, я скопировал часть вашего кода и оставил там оператор yield. Я отредактировал код.
3. Спасибо за вашу помощь! Я получил код, работающий с вашей помощью, и некоторые настройки с моей стороны. Смотрите Мой комментарий ниже для моего решения. : D
4. @Overboard Не отмечать ответ считается дурным тоном и может привести к негативной репутации. Просто чтобы вы знали.
5. Спасибо за предупреждение! Мой плохой!
Ответ №2:
Я получил код, работающий с помощью Milan в этой теме. После использования его кода. Я использовал сериализованные поля, чтобы увидеть, какие условия не выполняются, и обнаружил, что проверка InBounds не прошла. Поэтому я переработал проверку так, чтобы она выполнялась в Start(), а не в OnTriggerEnter();
Я только что пришел к этому решению, поэтому дайте мне знать, если у вас есть полезные отзывы.
Вот новый код
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class betterTileSpawner : MonoBehaviour
{
public int openingDirection;
//1 = N
//2 = E
//3 = S
//4 = W
public LayerMask checkBox;
//Layermask to check if there is already ground at spawn point
public Vector3 chunkScale;
//size of check area for dup spawns
private Collider Boundarys;
//Used for
private GroundTemplates templates;
//Stores tiles to spawn
private int rand;
[SerializeField]
private bool spawned = false;
[SerializeField]
private bool IsInBounds = false;
[SerializeField]
private string Helper;
[SerializeField]
private int NumColliders;
IEnumerator Start()
{
templates = GameObject.FindGameObjectWithTag("GroundTiles").GetComponent<GroundTemplates>();
yield return new WaitForSeconds(.01f);
var hitColliders = Physics.OverlapBox(transform.position, chunkScale, Quaternion.identity, checkBox); // makes an array of objects inside chunk with layer mask checkBox. Used for checking if ground is present
Boundarys = GameObject.FindGameObjectWithTag("InBounds").GetComponent<Collider>();
Debug.Log("colliders: " hitColliders.Length);
NumColliders = hitColliders.Length;
Helper = "didn't do any ifs";
if (Boundarys.bounds.Contains(transform.position))
{ IsInBounds = true; }
if (!spawned)
{
Helper = "Not Spawned " NumColliders;
Debug.Log("Not Spawned");
if (IsInBounds amp;amp; hitColliders.Length == 0)
{
GameObject[] rooms = null;
switch (openingDirection)
{
case 1: rooms = templates.NorthRooms; break;
case 2: rooms = templates.EastRooms; break;
case 3: rooms = templates.SouthRooms; break;
case 4: rooms = templates.WestRooms; break;
default: Debug.Log("Default case"); break;
}
var room = rooms[UnityEngine.Random.Range(0, rooms.Length)];
Instantiate(room, transform.position, room.transform.rotation);
Helper = "Should have spawned, wenth through code";
Debug.Log("Spawned");
spawned = true;
}
}
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Spawn") amp;amp; (other.GetComponent<betterTileSpawner>().spawned == true))
{
Destroy(gameObject);
Debug.Log("Destroyed");
}
else if (other.CompareTag("Ground"))
{
Destroy(gameObject);
Debug.Log("Destroyed");
}
}
}