Собственно как у нас игра может быть без НПС, в особенности если это экшен или RPG?
Ставим задачу: разместить на карте блок в котором будут создаваться новые НПС с некоторой периодичностью. Само собой, в последствии мы усложним себе задачку.
Для начала берём стандартного Drooling Zombie из тулбокса и кидаем на плейс:
Взяли его по тому, что у него уже есть крипты с анимацией и поведением. Так будет веселее.
Теперь добавляем парт для обозначения спавна. Ставим на него нашего зомби и пихаем его под парт. Также запихиваем в него Script.
Подготовка завершена. Пишем скрипт что будет брать нашего зомби как источник и периодически запускать нового в этом же месте.
local timer = 5 -- Задаём таймер возрождения нового зомби local zombie = script.Parent["Drooling Zombie"]:Clone() -- создаём себе модель в памяти script.Parent["Drooling Zombie"]:Destroy() -- удаляем старую модель local oldSpawnTime = workspace:GetServerTimeNow() -- Запоминаем время старого спавна while task.wait() do if workspace:GetServerTimeNow() - oldSpawnTime >= timer then -- Проверяем прошёл ли таймер local newZombie = zombie:Clone() -- Клонируем модель newZombie.Parent = workspace -- Перемещаем модель в workspace oldSpawnTime = workspace:GetServerTimeNow() -- Обновляем время старого спавна end end
Ну вот, зомбаки спавнятся и радостно бегут к нам. Заодно и убивают. Можем дать нашему игроку в руки мечь, чтобы давал сдачи и.. вот уже игра — правда без цели.
Первичная задача выполнена. Мы так же можем поставить кучу копий нашего спавнера и каждый из них будет генерировать свою армию. Естественно, что если заменить модельку зомби на иную, то и армия будет разношёрстной (но и код надо менять). И вот они уже задорно машутся, почти не обращая на меня внимания.
Что дальше? Надо упростить работу дизайнеру, чтобы ему не нужно было лезть в код. Никогда!
- Надо сделать так, чтобы не нужно было менять код, при смене модели для спавна.
- А ещё, нужно изменить разбег в таймере, да ещё и так чтобы спавн был похож на случайный
- Добавить ограничение по количеству единовременно заспавленных мобов
- Добавить радиус реагирования, чтобы мобу не спавнились, если рядом нет игрока (не зачем захламлять карту)
Т.е. нам надо добавить в спавнер параметры, определить место для модели и где будут храниться живые мобы.
Для начала, по взрослому, мы должны наш спавнер превратить в модель. И всё дальнейшее уже производить с ней.
Для начала, в модели должны быть две папочки: Object — в ней будет у нас модель для спавна и Spawned — будет содержать заспавненные модели.
Теперь надо добавить параметры: timeMin, timeMax — интервал времени спавна, spawnNum — ограничитель числа живых на карте, spawnMax — ограничитель общего числа заспавненных, radius — радиус поиска игроков для спавна.
Немножко микро-оптимизации. Стоит сразу определиться какие числа у нас будут целыми, а какие дробными. Не стоит все делать дробными (с заделом на будущее) или целыми, чтобы в последствии не пришлось маться.
Ещё немного об облегчении жизни программиста в будущем. Стоит использовать печатать сообщения, если дизайнер забыл что-то сделать — положить модель, указать значения и.. останавливать работу скрипта, чтобы не плодились ошибки. Либо менять значения на значения по умолчанию.
Ну и собственно сам скрипт:
--[[ Универсальный спавнер НПС
Модель спавнера размещается в любом месте плейса.
В папку Object необходимо положить модель NPC которого нужно спавнить.
Модель НПС должна быть размещена на спавнере.
В переменных спавнера задаются значения:
Radius - радиус спавна NPC от центра спавнера
TimeMin - минимальное время спавна NPC
TimeMax - максимальное время спавна NPC
SpawnMax - максимальное количество NPC одновременно
SpawnNum - количество данных NPC на карте единовременно
Создано: GAVsi 10/09/2026
Изменено:
]]
local me = script.Parent -- ссылка на сам спавнер
local model = me.Object:FindFirstChildWhichIsA("Model") -- нам нужна модель
if model then -- проверка того, что модель есть
local npc = model:Clone() -- создаём себе модель в памяти
model:Destroy() -- удаляем старую модель
local radius = me.Radius.Value -- радиус реагирования на игрока для спавна
local timeMin = me.TimeMin.Value -- минимальное время
local timeMax = me.TimeMax.Value -- максимальное время
local spawnNum = me.SpawnNum.Value -- ограничитель числа НПС на карте
local spawnMax = me.SpawnMax.Value -- ограничитель общего числа заспавненных НПС
-- проверка на корректность значений
if radius == 0 then
print("Radius не может быть 0")
radius = 100
end
if timeMax == 0 then
print("TimeMax не может быть 0")
timeMax = 6
end
if timeMin == 0 then
print("TimeMin не может быть 0")
timeMin = 3
end
if timeMin > timeMax then
print("TimeMin не может быть больше TimeMax")
timeMin = timeMax
end
if spawnNum == 0 then
print("SpawnNum не может быть 0")
spawnNum = 5
end
if spawnMax == 0 then
print("SpawnMax не может быть 0")
spawnMax = 10
end
local newSpawnTime = workspace:GetServerTimeNow() -- Запоминаем время старого спавна
local allSpawned = 0 -- Сколько НПС уже было заспавнено
while allSpawned < spawnMax do
task.wait() -- технологическое прерывание цикла
-- определяем есть ли хоть один игрок на дистанции для спавна
local distance = 0 -- Расстояние между игроком и NPC
for i, player in pairs(game.Players:GetPlayers()) do -- Проверяем расстояние между игроком и NPC
local character = player.Character -- Получаем модель игрока
if character then
local head = character:FindFirstChild("Head") -- Ищем голову у игрока
if head then
local humanoid = character:FindFirstChild("Humanoid")
if humanoid then
if humanoid.Health > 0 then -- игрок есть и он живой
local dst = (me.Spawner.Position - head.Position).Magnitude
if dst <= radius then
distance = dst -- расстояние как флаг
break -- дальше не зачем проходить цикл
end
end
end
end
end
end
if distance > 0 then -- игрок найден
if workspace:GetServerTimeNow() > newSpawnTime then -- время вышло, можно спавнить
local current = me.Spawn:GetChildren() -- сколько сейчас заспавнено
if #current < spawnNum then -- можно спавнить?
allSpawned = allSpawned + 1 -- увеличиваем счетчик спавненных NPC
newSpawnTime = workspace:GetServerTimeNow() + math.random(timeMin, timeMax) -- время следующего спавна
local newNPC = npc:Clone() -- Клонируем модель
newNPC.Parent = me.Spawn -- Перемещаем модель в workspace
end
end
end
end
else
warn("Нет модели в Object", script.Parent) -- критическое замечание, блокирующее работу скрипта
end
Как можно заметить, большая часть скрипта, это комментарии на будущее вмешательство и.. проверка ошибок человеческого фактора.
Ну как то так. Правда случился косяк — одна модель не полностью удалилась и считалась как присутствующая… Но это уже косяк скрипта в зомбике, а не в нашем. Хотя… Мы конечно можем добавить у себя проверку на то, что НПС живой, ведь не всегда модели поверженных НПС убираются из мира.
Само собой, можем размножить модель спавнера с заменой модели НПС и поменяв остальные настройки. Но! В качестве домашнего задания — а что будет когда таких спавнеров десятки и в его скрипте надо что-то изменить? Ответ «простой» — надо скрипт выносить отдельно, чтобы он следил сразу за всеми спавнерами. Но это уже к простейшему спавнеру не относится.




