Un robot apparaît
Leçon 16 sur 25 du cours Jeu Roblox : Arène de combat de Coddy.
Maintenant, un adversaire. Un bot est un modèle dans ServerStorage.Bot avec un Humanoid, cloné sur la plateforme rouge, avec ses valeurs provenant de Settings et une barre de santé comme celle du mannequin.
Roblox permet au client d'un joueur proche de gérer la physique d'un modèle non ancré, et un appel serveur comme ApplyImpulse sur celui-ci ne fait alors rien. Ton coup projette les bots vers l'arrière depuis le serveur ; garde donc la physique de chaque bot sur le serveur, une fois le bot dans l'espace de travail :
crate.Parent = workspace
crate:SetNetworkOwner(nil)Défi
DifficilePlace un bot dans l’arène.
- Ajoute
local bots = workspace.Bots. ÉcrisspawnBot(): cloneServerStorage.Bot3 studs au-dessus deworkspace.BotSpawn; définis MaxHealth et Health surSettings.BotHealth, et WalkSpeed surSettings.BotSpeed. - Définis son parent sur
bots, puis appellebot.HumanoidRootPart:SetNetworkOwner(nil). - Redimensionne
bot.HealthBar.Back.Filllors de HealthChanged, comme pour le mannequin. AppellespawnBot().
Appuie sur « Jouer » : un bot rouge se tient sur la plateforme rouge. Puis appuie sur « Vérifier ».
Essayez vous-même
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 -> moment de son dernier coup d’épée
local combo = {} -- player -> { nombre de coups consécutifs, moment où le dernier a touché }
-- Les animations d’épée de 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")
-- Un nombre qui flotte un instant au-dessus de la personne touchée
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
-- Tous les coups portés dans le jeu passent par ici
local function hurt(humanoid, amount)
if humanoid.Health <= 0 then
return
end
local character = humanoid.Parent
-- Une parade absorbe la majeure partie du coup
if character:GetAttribute("Blocking") then
amount *= Settings.BlockMultiplier
end
humanoid:TakeDamage(amount)
damageNumber(character, amount)
end
-- Un étourdissement immobilise quelqu’un pendant un instant
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
-- Un coup d’épée : l’animation se joue, puis toutes les personnes dans la zone devant vous sont touchées
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
-- Trois coups d’épée consécutifs forment un combo ; le troisième est une estocade
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()
-- La zone devant vous, jamais votre propre corps
local params = OverlapParams.new()
params.FilterDescendantsInstances = { character }
local box = root.CFrame * CFrame.new(0, 0, -3)
local hit = {} -- Chaque humanoid une seule fois par coup d’épée
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
-- Les capacités, par nom : ce que fait chacune
local abilities = {}
-- L’appareil du joueur déplace son personnage, donc le dash s’effectue dessus.
function abilities.Dash(player)
remotes.Ability:FireClient(player, "Dash")
end
-- Tous ceux qui t’entourent sont touchés et projetés loin de toi.
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
-- Le client demande une capacité par son nom. Le serveur vérifie qu’il s’agit d’une véritable
-- capacité, que le joueur est en vie et que son délai de récupération est écoulé. Le moment où elle sera de nouveau disponible
-- est un attribut du joueur, afin que son écran puisse l’afficher.
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)
-- Chaque joueur reçoit une épée à chacune de ses vies.
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)
-- Le mannequin d’entraînement : sa barre de vie suit sa santé et, une fois vaincu, le
-- mannequin réapparaît peu après sous forme d’une nouvelle copie au même endroit.
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)
-- Leçon 16 : Un bot apparaît se place ici.
Toutes les leçons de Jeu Roblox : Arène de combat
Entraînez-vous par vous-même : Playground Roblox