Не могу разобраться со спавном объектов в Unity
Сделал скрипт рандомного спавна объеков по клеткам, чтобы они не спавнились друг на друге, спавнились с редкостью. Деревянная коробка и часы очень часто, каменная коробка, монеты и бомба реже чем деревянная коробка, красное зелье и зомби редко а зелёное зелье очень редко. Чтобы бомба и зомби не спавнилась рядом с персонажем. Вот скрипт:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawn : MonoBehaviour
{
public Transform[] Spawn_Points; //Точки по которым спавнятся обьекты.
public GameObject[] Objects; //Сами объекты.
public bool[] IsFull; //Буллы для проверки есть ли на точке объект.
public float spawnWait; //Время спавна
public int Soft = 5; //Сколько ломающихся объектов на сцене
public GameObject Bomb; //Ссылка бомбы
public float Timer = 30; //Таймер
public bool Exit = true; //Булл для проверки выхода из условия
private void Start()
{
StartCoroutine(SpawnWaves()); //Старт коррутины
}
private void Update() {
Timer -= Time.deltaTime; //Старт таймера
}
IEnumerator SpawnWaves()
{
while (true)
{
int randomSpawn = Random.Range(0, Spawn_Points.Length - 2); //Рандомный выбор точки спавна объекта
int randomObject = Random.Range(0, 6); //Рандомный выбор спавна обычного объекта
int randomRareObject = Random.Range(0, 3); //Рандомный выбор спавна редкого объекта
int randomChance = Random.Range(0, 121); //Выбор рандомного числа шанса
Quaternion spawnRotation = Quaternion.identity;
if (randomObject == 0 || randomObject == 2 || randomObject == 3 || randomObject == 5 || randomObject == 6)
{
Soft += 1; //При спавне ломающегося объекта прибавляем его здесь
}
if (randomObject == 4)
{
Bomb.GetComponent<Bomb_Script>().Respawn = true; //При спавне бомбы включаем отчет до взрыва
}
if(Timer <= 10 && IsFull[randomSpawn] == true) { //Если таймер меньше 10 и IsFull точки спавна свободен
Instantiate(Objects[3], Spawn_Points[randomSpawn].position, spawnRotation); //Спавним часы
spawnWait = 3; //Возвращаем значение
Timer += 5; //К числу таймера плюсуем число которое дают часы
Exit = true; //Разрешаем проход дальше
}
else if(Timer <= 10) { //Если прошлое условие не верно и таймер меньше 10
Exit = false; //Запрещаем проход дальше
spawnWait = 0; //Ставим ожидание спавна на 0 чтобы быстрее заспавнить часы
}
if(randomChance <= 100 && IsFull[randomSpawn] == true && Exit == true) { //Если шанс равен или меньше 100, IsFull точки спавна свободен и проход доступен
if(randomObject == 2 && Soft >= 5) { //Если рандомный объект равен 2 и количество ломающихся объектов на сцене равно или больше 5
Instantiate(Objects[2], Spawn_Points[randomSpawn].position, spawnRotation);//Спавним каменную коробку
spawnWait = 3; //Возвращаем значение
}
else { //Если прошлое условие не верно
Instantiate(Objects[randomObject], Spawn_Points[randomSpawn].position, spawnRotation); //Спавним обычный рандомный объект
spawnWait = 3; //Возвращаем значение
}
}
else if(randomChance >= 100 && randomChance <= 110 && IsFull[randomSpawn] == true) { //Если прошлое условие не верно, шанс равен или больше 100 но равен или меньше 110 и IsFull точки спавна свободен
Instantiate(Objects[7], Spawn_Points[randomSpawn].position, spawnRotation); //Спавн зелёного зелья
spawnWait = 3; //Возвращаем значение
}
else if(randomChance >= 100 && IsFull[randomSpawn] == true) { //Если шанс равен или больше 100 и IsFull точки спавна свободен
Instantiate(Objects[randomRareObject], Spawn_Points[randomSpawn].position, spawnRotation); //Спавн редкого рандомного объекта
spawnWait = 3; //Возвращаем значение
}
else if(IsFull[randomSpawn] == false) //Если все выше перечисленные условия не равны и IsFull точки спавна не свободен
{
spawnWait = 0; //Ставим ожидание спавна на 0 чтобы быстрее заспавнить объекты
}
yield return new WaitForSeconds(spawnWait);
}
}
}
Осталось только сделать отдельный спавн зомби и бомбы вдалеке от игрока. Может как-то оптимизировать. Помогите как это сделать.
Ответы (1 шт):
Автор решения: Yaroslav
→ Ссылка
public class TileObject : MonoBehaviour // у предметов очевидно должен быть свой класс
{
[SerializeField] private ObjectType _type;
[SerializeField] private bool _isSoft = false;
// относительный шанс (относительно других)
// например A:6 к B:2, в A в твое чаще B
[SerializeField] private int _spawnChange = 1;
public ObjectType Type { get => _type; } // свойство позволяет читать (get), но не иметь право изменять (set)
public bool IsSoft { get => _isSoft; }
public int SpawnChange { get => _spawnChange; }
}
public enum ObjectType { BoxWood, BoxRock, PotionRed, PotionGreen, Clock, Coin, Zombie ,Bomb }
public class GameGrid : MonoBehaviour
{
// поля внутренней кухни должны быть private
// SerializeField делает поля видимыми в инспекторе
[SerializeField] private float _sessionTime = 90f;
[SerializeField] private float _spawnDelay = 1f;
[SerializeField] private int _gridWidth = 10;
[SerializeField] private int _gridHeight = 6;
[SerializeField] private float _gridTileStep = 32;
[Space]
[SerializeField] private TileObject[] _objectTemplates; // все объекты
private TileObject[,] _grid; // если это клетки то это должен быть двумерный массив (матрица)
private Dictionary<ObjectType, TileObject> _templates;
private List<TileObject> _objects;
private float _sessionBackTimer; // Timer чево...
private int _softCounter; // имя Soft не отображает функцию
private IEnumerator _spawn;
private IEnumerator _backTimer;
private void Start ()
{
// для удобства создаём справочник объектов для поиска по их типу при необходимости _templates[ObjectType.Bomb]
_templates = new Dictionary<ObjectType, TileObject>();
foreach (TileObject template in _objectTemplates)
_templates.Add(template.Type, template);
_objects = new List<TileObject>();
_grid = new TileObject[_gridWidth, _gridHeight];
_sessionBackTimer = _sessionTime;
_spawn = SpawnCoroutine(); // нужна ссылка на корутину, что бы потом её можно было остановить
StartCoroutine(_spawn);
_backTimer = BackTimerCoroutine();
StartCoroutine(_backTimer);
}
private IEnumerator SpawnCoroutine ()
{
while (true) {
if (GriidIsFull() == false) // одна функция, одна задача, есть ли пустые клетки
SpawnObject(); // одна функция, одна задача, спавн
yield return new WaitForSeconds(_spawnDelay);
}
}
private void SpawnObject ()
{
TileObject Object = GetSpawnObject(); // одна задача, ролл спавнящегося предмета
if (Object != null)
{
Vector2 Tile = GetFreeTile(); // одна задача, нахождение случайной свободной клетки
TileObject NewObject = Instantiate(Object.gameObject, transform).GetComponent<TileObject>();
NewObject.transform.localPosition = GetPositionOfTile(Tile);
if (NewObject.IsSoft)
_softCounter++;
_grid[(int)Tile.x, (int)Tile.y] = NewObject;
_objects.Add(NewObject);
}
else
Debug.LogWarning("SpawnObject.Fail");
}
private TileObject GetSpawnObject ()
{
int ChanceSumm = 0; // сумма всех шансов
foreach (var template in _templates)
ChanceSumm += template.Value.SpawnChange;
int Target = Random.Range(0, ChanceSumm); // случайное число из общей суммы
foreach (var template in _templates)
{
Target -= template.Value.SpawnChange;
if (Target <= 0)
return template.Value; // на ком закончится и есть выпавший предмет
}
return null;
}
private Vector2 GetFreeTile ()
{
List<Vector2> FreeTiles = new List<Vector2>();
for (int x = 0; x < _gridWidth; x++)
for (int y = 0; y < _gridHeight; y++)
if (_grid[x, y] == null)
FreeTiles.Add(new Vector2(x, y));
int Rnd = Random.Range(0, FreeTiles.Count);
return FreeTiles[Rnd];
}
private bool GriidIsFull ()
{
for (int x = 0; x < _gridWidth; x++)
for (int y = 0; y < _gridHeight; y++)
if (_grid[x, y] == null)
return false;
return true;
}
private Vector2 GetPositionOfTile (Vector2 tile)
{
Vector2 GridSize = new Vector2(_gridWidth-1, _gridHeight-1)*_gridTileStep;
return -GridSize*0.5f+tile*_gridTileStep;
}
private IEnumerator BackTimerCoroutine ()
{
while (_sessionBackTimer > 0)
{
_sessionBackTimer -= Time.deltaTime;
yield return null;
}
_sessionBackTimer = 0;
TimeOut();
}
private void TimeOut ()
{
StopCoroutine(_spawn);
}
}