Menu
CoddyTech

בוט שרודף

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

עכשיו יריב. בוט הוא מודל שנמצא ב-ServerStorage.Bot, עם Humanoid, שמשוכפל אל המשטח האדום. המוח שלו הוא לולאה שרצה בתהליכון משלו: כל רבע שנייה, למצוא את השחקן החי הקרוב ביותר שהוא יכול לראות (בטווח של Settings.BotSight) וללכת לעברו באמצעות Humanoid:MoveTo.

while bot.Parent and humanoid.Health > 0 do
	local target = nearestCharacter(root.Position)
	if target then
		humanoid:MoveTo(target.HumanoidRootPart.Position)
	end
	task.wait(0.25)
end

מציאת השחקן הקרוב ביותר מתבצעת באמצעות הלולאה הקלאסית: מתחילים בלי אף אחד, כשמרחק הראייה הוא המרחק הטוב ביותר, וממשיכים עם כל מי שקרוב יותר.

Roblox מאפשר ללקוח של שחקן קרוב להריץ את הפיזיקה של מודל לא מעוגן, ואז קריאה מהשרת כגון ApplyImpulse עליו לא עושה דבר. המכה שלך מעיפה בוטים לאחור מהשרת, לכן השאר את הפיזיקה של כל בוט בשרת: bot.HumanoidRootPart:SetNetworkOwner(nil), אחרי שהבוט נמצא ב-workspace.

challenge icon

אתגר

קשה
  1. הוסף local bots = workspace.Bots.
  2. כתוב nearestCharacter(position): הדמות של השחקן החי הקרוב ביותר שנמצא בטווח Settings.BotSight, והמרחק אליו (או nil).
  3. כתוב runBot(bot): כל עוד הבוט חי, לך לעבר הדמות הקרובה ביותר, או עמוד במקום, והמתן 0.25 שניות.
  4. כתוב spawnBot(): שכפל את Bot בגובה 3 סטאדים מעל workspace.BotSpawn, הגדר את MaxHealth, את Health ואת WalkSpeed שלו לפי Settings, קבע את bots כהורה שלו, השאר את הפיזיקה שלו בשרת, דאג שסרגל הבריאות שלו יתעדכן בהתאם לבריאות שלו והפעל את runBot. צור בוט אחד.

לחץ על הפעל והתקדם לעבר המשטח האדום. לאחר מכן לחץ על בדיקה.

נסו בעצמכם

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)
	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, 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)
			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()
		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)
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

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