Roblox — ХитБокс — Hit Box

Автор: | 31 марта, 2025
Поделиться...

Настала пора немного прилизать, то что уже имеется. Для начала, добавим проверку, что если игрок вышел во время смерти, то его здоровье будет 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)

Главное не забыть — у нас два инструмента и значит два скрипта. Т.е. исправленный надо скопировать и во второй инструмент, заменив там исходный.

Я решил пока не показывать, как сместить от центра в сторону взгляда наш хитбокс. И как ему задать нужные параметры (ага, они будут разные у разных инструментов). Надо же дать вам хоть чуть-чуть самим подумать.

Ссылка на опубликованный плейс.

Ну и локальный архив прилагается:


Поделиться...

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *