Roblox Studio · 第 19 课

属性与任务:收集三颗能量晶石

用 Attributes 记录每位玩家的收集进度,让任务目标和奖励规则清楚可查。

10–15 岁 75 分钟 Attributes / 任务状态 动手实践 测试与保存

Course overview

三颗晶石,一个任务,每个人有自己的进度。

金币适合反复收集,任务物品则通常只计算一次。这次你要做一个明确的目标:找到三颗不同的能量晶石。

Attributes 像贴在对象上的小标签。我们在 Player 上记录 Crystals 和 QuestComplete,让进度既容易读取,也容易在 Properties 中检查。

01 · BEFORE

开始之前

理解 table、Player 和服务器脚本。新建 Lesson19,不依赖旧金币系统。

02 · MAKE

你的作品

三颗每人各可收集一次的晶石,集齐后将 QuestComplete 设为 true。

03 · CHECK

完成标志

反复碰同一颗不加进度;三个不同晶石才完成;两人互不抢走任务物品。

Properties 中 Attributes 区域的官方截图,属性名为官方示例
图 19-1:官方 Attributes 属性区参考:本课创建的是 Crystals 与 QuestComplete,请以代码里的名称为准。Roblox Creator Documentation · CC BY 4.0 · 原图未修改 · 许可说明

Learning route

先准备,再动手;看到结果后,记得保存。

每完成一小步,就测试一次。先说出你预计会看到什么,再用 Play 和 Output 检查,不用急着一次写完所有代码。

1

01 · 用 Folder 把任务物品放一起

建立一个只装晶石 Part 的文件夹,脚本就能一次找到它们。不要把灯、路标和脚本也放进去让程序误认。

2

02 · 先认清两个进度标签

Crystals 保存已收集数量,QuestComplete 保存是否完成。SetAttribute 设置一个值,GetAttribute 读取它;它们和内置的 Position、Color 不同,是自己定义的标签。

3

03 · 用“玩家 → 已领晶石”表防止重复

collected[player] 是这个玩家的领取表,里面用 crystal 对象作键。重复触碰同一对象会马上返回,而碰到另一颗会继续处理。

4

04 · 看得见的物体,不一定还可领取

同一颗晶石对 A 可能已领取,对 B 却仍然可领取,所以共享外观不能简单代表所有人的任务状态。

5

05 · 检查“不同三颗”的含义

任务完成不是碰三次,而是碰到三颗不同晶石。先把这句话写下来,再检查代码是否遵守。

Step by step

01 · 用 Folder 把任务物品放一起

建立一个只装晶石 Part 的文件夹,脚本就能一次找到它们。不要把灯、路标和脚本也放进去让程序误认。

为了简单,本课保持晶石可见。每位玩家是否收集过,由服务器的记录判断,不通过全局隐藏晶石来决定。

  1. 在 Workspace 创建 Folder:Crystals。
  2. 在里面创建 CrystalA、CrystalB、CrystalC,Size=3,3,3,Position 分别为 -10,3,-10;0,3,-20;10,3,-10。
  3. 每颗晶石 Anchored=true、CanCollide=false、CanTouch=true;颜色各不相同。
  4. 在 ServerScriptService 创建 CrystalQuest。
Explorer 中文件夹与模型层级的官方截图
图 19-2:官方 Explorer 层级参考:这里示范容器与子对象;本课的 Crystals 文件夹放在 Workspace 下。Roblox Creator Documentation · CC BY 4.0 · 原图未修改 · 许可说明

Step by step

02 · 先认清两个进度标签

Crystals 保存已收集数量,QuestComplete 保存是否完成。SetAttribute 设置一个值,GetAttribute 读取它;它们和内置的 Position、Color 不同,是自己定义的标签。

收集记录必须由服务器修改。客户端以后可以用 GetAttributeChangedSignal 更新 HUD,但不能把自己改出来的数字当作真实任务进度。

  1. 在 Play 的服务器视角选择 Player,找到 Properties 下的 Attributes。
  2. 开始时应看到 Crystals=0、QuestComplete=false。

Step by step

03 · 用“玩家 → 已领晶石”表防止重复

collected[player] 是这个玩家的领取表,里面用 crystal 对象作键。重复触碰同一对象会马上返回,而碰到另一颗会继续处理。

所有判断都在增加进度之前完成。距离检查给小型晶石留出 8 studs 范围;改成巨大物体时,需要重新设计这个范围。

  1. 把完整代码放入 CrystalQuest。
  2. 按 A、A、B、C 的顺序触碰,预计进度为 1、1、2、3。
  3. 注意目标数量 TARGET 必须与本课计划一致,不把装饰算作任务物品。
ServerScriptService > CrystalQuest · 完整脚本Luau
local Players = game:GetService("Players")
local folder = workspace:WaitForChild("Crystals")
local TARGET = 3
local collected = {}
local function setup(player)
    collected[player] = {}
    player:SetAttribute("Crystals", 0)
    player:SetAttribute("QuestComplete", false)
end
Players.PlayerAdded:Connect(setup)
for _, player in ipairs(Players:GetPlayers()) do setup(player) end
Players.PlayerRemoving:Connect(function(player) collected[player] = nil end)

for _, crystal in ipairs(folder:GetChildren()) do
    if not crystal:IsA("BasePart") then continue end
    crystal.Touched:Connect(function(hit)
        local character = hit:FindFirstAncestorOfClass("Model")
        local player = character and Players:GetPlayerFromCharacter(character)
        local root = character and character:FindFirstChild("HumanoidRootPart")
        local humanoid = character and character:FindFirstChildOfClass("Humanoid")
        if not player or not root or not humanoid or humanoid.Health <= 0 then return end
        if (root.Position - crystal.Position).Magnitude > 8 then return end
        local record = collected[player]
        if not record or record[crystal] then return end
        record[crystal] = true
        local count = (player:GetAttribute("Crystals") or 0) + 1
        player:SetAttribute("Crystals", count)
        player:SetAttribute("QuestComplete", count >= TARGET)
        print(player.Name, "Crystals:", count, "/", TARGET)
    end)
end

Step by step

04 · 看得见的物体,不一定还可领取

同一颗晶石对 A 可能已领取,对 B 却仍然可领取,所以共享外观不能简单代表所有人的任务状态。

本课用 Output 与个人 Attribute 验证。综合项目会加入个人 HUD;若要让已领取晶石只对自己淡出,还需要客户端的个人显示逻辑。

  1. A 碰到 CrystalA 后,B 再碰同一颗,两人都应各得 1。
  2. A 重生后再碰 CrystalA,不应再加 1。
  3. Stop 后重新开始才会清空整个任务记录。

Step by step

05 · 检查“不同三颗”的含义

任务完成不是碰三次,而是碰到三颗不同晶石。先把这句话写下来,再检查代码是否遵守。

如果后来动态创建新晶石,这段启动时 GetChildren 的连接不会自动覆盖它们。本课物品在开始前就摆好;动态生成需要额外处理 ChildAdded。

  1. 连续触碰同一颗至少五次,进度仍为 1。
  2. 按不同顺序收集三颗,仍可完成。
  3. 确认没有其他脚本同时改 Crystals。

Homework

轮到你来改一改,做出自己的版本。

1 寻找顺序自由

设计三条不同路线,验证无论先找到哪颗都能完成。

2 清楚的任务说明

写上“收集三颗不同晶石”,避免让玩家误以为同一颗可刷三次。

3 选做:四颗任务

增加 CrystalD 并把 TARGET 改为 4,重新做重复领取测试。

完成标准:先在 Playtest 中验证作品,再点击 Stop,保存为“姓名缩写_Lesson19”或对应 Homework 副本;不要覆盖上一课的可运行版本。

Exit questions

先自己回答,再展开看看。

Crystals 是 Roblox 自带属性吗?

不是,是通过 SetAttribute 定义的自定义属性。

为什么不领取后直接 Destroy 晶石?

本课规定每人各有一份,服务器删除会让其他玩家也失去它。

什么防止同一颗反复计数?

collected[player][crystal] 记录了这个玩家已经领取过的对象。

运行后新增晶石会自动连接吗?

本课代码不会;它只在启动时遍历已有子对象。

Explore further

需要查资料时,从这里出发。

正文代码可以直接复制。先确认脚本类型、放置位置,以及这段代码是完整版本还是小实验;不要把同一脚本的多个版本叠加运行。界面参考图已标注来源,图中的对象名可能与本课不同,请以操作步骤为准。