Bot đuổi theo
Bài học 17 trên 25 trong khóa học Trò chơi Roblox: Đấu trường của Coddy.
Bot cần có trí óc: một vòng lặp chạy trong luồng riêng, cứ mỗi phần tư giây lại tìm người chơi còn sống gần nhất mà nó nhìn thấy (trong phạm vi Settings.BotSight) và đi về phía người đó bằng Humanoid:MoveTo.
while bot.Parent and humanoid.Health > 0 do
local target = nearestCharacter(root.Position)
if target then
humanoid:MoveTo(target.HumanoidRootPart.Position)
end
task.wait(0.25)
endTìm người ở gần nhất là cách dùng vòng lặp kinh điển: bắt đầu với khoảng cách tốt nhất là phạm vi nhìn và chưa có mục tiêu nào, rồi tiếp tục giữ lại người ở gần hơn. task.spawn(runBot, bot) khởi chạy vòng lặp mà không dừng phần còn lại của tập lệnh.
Thử thách
KhóKhiến bot đi về phía bạn.
- Viết
nearestCharacter(position): tìm nhân vật của người chơi còn sống gần nhất trong phạm viSettings.BotSightvà khoảng cách đến nhân vật đó. - Viết
runBot(bot): khi bot còn sống, dùngMoveTođể đi đến phần gốc của nhân vật gần nhất (hoặc vị trí của chính bot), sau đó chạytask.wait(0.25). - Ở cuối
spawnBot:task.spawn(runBot, bot).
Nhấn Chơi và đi về phía bệ màu đỏ: bot sẽ tìm đến bạn. 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 gần nhất trúng đích }
-- Hoạt ảnh dùng 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")
-- Con số hiện phía trên người trúng đòn 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ục tiêu đứ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à đòn đâm
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 trước mặt 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 trong 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 động 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 một 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 kỹ năng đã hết. Thời điểm kỹ năng sẵn sàng trở lại
-- là một thuộc tính (attribute) của người chơi, để màn hình của họ có thể hiển thị thời điểm đó.
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)
-- Mỗi lần hồi sinh, người chơi nhận một thanh kiếm
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,
-- hình nộm sẽ xuất hiện lại sau chốc lát dưới dạng bản sao mới ở đúng vị trí cũ
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)
-- Bài học 17: Bot truy đuổi sẽ được thêm vào đây
-- Một bot mới xuất hiện 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ờ đó cú hất văng của đòn giáng có thể đẩy nó đi
bot.HumanoidRootPart:SetNetworkOwner(nil)
bot.Humanoid.HealthChanged:Connect(function(health)
bot.HealthBar.Back.Fill.Size = UDim2.fromScale(health / bot.Humanoid.MaxHealth, 1)
end)
end
spawnBot()
Tất cả bài học trong Trò chơi Roblox: Đấu trường
Tự luyện tập: Playground Roblox