ROBLOX — Добыча ресурсов — Resource extraction

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

В предыдущих частях мы создали мир и разместили в нём ресурсы. Пора начать их добывать. Для этого нам понадобится инструмент добычи. Но не один, а два. Первый будет рубящий, второй дробящий. Ведь камень особо не порубишь, а дерево наоборот не особо раздробишь. Вот так и получаем что нам нужны топор и кирка. Естественно возьмём уже готовые из тулбокса. И конечно переделаем скрипты их использования. (Выбрать что-то стоящее та ещё головная боль.)

Второй шаг, определить что мы получаем с ресурса и в каких.. пропорциях. Естественно исходя из используемого орудия труда. У нас уже есть Configure что отвечает за появление ресурсов. Создадим аналогичную таблицу для добычи ресов. Примерно такую, но в Роблоксе:

Но сперва мы должны определиться — что для нас главнее (удобнее): источник ресурсов, инструмент или же сами добываемые ресурсы? Я решил что это будут источники ресурсов. Потому что:

  • Источников ресурсов будет гораздо больше, чем инструментов
  • Самих ресурсов может быть слишком много
  • У источников ресурсов может не быть полного перечня инструментов для добычи
  • Источник ресурсов будет получать урон, если он присутствует в перечне

Это конечно не полный перечень доступного в натуральной игре, но пока что и этого будет достаточно. (Как минимум мы опустим такой критерий как величина урона в зависимости от инструмента и самого ресурса.) В итоге получаем следующий модуль конфигурации:

Ну вот, мы определились что, из чего и чем мы будем добывать. Теперь можно переходить к программированию инструментов. Хотя нет. Сперва нам нужно в наш инструмент добавить описание его типа (имена могут быть разные, а тип от этого не изменится) и базовую силу удара (поставим от балды — после исправим).

На примере кирки:

  • StringValue.Name = IsType
  • StringValue.Value = pickaxe
  • IntegerValue.Name = BaseDamage
  • IntegerValue.Value = 100

Мы звёзд с неба не хватаем, а потому будем делать всё сразу серверным скриптом (для пробы нам хватит). Пора ваять скрипт? Нет, сперва теория!

  • Первично инструмент попадает в Backpack находящийся в Player-е соответствующего игрока
  • При экипировке инструмента он переносится в персонажа игрока
  • При снятии инструмента он переносится обратно в Backpack
  • Кроме одеть/снять есть ещё вариант активации инструмента (для нас это — удар)
  • В инструменте есть обязательный элемент Handle, определяющий как он будет держаться в руке

Из этого выходит что наша болванка скрипта будет выглядеть так:

-- скрипт управления инструментом добычи ресурсов

local Players = game:GetService("Players")

local me = script.Parent			-- объект инструмента

local isType = me:WaitForChild("IsType").Value		-- тип инструмента
local damage = me:WaitForChild("BaseDamage").Value	-- полный урон
local handle = me:WaitForChild("Handle")			-- рукоять инструмента

-- одели инструмент
me.Equipped:Connect(function()
	print("одели")
end)

-- сняли инструмент
me.Unequipped:Connect(function()
	print("сняли")
end)

-- активировали инструмент
me.Activated:Connect(function()
	print("активировали")
end)

Если обратить внимание, то нам как бы одел/снял не особо то и нужны. В реальности они нужны для воспроизведения звуков и анимации соответствующего действия, а нам же это не важно. С учётом сказанного, активировать мы можем только в случае, если у нас уже одет тулс. Т.е. при активации нам надо лишь произвести анимацию атаки и проверить во что мы попали за время этой самой атаки.

Анимацию будем запускать дедовским методом — создание текстовой строки в инструменте с её надписью. И.. всё таки пусть у нас повреждения наносит не рукоять, наконечник инструмента. А чтобы его определить мы просто зададим PrimaryPart в нашем инструменте с указанием на этот самый наконечник. Таким образом нам не придётся заботится о названии наконечника. Добавляем функцию столкновения и переменную активности атаки:

local attack = false	-- активен ли режим атаки?
local function Attack(hit)
	if attack == true then
		print(hit.Parent)	-- в какую модель попали?
	end
end
handle.Touched:Connect(Attack)

-- активировали инструмент
me.Activated:Connect(function()
	print("активировали")
	
	attack = true	-- включаем режим атаки
	
	-- запуск анимации
	local anim = Instance.new("StringValue")
	anim.Name = "toolanim"
	anim.Value = "Slash" 
	anim.Parent = me
	
	task.wait(1)
	attack = false	-- выключаем режим атаки
end)

Ну что же, постучав по деревьям и камням (а так же по земле), мы находим что нам нужно дописать в функции атаки, чтобы она у нас реагировала только на попадание по нужным объектам. А ещё нам нужно определять, чтобы по одному и тому же объекту не попадали несколько раз…

А ещё.. нам нужно определить сколько всего ресурсов в источнике и его остаток «здоровья». И уничтожить объект если здоровье кончилось. Вот итоговый скрипт:

-- скрипт управления инструментом добычи ресурсов

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	-- полный урон
-- local handle = me:WaitForChild("Handle")			-- рукоять инструмента
local handle = me.PrimaryPart						-- что наносит урон

-- одели инструмент
me.Equipped:Connect(function()
	-- print("одели")
end)

-- сняли инструмент
me.Unequipped:Connect(function()
	-- print("сняли")
end)

local attack = false	-- активен ли режим атаки?
local target = {}		-- список объектов по которым попали
local function Attack(hit)
	if attack == true then
		-- print(hit.Parent)
		local model = hit.Parent	-- в какую модель попали?
		if model:IsA("Model") then	-- это модель?
			-- данная модель расположена в папке заспавненных ресурсов
			if model.Parent == workspace.Resource then
				if target[model] == nil then	-- ещё не фиксировали данную цель
					target[model] = true		-- фиксируем попадание
				end
			end
		end
	end
end
handle.Touched:Connect(Attack)

-- активировали инструмент
me.Activated:Connect(function()
	-- print("активировали")
	
	table.clear(target)	-- зачищаем таблицу попаданий
	attack = true	-- включаем режим атаки
	
	-- запуск анимации
	local anim = Instance.new("StringValue")
	anim.Name = "toolanim"
	anim.Value = "Slash" 
	anim.Parent = me
	
	task.wait(1)	-- ожидание конца анимации
	
	attack = false	-- выключаем режим атаки
	-- 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)
					end
				end
			end
			
			-- теперь нанесём повреждание
			local newHealth = health.Value - damage
			if newHealth > 0 then
				health.Value = newHealth
			else
				-- уничтожаем объект
				object:Destroy()
			end
		end
	end
end)

Внимательный читатель заметит, что наши конфиги имеют одинаковые блоки, а значит их можно объединить и избавиться от лишнего файла в игре.

Доработать (добавить) можно

  • Анимации достал, убрал инструмент
  • Анимации ударов под конкретный инструмент
  • Визуал получения повреждений
  • Озвучку удара, получение урона
  • Сохранение полученных ресурсов в инвентаре игрока
  • …придумайте сами…

Ну и собственно итоговый плейс:


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

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

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