Menu
CoddyTech

Schadenszahlen

Lektion 13 von 20 im Kurs Roblox-Spiel: Schlachtfelder von Coddy.

Ein Treffer sollte anzeigen, wie viel Schaden er verursacht hat: eine Zahl, die über dem Ziel erscheint und kurz darauf wieder verschwindet. Dafür wird ein BillboardGui mit einem TextLabel an der Wurzel des Ziels angebracht, und Debris entfernt es nach 0.8 Sekunden, ohne das Spiel aufzuhalten:

Debris:AddItem(tag, 0.8)

Es kommt in hurt, nach der Blockregel, damit ein geblockter Treffer den verursachten Schaden anzeigt. math.round sorgt für eine ganze Zahl.

challenge icon

Aufgabe

Mittel

Schreibe damageNumber(character, amount) oberhalb von hurt: ein BillboardGui namens Damage (60 mal 30, 4.5 Studs über der Root, an ihr befestigt), das ein TextLabel mit dem Inhalt -10 ("-" .. math.round(amount)) enthält, dem Character als Parent zugewiesen wird und nach 0.8 Sekunden von Debris entfernt wird. Rufe es in hurt auf, nachdem der Schaden verursacht wurde.

Drücke Play und dann Check.

Probier es selbst

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Settings = require(game.ReplicatedStorage.Settings)

local remotes = game.ReplicatedStorage.Remotes
local lastSwing = {} -- player -> Zeitpunkt des letzten Schlags
local combo = {} -- player -> { Treffer in Folge, Zeitpunkt des letzten Treffers }

-- Roblox-eigene Schwertanimationen
local slash = Instance.new("Animation")
slash.AnimationId = "rbxassetid://522635514"
local lunge = Instance.new("Animation")
lunge.AnimationId = "rbxassetid://522638767"

print("Swords deal " .. Settings.SwordDamage .. " damage")

-- Jeder Treffer im Spiel wird hier verarbeitet
local function hurt(humanoid, amount)
	if humanoid.Health <= 0 then
		return
	end
	local character = humanoid.Parent
	-- Ein Block fängt den Großteil des Treffers ab
	if character:GetAttribute("Blocking") then
		amount *= Settings.BlockMultiplier
	end
	humanoid:TakeDamage(amount)
end

-- Eine Betäubung macht jemanden für einen Moment bewegungsunfähig
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

-- Ein Schwertschlag: erst die Animation, dann werden alle im Bereich vor dir getroffen
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
	-- Drei Schwertschläge in Folge ergeben eine Kombo; der dritte ist ein Ausfallschritt
	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()
	-- Der Bereich vor dir, niemals dein eigener Körper
	local params = OverlapParams.new()
	params.FilterDescendantsInstances = { character }
	local box = root.CFrame * CFrame.new(0, 0, -3)
	local hit = {} -- Jeder humanoid wird pro Schwertschlag nur einmal getroffen
	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

-- Die Fähigkeiten nach Namen: was jede einzelne bewirkt
local abilities = {}

function abilities.Dash(player, character, root)
	root.AssemblyLinearVelocity = root.CFrame.LookVector * Settings.DashSpeed + Vector3.new(0, 15, 0)
end

-- Alle um dich herum werden getroffen und von dir weggeschleudert
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

-- Der Client fordert eine Fähigkeit anhand ihres Namens an. Der Server prüft: Ist es eine gültige
-- Fähigkeit, lebt der Spieler und ist die Abklingzeit abgelaufen? Wann sie wieder einsatzbereit ist,
-- steht in einem Attribut des Spielers, damit es auf seinem Bildschirm angezeigt werden kann.
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)

-- Jeder Spieler erhält Kills auf der Bestenliste und bei jedem neuen Leben ein Schwert
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)

-- Die Trainingspuppe: Ihre Lebensanzeige zeigt ihre Gesundheit an, und eine besiegte
-- Puppe erscheint nach kurzer Zeit als neue Kopie am selben Ort wieder
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)
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Roblox-Spiel: Schlachtfelder