Roblox · Анимации

Удар мечом по ключевому кадру

Меч: клик — взмах, и урон наносится в тот момент анимации, когда клинок проходит перед персонажем (метка Hit). Сервер сам проверяет, кто перед мечом, — урон честный.

Структура

Синим — что нужно создать.

StarterPack
└─ Sword (Tool)
   ├─ Handle (Part) — клинок; CanCollide выкл.
   │  └─ Swing (Sound) — свист
   ├─ Swing (Animation) — взмах; метка Hit — момент удара
   ├─ Hit (RemoteEvent) — клиент → сервер
   ├─ Configuration (Configuration) — Damage 25, Range 6, Cooldown 0.8, HitTime 0.25
   ├─ SwordLocal (LocalScript)
   └─ SwordServer (Script)

Шаги

  1. Сделать анимацию: Avatar → Rig Builder → R15 (или R6 — как у персонажей в игре: Game Settings → Avatar). Выдели риг → Avatar → Animation Editor → имя анимации → Create.
  2. В Animation Editor поставь курсор на кадр, где клинок перед персонажем → ПКМ по шкале → Add Animation Event (метка) → имя Hit. Приоритет Action. Готово — «⋯» → Publish to Roblox → Submit → скопируй ID. Публикуй с того же аккаунта (или группы), что и игру: чужая анимация в твоей игре не проиграется.
  3. Без своей анимации стоит стандартный взмах Roblox (522635514) — у него нет метки, удар идёт через HitTime секунд.
  4. Клиент: Activated → анимация → на метке Hit (или через HitTime) FireServer. Сервер: меч в руках, откат прошёл → ищет живых в коробке перед персонажем (GetPartBoundsInBox) и наносит урон по одному разу.
  5. Если метка в анимации позже HitTime — увеличь HitTime, иначе удар сработает раньше метки.

Скрипты

SwordLocal

Где: StarterPack → Sword · LocalScript
Зачем: Взмах и сигнал удара в нужный момент.

-- меч (Tool)
local tool = script.Parent
-- сигнал «удар» на сервер
local hitEvent = tool:WaitForChild("Hit")
-- анимация взмаха
local anim = tool:WaitForChild("Swing")
-- настройки
local config = tool:WaitForChild("Configuration")
-- загрузим, когда возьмём меч
local track = nil
-- идёт взмах
local busy = false

-- взяли меч
local function OnEquipped()
    -- Humanoid
    local humanoid = tool.Parent:FindFirstChildOfClass("Humanoid")
    -- анимация задана
    if humanoid and anim.AnimationId ~= "" then
        -- загружаем
        track = humanoid:WaitForChild("Animator"):LoadAnimation(anim)
        -- главнее ходьбы
        track.Priority = Enum.AnimationPriority.Action
    -- конец проверки
    end
-- конец функции
end
-- каждый раз, когда берут в руки
tool.Equipped:Connect(OnEquipped)
-- подобрали с земли: скрипт запустился уже в руках — Equipped был раньше
if tool.Parent and tool.Parent:FindFirstChildOfClass("Humanoid") then OnEquipped() end

-- клик с мечом в руках
tool.Activated:Connect(function()
    -- ещё машем
    if busy then return end
    -- начали
    busy = true
    -- время взмаха
    local start = os.clock()
    -- удар уже отправлен?
    local done = false
    -- отправить удар
    local function Hit()
        -- только один раз за взмах
        if done then return end
        -- отправили
        done = true
        -- сервер проверит и нанесёт урон
        hitEvent:FireServer()
    -- конец функции
    end
    -- анимация есть
    if track then
        -- метка Hit в анимации — момент удара
        local marker = track:GetMarkerReachedSignal("Hit"):Connect(Hit)
        -- взмах
        track:Play()
        -- метки нет — удар через HitTime
        task.delay(config.HitTime.Value, Hit)
        -- ждём конец взмаха
        track.Stopped:Wait()
        -- метка больше не нужна
        marker:Disconnect()
    -- анимации нет
    else
        -- просто ждём
        task.wait(config.HitTime.Value)
        -- удар
        Hit()
    -- конец проверки
    end
    -- откат до следующего взмаха
    task.wait(math.max(config.Cooldown.Value - (os.clock() - start), 0))
    -- можно снова
    busy = false
-- конец обработчика
end)

SwordServer

Где: StarterPack → Sword · Script
Зачем: Проверка и урон тем, кто перед мечом.

-- меч (Tool)
local tool = script.Parent
-- сигнал от игрока
local hitEvent = tool:WaitForChild("Hit")
-- урон, дальность, откат
local config = tool:WaitForChild("Configuration")
-- звук взмаха
local swingSound = tool:WaitForChild("Handle"):WaitForChild("Swing")
-- время прошлого удара каждого игрока
local last = {}

-- игрок ударил
hitEvent.OnServerEvent:Connect(function(player)
    -- его персонаж
    local character = player.Character
    -- меч должен быть у него в руках
    if not character or tool.Parent ~= character then return end
    -- слишком часто — не верим
    if os.clock() - (last[player] or 0) < config.Cooldown.Value - 0.1 then return end
    -- запоминаем
    last[player] = os.clock()
    -- центр персонажа
    local root = character:FindFirstChild("HumanoidRootPart")
    -- нет
    if not root then return end
    -- свист меча
    swingSound:Play()
    -- дальность удара
    local range = config.Range.Value
    -- коробка прямо перед персонажем
    local box = root.CFrame * CFrame.new(0, 0, -range / 2)
    -- настройки поиска
    local params = OverlapParams.new()
    -- не замечать список
    params.FilterType = Enum.RaycastFilterType.Exclude
    -- себя не бьём
    params.FilterDescendantsInstances = {character}
    -- кого уже ударили
    local hit = {}
    -- все детали в коробке
    for _, part in ipairs(workspace:GetPartBoundsInBox(box, Vector3.new(4, 5, range), params)) do
        -- модель детали
        local model = part:FindFirstAncestorOfClass("Model")
        -- живая?
        local humanoid = model and model:FindFirstChildOfClass("Humanoid")
        -- жива и ещё не ударена
        if humanoid and humanoid.Health > 0 and not hit[humanoid] then
            -- один удар — один урон
            hit[humanoid] = true
            -- урон
            humanoid:TakeDamage(config.Damage.Value)
        -- конец проверки
        end
    -- конец перебора
    end
-- конец обработчика
end)

Проверка

Запусти Play (или Run) и убедись:

Собрать меч одной командой