Простой пример генерации уровня

Логика работы генератора в таких случаях, зависит от поставленной задачи, например, нужно разместить рандомно объекты на участке определенного размера, или сделать сборку из заготовленных комнат. В нашем примере реализации генерации уровня, мы попробуем сделать генератор туннеля / пещеры, что-то в этом духе. Суть в следующем. У нас есть подготовленные префабы различных секций, в каждой из них, есть точка входа и выхода. Задача скрипта, «склеить» точку выхода одной секции, с точкой входа другой. То есть, это получится коридор, рандомной длинны, не превышая максимальное значение, и с поворотами в одну или другую сторону, так же в случайном порядке.

Простой пример генерации уровня

Расстановка точек на модели:


Обратите внимание, на направление оси Z, именно этот вектор будет направляющим. И как видно на скриншоте, секции должны быть одинаковые по размеру, чтобы условно всё поле делилось на равные клетки.

Далее, на префабы цепляем скрипт Section:

using UnityEngine;
using System.Collections;

public class Section : MonoBehaviour {

	public Transform startPoint;
	public Transform endPoint;

}

Тут и указываем дочерние точки объекта.

Кроме основных префабов, еще понадобятся стартовые и конечные. Допустим, есть пять стартовых и финальных комнат, скрипт с начало выберет случайную стартовую секцию, затем от нее будет строить основные, а в конце цикла, прилепит конечную комнату. Поэтому, создаем эти секции, эти префабы будут потом в отдельных массивах.

Теперь, цепляем на сцену скрипт Generator:

using UnityEngine;
using System.Collections;

public class Generator : MonoBehaviour {

	public Section[] prefabs; // секции с двумя контактными точками
	public Section[] start; // стартовые секции
	public Section[] stop; // конечные секции
	public int sections = 20; // макс. возможное число секций
	public float sectionSize = 1; // размер секции, все стороны должны быть равны

	private Section current, previous;
	private int index;

	void Awake()
	{
		Generate();
	}

	void InstSection(Section[] arr)
	{
		current = Instantiate(arr[Random.Range(0, arr.Length)]) as Section;
		current.gameObject.name = "Section_" + index;
		current.transform.parent = transform;

		if(previous) 
		{
			current.transform.forward = previous.endPoint.forward;
			current.transform.position += previous.endPoint.position - current.startPoint.position;
		}
	}

	void Generate() 
	{
		InstSection(start);
		previous = current;

		Section tmp = null;

		for(int i = 0; i < sections; i++) 
		{
			index = i;

			if(!Check())
			{
				tmp = current;
				InstSection(prefabs);
			}
			else
			{
				Destroy(current.gameObject);
				previous = tmp;
				InstSection(stop);
				return;
			}

			previous = current;
		}

		InstSection(stop);
	}

	bool Check() // проверка, есть ли на пути ранее созданные секции
	{
		Vector3 position = current.endPoint.position + current.endPoint.forward * sectionSize/2;
		Collider[] colliders = Physics.OverlapSphere(position, sectionSize/4);
		foreach(Collider hit in colliders) if(hit) return true;
		return false;
	}
}

Не смотря на то что, мы указываем число циклов sections, перед тем как создать объект, скрипт делает проверку на наличие в зоне других объектов, если они там есть, текущая секция уничтожается и на ее место ставится финальная, цикл завершается. Таким образом, общая длина карты может быть разной, то есть, опять же рандом.

Скачать демо проект:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.

Комментариев 8

Офлайн
proger33 28 января 2018
Light,
Пардон sweat_smile Vector3 position = current.endPoint[i].position + current.endPoint[i].forward * sectionSize / 2;

Light,
Я исправил свои ошибки, но ничего всё равно не изменилось, он до сих пор ругается на Transform[].
Офлайн
proger33 28 января 2018
Light,
Такой вопрос, а можно ли использовать несколько endPoint'ов в вашем скрипте? Пытался модифицировать но так и не получилось, ругается что массив Transform[] задал. Ну по сути делаю как-то так, поправьте если ошибаюсь.
bool Check() // проверка, есть ли на пути ранее созданные секции
{
for (int i = 0; i <= current.endPoint.length; i++)
{
Vector3 position = current.endPoint.i.position + current.endPoint.i.forward * sectionSize / 2;
Collider[] colliders = Physics.OverlapSphere(position, sectionSize / 4);
foreach (Collider hit in colliders) if (hit) return true;
return false;
}
}
Офлайн
Light 8 сентября 2017
Скрипт отредактирован!

Magitrop, для 2д почти тоже самое всё, смотри скриншот точек контакта на модели, если у тебя вместо Z, направляющая ось это Y, то тогда forward везде нужно поменять на up. Затем физику изменить под 2д. Кроме того, необходимо правильно указать размер sectionSize.

Чтобы определить размер секции, если у тебя составная модель из нескольких других, то нужно создать дочерний объект, поместить его в центр и прицепить 2д бокс коллайдер, затем растянуть его, чтобы он перекрыл всю модель. Наибольшая сторона и будет считаться размером секций. Внимание! Нужно учитывать, чтобы все секции были одинакового размера.

Если возникнут трудности, пиши в ЛС.
Офлайн
Magitrop 7 сентября 2017
Light, ну, вот, я и говорю, будут Transform'ы, отражающие точки контакта (может, даже больше 2, не знаю), только в 2д: оси X и Y

Light, мне бы только поменять ваш код таким образом, чтоб он выстраивал все блоки по плоскости XY, типа, как в 2д, и все, этого бы хватило
Офлайн
Light 7 сентября 2017
Magitrop, вот я и спрашиваю как у тебя сделано, потому что от направления осей точек контакта, зависит сам код.
Офлайн
Magitrop 7 сентября 2017
Light, ммм, это вопрос ко мне, как я делаю у себя, или как это сделано в вашем коде?

Light, если что, мне нужно сделать основными осями X и Y, а точки контакта желательно было бы типа Vector2, но и Transform тоже подходит, так что мне нужна только генерация всего этого, как тут есть, но только под другим осям и в 2D.
Офлайн
Light 7 сентября 2017
Magitrop, а как сделаны точки контакта? Направление осей.
Офлайн
Magitrop 6 сентября 2017
Соре за тупость, но я не могу понять, как это переделать под 2д, с комнатами-спрайтами?

Я менял Physics на Physics2D (ну, и OverlapSphere на OverlapCircleAll соответственно), менял все forward на up, но это не работает.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика