Настала пора немного прилизать, то что уже имеется. Для начала, добавим проверку, что если игрок вышел во время смерти, то его здоровье будет 100% и если помер в процессе игры, то сытость будет так же 100%.
-- привяжем изменение здоровья
local humanoid = character:FindFirstChild("Humanoid")
humanoid.HealthChanged:Connect(function()
local BD = player:FindFirstChild("DataBase")
if BD then -- игнорируем, если если БД ещё не загружена
local healthBD = BD:FindFirstChild("Health")
if healthBD then
healthBD.Value = humanoid.Health
if healthBD.Value == 0 then
healthBD.Value = 100
end
end
end
end)
-- проверка на то, что игрок помер во время игры
if player:FindFirstChild("BDloaded") then
local statuses = player:FindFirstChild("Statuses")
if statuses then
local satiety = statuses:FindFirstChild( "Satiety" )
if satiety then
-- восстановить значение сытости
satiety.Value = config.SatietyMaximum -- первичное значение
end
end
end
Ну и переходим к топику. Это меня бесит с самого начала, что нужно как-то хитро прицелиться/ударить, чтобы ресурс добыть. А значит Touch нашего мелкого инструмента, надо заменить на хитбокс.
HitBox — это некая область возле персонажа, которая наносит или получает урон. Первично, это было применено в файтингах.

Позже это стало применяться повсеместно.

В примерах выше — красная зона наносит повреждения врагам, а синяя зона воспринимает их. Таким образом, у нас надо проверить некую область перед игроком, во время удара, и записать в таблицу все объекты что в неё попали. После же определиться что получило урон, какой именно и что из этого дропнулось нам. С учётом анимации, нам нужно всё это просчитать на половине её воспроизведения.
С учётом того что у нас 3D пространство, а анимации мы используем дэфолтные, то нам нужно.. просто создать какую-то область перед персонажем и уже в ней проверять что в неё попало. Но для начала уберём «дребезг», т.е. множественную активацию инструмента при быстром нажатии левой кнопки, через глобальный debounce:
local debounce = true me.Activated:Connect(function() if debounce == true then debounce = false -- весь остальной скрипт debounce = true end
Естественно, что у нас теперь не будет функции реакции на столкновение инструмента с чем либо. Удаляем её. ХитБокс создадим впереди игрока (от его HumanoidRootPart) с заданным размером. Вопрос состоит в том, каким образом его создавать и конечно каким размером? Можно использовать Part, MeshPart и использовать GetPartsInPart или использовать сразу использовать функции нахождения партов в области: GetPartBoundsInRadius и GetPartBoundsInBox. Остаётся только вопрос размера области расчёта — примем её равной размеру самой модели персонажа (а вообще каждый может выделываться как хочет), а её размер можно получить с помощью функции GetExtentsSize. Кажется все моменты обозначены, а значит можно приступать к программированию.
Пока кодил вспомнил о другой функции GetBoundingBox — она вернёт не только размер, но и ориентацию модели! А ещё подумалось, если это вернёт полный размер, то нужно ли его смещать, чтобы понять куда ударили? Ведь, по идее, наш бокс будет такого размера чтобы персонаж с инструментом весь в него вписался? Ну конечно тут есть косячёк — сам персонаж тоже будет хитбоксом (например стоя в траве и махая за её пределами, мы таки будет её косить). Итак, скрипт у инструмента, с переписанной функцией атаки станет выглядеть так:
-- скрипт управления инструментом добычи ресурсов
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local me = script.Parent -- объект инструмента
local resource = ReplicatedStorage:WaitForChild("Resource") -- папка с конфигами
local config = require(resource:WaitForChild("Configure")) -- основной конфиг
local extraction = require(resource:WaitForChild("Extraction")) -- конфиг инструментов
local isType = me:WaitForChild("IsType").Value -- тип инструмента
local damage = me:WaitForChild("BaseDamage").Value -- полный урон
-- одели инструмент
me.Equipped:Connect(function()
-- print("одели")
end)
-- сняли инструмент
me.Unequipped:Connect(function()
-- print("сняли")
end)
local target = {} -- список объектов по которым попали
local function Attack()
-- Создадим HitBox и посчитаем что в него попало
local model = script.Parent.Parent -- будет указывать на персонажа
local centr = model.PrimaryPart -- это будет HRP персонажа
local orientation, size = model:GetBoundingBox()
-- без смещения
local overlapParams = OverlapParams.new()
overlapParams.FilterDescendantsInstances = model:GetDescendants()
overlapParams.FilterType = Enum.RaycastFilterType.Exclude
-- overlapParams.MaxParts = 1
local inBox = workspace:GetPartBoundsInBox(orientation, size, overlapParams)
table.clear(target) -- зачистим таблицу от предыдущих попаданий
for _, part in pairs(inBox) do
local hit = part.Parent
if hit:IsA("Model") then
if hit.Parent == workspace.Resource then -- расположено в ресурсах
if target[hit] == nil then
target[hit] = true
end
end
end
end
end
-- активировали инструмент
local debounce = true
me.Activated:Connect(function()
if debounce == true then
debounce = false
table.clear(target) -- зачищаем таблицу попаданий
-- запуск анимации
local anim = Instance.new("StringValue")
anim.Name = "toolanim"
anim.Value = "Slash"
anim.Parent = me
task.wait(0.5) -- ожидание середины анимации (а сколько же она длится?)
Attack()
warn(target)
-- будем определять нанесённый урон и полученные ресурсы
for object, b in pairs(target) do
local find = config[object.Name] -- нет в конфиге = нет урона
if find then -- есть в основном конфиге
-- читаем, задаём стартовые значения
local health = object:FindFirstChild("Health") -- осталось здоровья
if health == nil then -- ещё не наносили урон = нет переменных
local scale = object:GetScale() -- какой масштаб использован
local orient, size = object:GetBoundingBox() -- нам нужен только размер
-- создадим переменную здоровья в модели
health = Instance.new("NumberValue")
health.Name = "Health"
health.Value = 300 * scale -- 300 это базовое здоровье (лучше в конфиг задать)
health.Parent = object
-- аналогично создадим переменную объёма ресурса
local res = Instance.new("NumberValue")
res.Name = "Resource"
local size = (find.SizeMax - find.SizeMin)
local def = find.RessMin + (find.RessMax - find.RessMin) / size * (scale - find.SizeMin)
res.Value = def -- значение зависит от размера и заданных параметров
res.Parent = object
end
local res = object:FindFirstChild("Resource") -- максимум ресурсов на удар
print(object, health.Value, res.Value)
-- рассчитаем что получаем
local give = extraction[object.Name][isType]
-- print(isType, give)
for name, procent in pairs(give) do
-- полученные ресурсы
if name ~= "None" then
local val =math.floor( (procent/100) * res.Value * math.random() )
if val > 0 then
warn("Получено:", name, val)
local character = script.Parent.Parent -- персонаж
local player = Players:GetPlayerFromCharacter(character) -- игрок
local invent = player:FindFirstChild("Inventory")
if invent then -- инвентарь уже должен быть (это контрольный выстрел)
local saveRes = invent:FindFirstChild(name) -- ищем ресурс
if saveRes == nil then -- если ещё его не считали
saveRes = Instance.new("NumberValue") -- создаём новую запись
saveRes.Name = name
saveRes.Value = 0
saveRes.Parent = invent
end
saveRes.Value = saveRes.Value + val -- обновляем значение
end
end
end
end
-- теперь нанесём повреждание
local newHealth = health.Value - damage
if newHealth > 0 then
health.Value = newHealth
else
-- уничтожаем объект
object:Destroy()
end
end
end
anim:Destroy() -- удалить анимашку
debounce = true
end
end)
Главное не забыть — у нас два инструмента и значит два скрипта. Т.е. исправленный надо скопировать и во второй инструмент, заменив там исходный.
Я решил пока не показывать, как сместить от центра в сторону взгляда наш хитбокс. И как ему задать нужные параметры (ага, они будут разные у разных инструментов). Надо же дать вам хоть чуть-чуть самим подумать.
Ссылка на опубликованный плейс.
Ну и локальный архив прилагается: