Настала пора немного прилизать, то что уже имеется. Для начала, добавим проверку, что если игрок вышел во время смерти, то его здоровье будет 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)
Главное не забыть — у нас два инструмента и значит два скрипта. Т.е. исправленный надо скопировать и во второй инструмент, заменив там исходный.
Я решил пока не показывать, как сместить от центра в сторону взгляда наш хитбокс. И как ему задать нужные параметры (ага, они будут разные у разных инструментов). Надо же дать вам хоть чуть-чуть самим подумать.
Ссылка на опубликованный плейс.
Ну и локальный архив прилагается: