Bot tấn công
Bài học 18 trên 25 trong khóa học Trò chơi Roblox: Đấu trường của Coddy.
Một bot ở trong phạm vi Settings.BotRange quanh bạn sẽ dừng lại và đánh, gây Settings.BotDamage sát thương, tối đa một lần mỗi Settings.BotAttackCooldown, với hoạt ảnh chém trên Animator riêng.
Các đòn đánh của nó cũng đi qua hurt như đòn đánh của bạn, nên đòn đỡ (F) đã chặn được hầu hết chúng. Dùng một hàm (function) cho mọi đòn đánh sẽ rất hữu ích ở đây.
if time() - lastAttack >= Settings.BotAttackCooldown then
lastAttack = time()
hurt(target.Humanoid, Settings.BotDamage)
endThử thách
KhóKhiến bot đánh trả.
- Trong
runBot, thêmlocal lastAttack = 0. - Khi mục tiêu ở trong phạm vi
Settings.BotRange, hãy đứng yên; nếu đã quaSettings.BotAttackCooldownkể từlastAttack, hãy phát hoạt ảnh chém trên Animator của bot và gọihurt(target.Humanoid, Settings.BotDamage). - Nếu không, hãy đuổi theo như trước.
Nhấn “Chơi” và để bot tiếp cận bạn: máu của bạn giảm sau mỗi cú đánh. Sau đó nhấn “Kiểm tra”.
Tự làm thử
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Debris = game:GetService("Debris")
local Settings = require(game.ReplicatedStorage.Settings)
local remotes = game.ReplicatedStorage.Remotes
local bots = workspace.Bots
local lastSwing = {} -- player -> thời điểm vung kiếm gần nhất
local combo = {} -- player -> { số đòn đánh liên tiếp, thời điểm đòn cuối trúng mục tiêu }
-- Hoạt ảnh vung kiếm có sẵn của Roblox
local slash = Instance.new("Animation")
slash.AnimationId = "rbxassetid://522635514"
local lunge = Instance.new("Animation")
lunge.AnimationId = "rbxassetid://522638767"
print("Swords deal " .. Settings.SwordDamage .. " damage")
-- Một con số hiện phía trên người bị đánh trong chốc lát
local function damageNumber(character, amount)
local root = character:FindFirstChild("HumanoidRootPart")
if not root then
return
end
local tag = Instance.new("BillboardGui")
tag.Name = "Damage"
tag.Size = UDim2.fromOffset(60, 30)
tag.StudsOffset = Vector3.new(0, 4.5, 0)
tag.Adornee = root
local text = Instance.new("TextLabel")
text.Size = UDim2.fromScale(1, 1)
text.BackgroundTransparency = 1
text.TextColor3 = Color3.fromRGB(255, 220, 80)
text.TextScaled = true
text.Text = "-" .. math.round(amount)
text.Parent = tag
tag.Parent = character
Debris:AddItem(tag, 0.8)
end
-- Mọi đòn đánh trong trò chơi đều đi qua đây
local function hurt(humanoid, amount)
if humanoid.Health <= 0 then
return
end
local character = humanoid.Parent
-- Đỡ đòn giúp giảm phần lớn sát thương
if character:GetAttribute("Blocking") then
amount *= Settings.BlockMultiplier
end
humanoid:TakeDamage(amount)
damageNumber(character, amount)
end
-- Choáng khiến một người đứng yên trong chốc lát
local function stun(humanoid)
local character = humanoid.Parent
if character:GetAttribute("Stunned") then
return
end
character:SetAttribute("Stunned", true)
print(character.Name .. " is stunned")
local speed = humanoid.WalkSpeed
humanoid.WalkSpeed = 0
task.delay(Settings.StunTime, function()
if character.Parent then
humanoid.WalkSpeed = speed
character:SetAttribute("Stunned", nil)
end
end)
end
-- Một lần vung kiếm: phát hoạt ảnh, rồi đánh trúng mọi người trong vùng trước mặt bạn
local function swing(player)
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
if not root or character.Humanoid.Health <= 0 then
return
end
local now = time()
if lastSwing[player] and now - lastSwing[player] < Settings.SwingCooldown then
return
end
lastSwing[player] = now
-- Ba lần vung kiếm liên tiếp tạo thành combo; lần thứ ba là một cú đâm tới
local c = combo[player]
if c and now - c.last <= Settings.ComboWindow then
c.count = c.count % 3 + 1
else
c = { count = 1 }
combo[player] = c
end
c.last = now
local finisher = c.count == 3
character.Humanoid.Animator:LoadAnimation(finisher and lunge or slash):Play()
-- Vùng phía trước bạn, không bao gồm cơ thể bạn
local params = OverlapParams.new()
params.FilterDescendantsInstances = { character }
local box = root.CFrame * CFrame.new(0, 0, -3)
local hit = {} -- Mỗi humanoid chỉ trúng đòn một lần cho mỗi lần vung kiếm
for _, part in workspace:GetPartBoundsInBox(box, Settings.HitboxSize, params) do
local humanoid = part.Parent:FindFirstChildOfClass("Humanoid")
if humanoid and not hit[humanoid] then
hit[humanoid] = true
hurt(humanoid, Settings.ComboDamage[c.count])
if finisher then
stun(humanoid)
end
end
end
end
-- Các kỹ năng theo tên và tác dụng của từng kỹ năng
local abilities = {}
-- Thiết bị của người chơi điều khiển nhân vật, nên thao tác lướt diễn ra trên thiết bị đó
function abilities.Dash(player)
remotes.Ability:FireClient(player, "Dash")
end
-- Mọi người xung quanh bạn đều trúng đòn và bị hất ra xa
function abilities.Slam(player, character, root)
local params = OverlapParams.new()
params.FilterDescendantsInstances = { character }
local hit = {}
for _, part in workspace:GetPartBoundsInRadius(root.Position, Settings.SlamRadius, params) do
local humanoid = part.Parent:FindFirstChildOfClass("Humanoid")
local theirRoot = part.Parent:FindFirstChild("HumanoidRootPart")
if humanoid and theirRoot and not hit[humanoid] then
hit[humanoid] = true
hurt(humanoid, Settings.SlamDamage)
local away = (theirRoot.Position - root.Position) * Vector3.new(1, 0, 1)
if away.Magnitude > 0 then
away = away.Unit
end
theirRoot:ApplyImpulse((away * Settings.SlamKnockback + Vector3.new(0, 40, 0)) * theirRoot.AssemblyMass)
end
end
end
function abilities.Block(player, character)
character:SetAttribute("Blocking", true)
task.delay(Settings.BlockTime, function()
if character.Parent then
character:SetAttribute("Blocking", nil)
end
end)
end
-- Máy khách yêu cầu dùng kỹ năng theo tên. Máy chủ kiểm tra: kỹ năng có tồn tại,
-- người chơi còn sống và thời gian hồi chiêu đã hết. Thời điểm kỹ năng sẵn sàng trở lại
-- là một thuộc tính của người chơi, để màn hình của họ có thể hiển thị.
remotes.Ability.OnServerEvent:Connect(function(player, name)
if typeof(name) ~= "string" or not abilities[name] then
return
end
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
if not root or character.Humanoid.Health <= 0 then
return
end
local now = workspace:GetServerTimeNow()
if now < (player:GetAttribute(name .. "ReadyAt") or 0) then
return
end
player:SetAttribute(name .. "ReadyAt", now + Settings.Cooldowns[name])
abilities[name](player, character, root)
end)
-- Người chơi nhận một thanh kiếm mỗi khi nhân vật xuất hiện
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function()
local sword = ServerStorage.Sword:Clone()
sword.Parent = player.Backpack
sword.Activated:Connect(function()
swing(player)
end)
end)
end)
-- Hình nộm tập luyện: thanh máu thay đổi theo lượng máu, và khi bị hạ gục,
-- nó sẽ xuất hiện lại sau một lúc dưới dạng bản mới ở cùng vị trí
local function setupDummy(dummy)
local humanoid = dummy.Humanoid
humanoid.HealthChanged:Connect(function(health)
dummy.HealthBar.Back.Fill.Size = UDim2.fromScale(health / humanoid.MaxHealth, 1)
end)
humanoid.Died:Connect(function()
local spot = dummy:GetPivot()
task.wait(0.5)
dummy:Destroy()
task.wait(Settings.DummyRespawn)
local fresh = ServerStorage.Dummy:Clone()
fresh:PivotTo(spot)
fresh.Parent = workspace
setupDummy(fresh)
end)
end
setupDummy(workspace.Dummy)
-- Nhân vật còn sống gần nhất mà bot nhìn thấy, cùng khoảng cách đến nhân vật đó
local function nearestCharacter(position)
local best, bestDistance = nil, Settings.BotSight
for _, player in Players:GetPlayers() do
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
if root and character.Humanoid.Health > 0 then
local distance = (root.Position - position).Magnitude
if distance < bestDistance then
best, bestDistance = character, distance
end
end
end
return best, bestDistance
end
-- Cách bot suy nghĩ: cứ mỗi phần tư giây, nó quan sát xung quanh rồi quyết định
local function runBot(bot)
local humanoid = bot.Humanoid
local root = bot.HumanoidRootPart
while bot.Parent and humanoid.Health > 0 do
local target, distance = nearestCharacter(root.Position)
-- Bài học 18: Bot biết tấn công sẽ được thêm vào đây
if target then
humanoid:MoveTo(target.HumanoidRootPart.Position)
else
humanoid:MoveTo(root.Position)
end
task.wait(0.25)
end
end
-- Một bot mới trên bệ đỏ, với các chỉ số và thanh máu
local function spawnBot()
local bot = ServerStorage.Bot:Clone()
bot:PivotTo(workspace.BotSpawn.CFrame + Vector3.new(0, 3, 0))
bot.Humanoid.MaxHealth = Settings.BotHealth
bot.Humanoid.Health = Settings.BotHealth
bot.Humanoid.WalkSpeed = Settings.BotSpeed
bot.Parent = bots
-- Để máy chủ xử lý vật lý của bot, nhờ đó lực đẩy lùi từ cú đập có thể làm bot di chuyển
bot.HumanoidRootPart:SetNetworkOwner(nil)
bot.Humanoid.HealthChanged:Connect(function(health)
bot.HealthBar.Back.Fill.Size = UDim2.fromScale(health / bot.Humanoid.MaxHealth, 1)
end)
task.spawn(runBot, bot)
end
spawnBot()
Tất cả bài học trong Trò chơi Roblox: Đấu trường
Tự luyện tập: Playground Roblox