היכולת האולטימטיבית
שיעור 20 מתוך 20 בקורס משחק Roblox: זירות קרב של Coddy.
היכולת האחרונה: מתקפת על ב־R. כל פגיעה שאת/ה מצליח/ה להנחית מוסיפה Settings.UltimatePerHit למד, שהוא מאפיין של השחקן, עד Settings.UltimateMax; מד מלא מאפשר ל־R לפגוע בכל מי שנמצא בטווח Settings.UltimateRadius ולגרום לו נזק של Settings.UltimateDamage, ואז המד מתרוקן.
מתקפה אולטימטיבית כשהמד ריק לא אמורה לעשות דבר או לעלות דבר, לכן יכולת יכולה עכשיו לסרב באמצעות החזרת false, והמטפל מגדיר את זמן הצינון רק כשהיא לא החזירה זאת:
if abilities[name](player, character, root) ~= false then
player:SetAttribute(name .. "ReadyAt", now + Settings.Cooldowns[name])
endסרגל ה־Ultimate ב־HUD מציג את המד. זה כל המשחק: להילחם, ליצור שילובים, לחסום ולמלא את המד.
אתגר
קשה- התחל את מאפיין
Ultimateשל כל שחקן ב-0. - ב-
hurt, פגיעה עם תוקף מוסיפהSettings.UltimatePerHitלמד של התוקף, עד למקסימום שלSettings.UltimateMax. - הוסף את
abilities.Ultimate: החזר false אלא אם המד מלא; פגע בכל מי שנמצא בתוךSettings.UltimateRadiusפעם אחת (לא בך), עםSettings.UltimateDamage; ואז רוקן את המד (אחרי הפגיעות, מכיוון שגם הן ממלאות אותו). הגדר את זמן ההמתנה רק כאשר יכולת לא החזירה false. - ב-
BattleClient, קשר את"Ultimate"למקש R, ושנה את הגודל שלhud.Ultimate.Fillבהתאם למד.
לחץ על הפעל והילחם. לאחר מכן לחץ על בדוק.
נסו בעצמכם
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Debris = game:GetService("Debris")
local PathfindingService = game:GetService("PathfindingService")
local Settings = require(game.ReplicatedStorage.Settings)
local remotes = game.ReplicatedStorage.Remotes
local bots = workspace.Bots
local lastSwing = {} -- player -> מתי הניף את החרב בפעם האחרונה
local combo = {} -- player -> { מספר הפגיעות ברצף, מתי התרחשה הפגיעה האחרונה }
local lastHit = {} -- humanoid -> השחקן שפגע בו אחרון
-- אנימציות החרב של 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
-- כל פגיעה במשחק עוברת כאן (attacker: השחקן שפגע, או nil)
local function hurt(humanoid, amount, attacker)
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)
if attacker then
lastHit[humanoid] = attacker
end
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], player)
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, player)
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()
player:SetAttribute("Streak", 0)
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)
-- הדמות הקרובה ביותר של שחקן חי שהבוט יכול לראות, והמרחק אליה
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
-- לכו לעבר דמות: ישירות אליה כששום דבר לא חוסם את הדרך, או
-- אל הנקודה הבאה במסלול שעוקף את המכשול
local function walkToward(bot, target, path)
local root = bot.HumanoidRootPart
local goal = target.HumanoidRootPart.Position
local params = RaycastParams.new()
params.FilterDescendantsInstances = { bot }
local hit = workspace:Raycast(root.Position, goal - root.Position, params)
if not hit or hit.Instance:IsDescendantOf(target) then
bot.Humanoid:MoveTo(goal)
return
end
path:ComputeAsync(root.Position, goal)
local points = path:GetWaypoints()
if path.Status == Enum.PathStatus.Success and points[2] then
bot.Humanoid:MoveTo(points[2].Position)
end
end
-- אופן החשיבה של הבוט: כל רבע שנייה הוא מסתכל סביב ומחליט
local function runBot(bot)
local humanoid = bot.Humanoid
local root = bot.HumanoidRootPart
local lastAttack = 0
local path = PathfindingService:CreatePath()
while bot.Parent and humanoid.Health > 0 do
local target, distance = nearestCharacter(root.Position)
local state
if bot:GetAttribute("Stunned") then
state = "Stunned"
elseif humanoid.Health < humanoid.MaxHealth * Settings.RetreatAt then
state = "Retreat"
elseif not target then
state = "Idle"
elseif distance <= Settings.BotRange then
state = "Attack"
else
state = "Chase"
end
bot:SetAttribute("State", state)
if state == "Chase" then
walkToward(bot, target, path)
elseif state == "Attack" then
humanoid:MoveTo(root.Position)
if time() - lastAttack >= Settings.BotAttackCooldown then
lastAttack = time()
humanoid.Animator:LoadAnimation(slash):Play()
hurt(target.Humanoid, Settings.BotDamage)
end
elseif state == "Retreat" and target then
local away = (root.Position - target.HumanoidRootPart.Position) * Vector3.new(1, 0, 1)
if away.Magnitude > 0 then
humanoid:MoveTo(root.Position + away.Unit * 12)
end
else
humanoid:MoveTo(root.Position)
end
task.wait(0.25)
end
end
-- בוט חדש על המשטח האדום, עם הנתונים שלו ומד בריאות
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
-- יש להשאיר את הפיזיקה של הבוט בשרת, כדי שההדיפה של מתקפת ה-Slam תזיז אותו
bot.HumanoidRootPart:SetNetworkOwner(nil)
bot.Humanoid.HealthChanged:Connect(function(health)
bot.HealthBar.Back.Fill.Size = UDim2.fromScale(health / bot.Humanoid.MaxHealth, 1)
end)
bot.Humanoid.Died:Connect(function()
-- השחקן שפגע בו אחרון מקבל את הקרדיט על החיסול
local killer = lastHit[bot.Humanoid]
lastHit[bot.Humanoid] = nil
if killer and killer.Parent then
killer.leaderstats.Kills.Value += 1
killer:SetAttribute("Streak", (killer:GetAttribute("Streak") or 0) + 1)
end
task.wait(1)
bot:Destroy()
task.wait(Settings.BotRespawn)
spawnBot()
end)
task.spawn(runBot, bot)
end
for _ = 1, Settings.Bots do
spawnBot()
end
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.