I want to make the npc’s attack be able to get dodge by player by simply getting out of the way before the attack animation reach the hit event
as you can see currently if the attack animation started the npc’s attack will gurantee to hit the player and that in my book seem a little unfun to play against
I’m still new to these kind of stuff so if you find my script poorly written please give me a recommendation beside the main problem
script
local npc = script.Parent
local MaxDistance = 30
local OriginalPosition = npc.PrimaryPart.Position
local AttackRange = 5
local dmg = 1
local AttackAnim = script.Attack
local AttackTrack = npc.Humanoid:LoadAnimation(AttackAnim)
local Debounce = false
local function IsAttacking(closest_player)
if Debounce == true then return end
Debounce = true
npc.Humanoid.WalkSpeed = 0
AttackTrack:Play()
AttackTrack:GetMarkerReachedSignal("Hit"):Connect(function()
local player = closest_player
game:GetService("ReplicatedStorage").Event.Hit:FireClient(player, dmg)
end)
AttackTrack.Stopped:Connect(function()
task.delay(2, function()
npc.Humanoid.WalkSpeed = 10
Debounce = false
end)
end)
end
local function Attack()
local closest_player, closest_distance = nil, AttackRange
for i, player in pairs(game.Players:GetChildren()) do
local character = player.Character
if character then
local Humanoid = character:FindFirstChild("Humanoid")
if Humanoid.Health ~= 0.1 then
local distance = (npc.HumanoidRootPart.Position - character.HumanoidRootPart.Position).Magnitude
if distance < closest_distance then
closest_player = player
closest_distance = distance
end
end
end
end
if closest_player then
IsAttacking(closest_player)
end
end
local function Chasing()
local closest_player, closest_distance = nil, MaxDistance
for i, player in pairs(game.Players:GetChildren()) do
local character = player.Character
if character then
local Humanoid = character:FindFirstChild("Humanoid")
if Humanoid.Health ~= 0.1 then
local distance = (npc.HumanoidRootPart.Position - character.HumanoidRootPart.Position).Magnitude
if distance < closest_distance then
closest_player = player
closest_distance = distance
end
end
end
end
if closest_player then
npc.Humanoid:MoveTo(closest_player.Character.HumanoidRootPart.Position)
else
npc.Humanoid:MoveTo(OriginalPosition)
end
end
game:GetService("RunService").Stepped:Connect(function()
Chasing()
Attack()
end)
you could check if the arms are getting touched while the animation plays? So like something like this
local unloadedAnim = script.Parent.Animation
local LoadedAnim = script.Parent.Humanoid:LoadAnimation(unloadedAnim)
local function Attack()
--Do whatever blah blah
LoadedAnim:Play()
Script.Parent.LeftArm.Touched:Connect(function()
--make it deal damage and stuff
end)
end
not sure if u already managed to solve ur issue, but in case u haven’t:
i’d strongly suggest using hitboxes instead of checking the distance (personal preference)
u should define the main variables u plan on using at the top of ur script to reduce the repetitiveness and increase readability
using hitboxes after a short delay is also better because that lets u dodge quickly
this is the result:
the npc's script after the minor behavior tweaks
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--\\ bot stuff
local bot = script.Parent
local botNoid = bot:FindFirstChildWhichIsA("Humanoid")
local botRoot = bot.PrimaryPart
local botStats = { -- table of constants
SCAN_RANGE = 30,
ATTACK_DISTANCE = 7,
DEFAULT_WALK_SPEED = 11,
ATTACK_WALK_SPEED = 0,
HITBOX_SIZE = Vector3.new(4, 4, 6), -- hitbox size (for easy dodging)
ANIM_OFFSET_TIME = 0.2,
}
--\\ misc variables
local currentTime = 0
local refreshTime = 0.5
local debounce = false
local windupAnim = script:WaitForChild("windup-anim")
local attackAnim = script:WaitForChild("attack-anim")
local windupTrack = botNoid:LoadAnimation(windupAnim)
local attackTrack = botNoid:LoadAnimation(attackAnim)
--\\ utility functions
local function initialize()
botNoid.WalkSpeed = botStats.DEFAULT_WALK_SPEED
windupTrack.Priority = Enum.AnimationPriority.Action2
attackTrack.Priority = Enum.AnimationPriority.Action3
-- disabling humanoid states that i'm assuming are gonna remain unused
botNoid:SetStateEnabled(Enum.HumanoidStateType.Swimming, false)
botNoid:SetStateEnabled(Enum.HumanoidStateType.Seated, false)
botNoid:SetStateEnabled(Enum.HumanoidStateType.Physics, false)
botNoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
botNoid:SetStateEnabled(Enum.HumanoidStateType.Climbing, false)
botRoot:SetNetworkOwner(nil) -- keep exploiters from moving the npc around
end
local function getDistance(a: Vector3, b: Vector3): number
return (b - a).Magnitude
end
local function checkHit(victim: Player)
attackTrack:Play()
local plrChar = victim.Character
if not plrChar then return end
local victimRoot = plrChar.PrimaryPart
if not victimRoot then return end
local botPos = botRoot.Position
local plrPos = victimRoot.Position
local hitbox = Instance.new("Part")
hitbox.CFrame = botRoot.CFrame * CFrame.new(0, 0, -botStats.HITBOX_SIZE.Z / 1.6) -- forward offset
hitbox.Anchored = true
hitbox.Material = Enum.Material.Neon
hitbox.Transparency = 0.8
hitbox.Color = Color3.fromRGB(156, 75, 255)
hitbox.CanCollide = false
hitbox.Name = `hitbox`
hitbox.Size = botStats.HITBOX_SIZE
hitbox.Parent = workspace
hitbox.Touched:Connect(function(hit)
if hit and hit:IsDescendantOf(plrChar) then
print(`breaking news: {victim} viciously mauled`)
-- now u can place ur event here and whatnot
end
end)
task.delay(0.5, function()
hitbox:Destroy()
end)
end
--\\ this is where the fun begins
function botMain()
local function performManhunt(): Player?
local closestPlayer = nil
local closestDistance = botStats.SCAN_RANGE
for _, player in Players:GetPlayers() do
local character = player.Character
if character then
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
local rootPart = character:FindFirstChild("HumanoidRootPart")
if humanoid and rootPart and humanoid.Health > 0 then
local distance = getDistance(botRoot.Position, rootPart.Position)
if distance < closestDistance then
closestPlayer = player
closestDistance = distance
end
end
end
end
return closestPlayer
end
local function attackVictim(victim: Player)
if victim and not debounce then
debounce = true
botNoid.WalkSpeed = botStats.ATTACK_WALK_SPEED
botNoid:MoveTo(botRoot.Position) -- HALT!
windupTrack:Play()
task.wait(windupTrack.Length + botStats.ANIM_OFFSET_TIME)
checkHit(victim)
task.delay(1, function()
botNoid.WalkSpeed = botStats.DEFAULT_WALK_SPEED
debounce = false
end)
end
end
local currentVictim = performManhunt()
if currentVictim then
local plrChar = currentVictim.Character
local victimRoot = plrChar.PrimaryPart
if getDistance(botRoot.Position, victimRoot.Position) <= botStats.ATTACK_DISTANCE then
attackVictim(currentVictim)
else
botNoid:MoveTo(victimRoot.Position)
end
end
end
--\\ run logic
RunService.Heartbeat:Connect(function(dt)
currentTime += dt
if currentTime >= refreshTime then
currentTime = 0 -- reset timer
botMain()
end
end)
initialize()