Bot yang Mengejar
Pelajaran 14 dari 20 dalam kursus Game Roblox: Battlegrounds di Coddy.
Sekarang, lawan. Bot adalah model di ServerStorage.Bot dengan Humanoid, yang disalin ke bantalan merah. Pikiran bot adalah perulangan di thread-nya sendiri: setiap seperempat detik, cari pemain hidup terdekat yang bisa dilihatnya (dalam jarak Settings.BotSight) lalu berjalan ke arahnya dengan 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)
endMencari yang terdekat adalah perulangan klasik: mulai tanpa kandidat dan gunakan jarak pandang sebagai jarak terbaik, lalu terus pilih siapa pun yang lebih dekat.
Roblox memungkinkan client pemain di dekatnya menjalankan fisika model yang tidak ditambatkan, dan pemanggilan di server seperti ApplyImpulse pada model itu tidak akan melakukan apa pun. Serangan bantinganmu mendorong bot menjauh dari server, jadi pertahankan fisika setiap bot di server: bot.HumanoidRootPart:SetNetworkOwner(nil), setelah bot berada di workspace.
Tantangan
Sulit- Tambahkan
local bots = workspace.Bots. - Tulis
nearestCharacter(position): karakter pemain terdekat yang masih hidup dalam jarakSettings.BotSight, beserta jaraknya (atau nil). - Tulis
runBot(bot): selama bot masih hidup, berjalanlah menuju karakter terdekat atau diam, lalu tunggu 0.25 detik. - Tulis
spawnBot(): kloning Bot pada ketinggian 3 studs di atasworkspace.BotSpawn, atur MaxHealth, Health, dan WalkSpeed dari Settings, jadikanbotssebagai parent-nya, pertahankan fisikanya di server, buat bilah kesehatannya mengikuti kesehatannya, lalu mulairunBot. Munculkan satu bot.
Tekan Play dan berjalanlah menuju bantalan merah. Lalu tekan Check.
Coba sendiri
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 lastSwing = {} -- player -> waktu ayunan pedang terakhirnya
local combo = {} -- player -> { jumlah pukulan beruntun, waktu pukulan terakhir mengenai sasaran }
-- Animasi pedang bawaan 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")
-- Angka yang melayang sejenak di atas karakter yang terkena pukulan
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
-- Setiap pukulan dalam game diproses di sini
local function hurt(humanoid, amount)
if humanoid.Health <= 0 then
return
end
local character = humanoid.Parent
-- Menangkis mengurangi sebagian besar dampak pukulan
if character:GetAttribute("Blocking") then
amount *= Settings.BlockMultiplier
end
humanoid:TakeDamage(amount)
damageNumber(character, amount)
end
-- Efek stun membuat seseorang tidak bisa bergerak sejenak
local function stun(humanoid)
local character = humanoid.Parent
if character:GetAttribute("Stunned") then
return
end
character:SetAttribute("Stunned", true)
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
-- Ayunan pedang: animasi diputar, lalu semua orang dalam kotak di depanmu terkena pukulan
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
-- Tiga ayunan pedang beruntun membentuk combo. Ayunan ketiga adalah serangan menerjang
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()
-- Kotak di depanmu, tidak pernah mencakup tubuhmu sendiri
local params = OverlapParams.new()
params.FilterDescendantsInstances = { character }
local box = root.CFrame * CFrame.new(0, 0, -3)
local hit = {} -- Setiap humanoid hanya terkena sekali per ayunan
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
-- Kemampuan berdasarkan nama: tindakan masing-masing kemampuan
local abilities = {}
function abilities.Dash(player, character, root)
root.AssemblyLinearVelocity = root.CFrame.LookVector * Settings.DashSpeed + Vector3.new(0, 15, 0)
end
-- Semua orang di sekitarmu terkena serangan dan terpental menjauh darimu
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
-- client meminta kemampuan berdasarkan namanya. server memeriksa apakah kemampuannya valid,
-- pemainnya masih hidup, dan waktu tunggunya sudah berakhir. Waktu kemampuan itu siap lagi
-- disimpan sebagai atribut pemain agar bisa ditampilkan di layarnya.
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)
-- Setiap pemain mendapat Kills di papan peringkat dan pedang setiap kali hidup kembali
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local kills = Instance.new("IntValue")
kills.Name = "Kills"
kills.Parent = leaderstats
player.CharacterAdded:Connect(function()
local sword = ServerStorage.Sword:Clone()
sword.Parent = player.Backpack
sword.Activated:Connect(function()
swing(player)
end)
end)
end)
-- Boneka latihan: bilah kesehatannya mengikuti kesehatannya, dan boneka yang dikalahkan
-- muncul kembali setelah beberapa saat sebagai salinan baru di tempat yang sama
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)
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Game Roblox: Battlegrounds
Latihan sendiri: Playground Roblox