Menu
CoddyTech

ניקוד על חיסולים

שיעור 22 מתוך 25 בקורס משחק Roblox: זירות קרב של Coddy.

צריך להחשיב גם ניצחון על בוט. כשיצור דמוי אדם מת, שום דבר לא מציין מי גרם לכך, ולכן hurt זוכר את השחקן האחרון שפגע בכל יצור דמוי אדם. הנפה או הטחה של שחקן מעבירה את השחקן הזה בתור תוקף; פגיעה של בוט לא מעבירה אף אחד:

if attacker then
	lastHit[humanoid] = attacker
end

בעת האירוע Died של הבוט, הפוגע האחרון מקבל הריגה בטבלת הדירוג: תיקייה בשם leaderstats בתוך השחקן, שמכילה IntValue.

challenge icon

אתגר

קשה

ספרו הריגה לזכות מי שמביס בוט.

  1. כששחקן מצטרף, הוסיפו leaderstats, תיקייה שמכילה IntValue בשם Kills.
  2. הוסיפו local lastHit = {}; הפונקציה hurt(humanoid, amount, attacker) שומרת את lastHit[humanoid] = attacker; העבירו את player בקריאות ל־swing ול־slam.
  3. כשמתרחש Died של בוט, הוסיפו 1 ל־Kills של אותו שחקן אם הוא עדיין כאן; נקו את הרשומה.

לחצו על שחק והביסו בוט: הערך של Kills עולה בטבלת הדירוג. לאחר מכן לחצו על בדיקה.

נסו בעצמכם

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 = {} -- שחקן -> מתי הניף את החרב בפעם האחרונה
local combo = {} -- שחקן -> { פגיעות ברצף, מתי התרחשה הפגיעה האחרונה }

-- הנפשות החרב של 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)
	-- שיעור 22: Score Kills יתווסף כאן
	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)

-- הדמות הקרובה ביותר של שחקן חי שהבוט יכול לראות, והמרחק אליה
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()
		task.wait(1)
		bot:Destroy()
		task.wait(Settings.BotRespawn)
		spawnBot()
	end)
	task.spawn(runBot, bot)
end

for _ = 1, Settings.Bots do
	spawnBot()
end

כל השיעורים ביחידה משחק Roblox: זירות קרב

תרגלו בעצמכם: Roblox אונליין