ظهور روبوت
الدرس 16 من 25 في دورة لعبة Roblox: ساحة القتال على Coddy.
والآن ننتقل إلى خصم. الروبوت هو نموذج في ServerStorage.Bot يحتوي على كائن Humanoid، ويُنسخ إلى المنصة الحمراء، وتُضبط أرقامه وفقًا لـ Settings، ويكون له شريط صحة مثل الدمية.
يتيح Roblox لعميل لاعب قريب تشغيل فيزياء نموذج غير مثبت، وعندها لا يفعل استدعاء من الخادم مثل ApplyImpulse أي شيء. لذا فإن ضربة الإطاحة التي توجهها تُبعد الروبوتات عن الخادم، ولذلك أبقِ فيزياء كل روبوت على الخادم، بعد أن يصبح الروبوت في مساحة العمل:
crate.Parent = workspace
crate:SetNetworkOwner(nil)التحدي
صعبضع روبوتًا في ساحة القتال.
- أضف
local bots = workspace.Bots. اكتبspawnBot(): استنسخServerStorage.Botعلى ارتفاع 3 وحدات فوقworkspace.BotSpawn؛ واجعل MaxHealth وHealth مساويين لـSettings.BotHealth، وWalkSpeed مساويًا لـSettings.BotSpeed. - اجعله تابعًا لـ
bots، ثم نفّذbot.HumanoidRootPart:SetNetworkOwner(nil). - غيّر حجم
bot.HealthBar.Back.Fillعند حدوث HealthChanged، كما فعلت مع الدمية. استدعِspawnBot().
اضغط «العب»: يقف روبوت أحمر على المنصة الحمراء. ثم اضغط «تحقق».
جرّب بنفسك
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)
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
-- ضربة بالسيف: تبدأ الحركة، ثم يُصاب كل من داخل المنطقة أمامك
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)
remotes.Ability:FireClient(player, "Dash")
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)
-- يحصل كل لاعب على سيف في كل مرة تعود فيها شخصيته إلى الحياة
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)
-- دمية التدريب: يتغير شريط صحتها تبعًا لصحتها، وإذا هُزمت
-- تعود بعد لحظة، بنسخة جديدة في المكان نفسه
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)
-- الدرس 16: ظهور روبوت يوضع هنا
جميع دروس لعبة Roblox: ساحة القتال
تدرّب بنفسك: Playground لـ Roblox