בוט שרודף
שיעור 14 מתוך 20 בקורס משחק Roblox: זירות קרב של Coddy.
עכשיו יריב. בוט הוא מודל שנמצא ב-ServerStorage.Bot, עם Humanoid, שמשוכפל אל המשטח האדום. המוח שלו הוא לולאה שרצה בתהליכון משלו: כל רבע שנייה, למצוא את השחקן החי הקרוב ביותר שהוא יכול לראות (בטווח של Settings.BotSight) וללכת לעברו באמצעות 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)
endמציאת השחקן הקרוב ביותר מתבצעת באמצעות הלולאה הקלאסית: מתחילים בלי אף אחד, כשמרחק הראייה הוא המרחק הטוב ביותר, וממשיכים עם כל מי שקרוב יותר.
Roblox מאפשר ללקוח של שחקן קרוב להריץ את הפיזיקה של מודל לא מעוגן, ואז קריאה מהשרת כגון ApplyImpulse עליו לא עושה דבר. המכה שלך מעיפה בוטים לאחור מהשרת, לכן השאר את הפיזיקה של כל בוט בשרת: bot.HumanoidRootPart:SetNetworkOwner(nil), אחרי שהבוט נמצא ב-workspace.
אתגר
קשה- הוסף
local bots = workspace.Bots. - כתוב
nearestCharacter(position): הדמות של השחקן החי הקרוב ביותר שנמצא בטווחSettings.BotSight, והמרחק אליו (או nil). - כתוב
runBot(bot): כל עוד הבוט חי, לך לעבר הדמות הקרובה ביותר, או עמוד במקום, והמתן 0.25 שניות. - כתוב
spawnBot(): שכפל את Bot בגובה 3 סטאדים מעלworkspace.BotSpawn, הגדר את MaxHealth, את Health ואת WalkSpeed שלו לפי Settings, קבע אתbotsכהורה שלו, השאר את הפיזיקה שלו בשרת, דאג שסרגל הבריאות שלו יתעדכן בהתאם לבריאות שלו והפעל אתrunBot. צור בוט אחד.
לחץ על הפעל והתקדם לעבר המשטח האדום. לאחר מכן לחץ על בדיקה.
נסו בעצמכם
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 -> מתי הניף את החרב בפעם האחרונה
local combo = {} -- player -> { מספר פגיעות רצופות, מתי התרחשה הפגיעה האחרונה }
-- אנימציות החרב של 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")
-- מספר שמרחף לרגע מעל מי שנפגע
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
-- כל פגיעה במשחק עוברת כאן
local function hurt(humanoid, amount)
if humanoid.Health <= 0 then
return
end
local character = humanoid.Parent
-- חסימה סופגת את רוב עוצמת הפגיעה
if character:GetAttribute("Blocking") then
amount *= Settings.BlockMultiplier
end
humanoid:TakeDamage(amount)
damageNumber(character, amount)
end
-- הלם משתק דמות לרגע
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
-- הנפת חרב: תחילה האנימציה, ואז כל מי שנמצא באזור שלפניך נפגע
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
-- שלוש הנפות רצופות יוצרות קומבו; השלישית היא הסתערות
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()
-- האזור שלפניך, אך לעולם לא הגוף שלך
local params = OverlapParams.new()
params.FilterDescendantsInstances = { character }
local box = root.CFrame * CFrame.new(0, 0, -3)
local hit = {} -- כל humanoid פעם אחת בכל הנפה
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
-- היכולות לפי שם: מה כל אחת עושה
local abilities = {}
function abilities.Dash(player, character, root)
root.AssemblyLinearVelocity = root.CFrame.LookVector * Settings.DashSpeed + Vector3.new(0, 15, 0)
end
-- כל מי שסביבך נפגע ומועף ממך והלאה
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
-- הלקוח מבקש יכולת לפי שמה. השרת בודק שזו
-- יכולת אמיתית, שהשחקן חי ושתקופת ההמתנה שלה הסתיימה. המועד שבו היא תהיה מוכנה שוב
-- נשמר כמאפיין של השחקן, כדי שאפשר יהיה להציג אותו על המסך שלו.
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)
-- כל שחקן מקבל Kills בטבלת המובילים, וחרב בכל פעם שהוא נוצר מחדש
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)
-- בובת האימון: מד החיים שלה מתעדכן לפי מצב הבריאות שלה, ובובה שהובסה
-- חוזרת כעבור רגע כעותק חדש באותו המקום
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)
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.