В предыдущих частях мы создали мир и разместили в нём ресурсы. Пора начать их добывать. Для этого нам понадобится инструмент добычи. Но не один, а два. Первый будет рубящий, второй дробящий. Ведь камень особо не порубишь, а дерево наоборот не особо раздробишь. Вот так и получаем что нам нужны топор и кирка. Естественно возьмём уже готовые из тулбокса. И конечно переделаем скрипты их использования. (Выбрать что-то стоящее та ещё головная боль.)
Второй шаг, определить что мы получаем с ресурса и в каких.. пропорциях. Естественно исходя из используемого орудия труда. У нас уже есть 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)
Внимательный читатель заметит, что наши конфиги имеют одинаковые блоки, а значит их можно объединить и избавиться от лишнего файла в игре.
Доработать (добавить) можно
- Анимации достал, убрал инструмент
- Анимации ударов под конкретный инструмент
- Визуал получения повреждений
- Озвучку удара, получение урона
- Сохранение полученных ресурсов в инвентаре игрока
- …придумайте сами…
Ну и собственно итоговый плейс: