Hello, Roblox community! I have previously crafted a non-customizable RPG-style script. Now that I have rewritten the script, it is better than ever. If you would like to use this script, follow the directions below.
Instructions:
-
Add your desired enemy to the workspace.
-
Add a server-sided script to the enemy [Regular script]
-
Paste this code in the script and customize it.
-
Be sure that all of the enemy’s body parts are all UnAnchored and CanCollide is turned on.
The script:
--Script made my Elite_Remote. Have fun(:
local damage = 10 --The damage the enemy does to the player after the attackSpeed.
local attackSpeed = 2 --Wait time before the NPC attacks again.
local respawnTime = 5 -- Wait time before the enemy respawns.
local defaultWalkSpeed = 16 --The walk speed your enemy will be defaulted to.
local hostileDistance = 25 --The distance at which the enemy will start walking towards the player
local attackDistance = 7.5 --The distance the enemy can attack the player at.
local randomWalkEnabled = true --When a player is not around and this is true, the enemy will walk around randomly.
local randomWalkDistance = 10 --When a player is not around and randomWalkEnabled is true, it will pathfind this distance away from the enemy.
--Advanced Options below
local constantWhileLoopWaitTime = 0.2 --Wait time for the function run(). .2 is a good place for this.
local randomWalkWaitTime = 2 -- If random walk is enabled, it will wait this amount of seconds before repathfinding randomly
local displayDamageUI = true -- When the enemy is damaged, if this is true, there will be text displaying how much damage the enemy took. Look below for customization.
local damageDiaplayColor = BrickColor.Red() --When the enemy takes damage, this color, along with the text, will display how much damage the monster took.
local damageDisplayFont = Enum.Font.SciFi --When teh enemy takes damage, this font, along with the text, will display how much damage the monster took.
local attackType = { --I will add to this but for now, friendly playes one animation and melee is close-ranged.
melee = true;
ranged = false;
friendly = false;
}
local rangedWeapon-- = game.ServerStorage.Handle --Set this if you have the ranged attackType enabled. MAKE sure that this is a part, union, or a meshpart.
local rangedVelocity = 100 --How hard your enemy will throw your rangedWeapon, if you have ranged enabled.
local animations = { --Change the numbers of the strings below to change the animation or leave empty if you want no animation.
spawnAnim = "rbxassetid://"; --When the enemy spawns in, it will use thi animation.
attackAnim = "rbxassetid://180416148"; --The animation that the enmy uses when a player is close enough to attack.
friendlyAnim = "rbxassetid://180416148"; --The animation used when the enemy's attackType is friendly [Plays the animation constantly]
}
local sounds = {
spawnSound = "rbxassetid://574999280";
attackSound = "rbxassetid://574999020";
hurtSound = "rbxassetid://6443096290";
deathSound = "rbxassetid://131138848"
}
local changeStatsAfterDeath = { --Add the leaderstats name [ex Gold] and the value it gains [+5]. Leave empty if you don't have leaderstats.
--Cash = 10;
--Kills = 1;
}
local drops = { -- Add possible drops and chances to your drops. 1 Being guaranteed. If the drop is a tool, it will go into the players inventory, however, if it is not a tool, it will go onto the enemy's standing position.
--[game.ServerStorage.sword1] = 1;
}
--Customization ends here!
local path = game:GetService("PathfindingService"):CreatePath()
local playerThatKilled = ""
local humanoid = script.Parent.Humanoid
local humanoidRootPart = script.Parent.HumanoidRootPart
local npcClone = script.Parent:Clone()
local lastHealth = humanoid.MaxHealth
local isRandomWalking = false
local isFollowingPlayer = false
local function pathFind(pos)
path:ComputeAsync(humanoidRootPart.Position, pos)
for i,v in pairs(path:GetWaypoints()) do
humanoid:MoveTo(v.Position)
task.wait(0.2)
if i == #path:GetWaypoints() then
return true
end
end
end
local function randomWalk(d)
task.wait(randomWalkWaitTime)
if isRandomWalking then
else
local randomPos = Vector3.new(humanoidRootPart.Position.X + math.random(-d,d),0,humanoidRootPart.Position.Z + math.random(-d,d))
if pathFind(randomPos) then
isRandomWalking = false
end
end
end
function getCharacters()
local plrTable = {}
for _,plr in pairs(game.Players:GetChildren()) do
for _,plrObject in pairs(workspace:GetChildren()) do
if plr.Name == plrObject.Name then
table.insert(plrTable, plrObject)
end
end
end
return plrTable
end
local function animate(animType)
if not script.Parent:FindFirstChildWhichIsA("Animation") then
for name,ID in pairs(animations) do
local Anim = Instance.new("Animation", script.Parent)
Anim.Name = name
Anim.AnimationId = ID
end
end
for name,ID in pairs(animations) do
local anim = script.Parent:FindFirstChild(name)
if anim then
local loadedAnim = humanoid.Animator:LoadAnimation(anim)
loadedAnim:Play()
task.wait(loadedAnim.Length)
return true
end
end
end
local function playSound(sound)
if not script.Parent:FindFirstChildWhichIsA("Sound") then
for name,ID in pairs(sounds) do
local sound = Instance.new("Sound", script.Parent)
sound.Name = name
sound.SoundId = ID
end
end
local soundItem = script.Parent:FindFirstChild(sound)
if soundItem then
soundItem:Play()
end
return true
end
local function run()
if attackType.melee then
for _,char in pairs(getCharacters()) do
local distanceToEnemy = (humanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude
if distanceToEnemy <= hostileDistance and not isFollowingPlayer then
isRandomWalking = false
isFollowingPlayer = true
if pathFind(char.HumanoidRootPart.Position) then
isFollowingPlayer = false
end
end
if distanceToEnemy <= attackDistance then
if animate("attackAnim") and humanoid.Health > 0 then
playSound("attackSound")
if (humanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude < attackDistance then
char.Humanoid.Health -= damage
end
end
end
end
if randomWalkEnabled and isRandomWalking == false and not isFollowingPlayer then
randomWalk(randomWalkDistance)
end
end
if attackType.ranged then
for _, char in pairs(getCharacters()) do
local distanceToEnemy = (humanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude
if distanceToEnemy <= hostileDistance then
animate("attackAnim")
playSound("attackSound")
humanoid.WalkSpeed = 0
humanoidRootPart.CFrame = CFrame.lookAt(humanoidRootPart.Position, char.HumanoidRootPart.Position)
local weaponClone = rangedWeapon:Clone()
weaponClone.Parent = workspace
weaponClone.Position = humanoidRootPart.Position
weaponClone.CanCollide = false
weaponClone.Anchored = false
weaponClone.CFrame = CFrame.lookAt(humanoidRootPart.Position, char.HumanoidRootPart.Position)
weaponClone.Velocity = humanoidRootPart.CFrame.LookVector * rangedVelocity
else
humanoid.WalkSpeed = defaultWalkSpeed
end
end
if randomWalkEnabled and isRandomWalking == false then
randomWalk(randomWalkDistance)
end
end
if attackType.friendly and randomWalkEnabled then
while animate("friendlyAnim") do
animate("friendlyAnim")
end
end
end
humanoid.HealthChanged:Connect(function(currentHealth)
playSound("hurtSound")
if displayDamageUI then
local dps = lastHealth - currentHealth
lastHealth = currentHealth
local partParent = Instance.new("Part", workspace)
partParent.Anchored = true
partParent.Transparency = 1
partParent.CanCollide = false
partParent.Position = humanoidRootPart.Position + Vector3.new(math.random(-3,3),math.random(-3,3),math.random(-3,3))
local billboardGui = Instance.new("BillboardGui")
billboardGui.Size = UDim2.new(0, 100, 0, 100)
billboardGui.AlwaysOnTop = false
billboardGui.Enabled = true
billboardGui.Parent = partParent
local textLabel = Instance.new("TextLabel")
textLabel.Text = dps
textLabel.Size = UDim2.new(0, 100, 0, 50)
textLabel.TextScaled = true
textLabel.BackgroundTransparency = 1
textLabel.Font = damageDisplayFont
textLabel.Parent = billboardGui
textLabel.TextColor = damageDiaplayColor
game.Debris:AddItem(partParent, 1)
end
end)
humanoid.Died:Connect(function()
for statName,statValue in pairs(changeStatsAfterDeath) do
local plr = game.Players:FindFirstChild(playerThatKilled)
if plr then
plr.leaderstats[statName].Value += statValue
end
end
for dropItem, dropChance in pairs(drops) do
local plr = game.Players:FindFirstChild(playerThatKilled)
local chance = math.random(1, dropChance)
if plr and dropChance == 1 then
if dropChance == 1 then
local drop = dropItem:Clone()
drop.Parent = plr.Backpack
end
end
end
playSound("deathSound")
task.wait(respawnTime)
npcClone.Parent = workspace
script.Parent:Destroy()
end)
humanoidRootPart.Touched:Connect(function(hit)
if hit.Parent.Parent:IsA("Model") and game.Players:FindFirstChild(hit.Parent.Parent.Name) then
playerThatKilled = hit.Parent.Parent.Name
end
end)
animate("spawnAnim")
playSound("spawnSound")
while task.wait(constantWhileLoopWaitTime) do
run()
end
I will be modifiying this script based on what other people would like to see within the script. If you feels as if there is a unwanted feature or a feature that you feel should be added, please feel free to post your ideas down below.
You can also click here to try the script out if the instructions confuse you.
Customizable Enemy AI - Roblox
Changelog:
Change 1: Fixed the damage portion of the script.
Change 2: Added some features recommended by other people.
Change 3: Enchanced Directions.
Change 4: Changed wait()s to task.wait()s.
Change 5: Made the script more coherent and optimal.
Change 6: Added a GUI displaying the damage took.
Change 7: Major Update! Added range mode, sounds, and drops.
Change 8: Made melee mode less buggy, added damage billboard options,