Skip to content

06 重复种植与变异 ​

先让一块地能反复种(还原状态 + parent 的特性),再给果实加上变异——偶尔结出变异的。

你将做出什么 ​

两件事:一块地能反复种(种完还原、还能再种),以及偶尔结出一颗变异果实(带特效、分数翻倍)。

种 → 收走果实 → 再种;以及偶尔出现的变异果实

1. 先让田地能反复种:现在它是"一次性"的 ​

第 03 课结尾留了个问题:结果实之后 plant:Destroy() 把种子销毁了,所以一块地只能种一次。

要能反复种,就得改成"用完还原":

text
现在是: 种子 → 结果实 → 销毁种子        (一次性)
要改成: 种子 → 结果实 → 种子复位, 互动还回去 → 又能种

2. 先弄清一个特性:parent = nil ​

第 02 课就是这样防重复种的:

lua
plot:SetAttribute("种植互动", interact)     -- 先把它存起来
interact.Parent = nil                       -- 再把它从地块上"摘下来"

Parent = nil 是什么意思? 物件从场景树上被摘下来了——它不在场景里了(不会显示、不会响应),但它还在内存里、你手里还捏着它的引用。所以之后还能把它装回去:

lua
picked.Parent = plot        -- 装回地块下 → 互动按钮又出现了

记住这个用法:Parent = nil 是"收起来",不是"删掉"。想彻底不要了才用 :Destroy()。

父子关系与查找的完整讲法:专题「找到场景对象并管理父子关系」

两种"用完怎么办":销毁 vs 还原 ​

上一课的结果实结尾是销毁:

lua
plant:Destroy()        -- 种子没了, 地也就不能再种了

那是当时的选择:先把"一颗种子长出一颗果实"跑通。代价是这块地只能用一次。

要想反复种,就得把"销毁"换成"还原"——把用过的种子恢复成没种过的样子。两件事的差别就一句话:

做法种子互动按钮结果
销毁没了不还一块地只能种一次
还原复位(缩回原大、回地底)装回去一块地能反复种

3. 改 PlantGrow 的收尾:还原而不是销毁 ​

  • 文件:server/main.lua 操作:改 spawnFruit 的收尾(其余不动)
lua
local function spawnFruit(plant, plot)                 -- 参数顺序和第 03 课一致
    -- ① 用预设创建果实(第 03 课学的)
    local fruit = World:CreateAsset(FRUIT_PRESET)
    fruit[1].Position = plant.Position
    fruit[1].OnLiftedBegin:Connect(function(liftunit)
        fruit[1]:SetAttribute("持有者", liftunit)
        print("[农场][server] 果实被拿起:", liftunit)
    end)
    fruit[1].OnLiftedEnd:Connect(function()
        fruit[1]:SetAttribute("持有者", nil)
    end)

    -- ② 本课改动: 不再销毁, 而是把这块地还原成"能再种"
    local picked = plot:GetAttribute("种植互动")
    if picked then picked.Parent = plot end          -- 互动装回去(parent = nil 的逆操作)

    plant.Size     = Vector3.New(0.5, 1, 0.5)        -- 种子缩回原大小
    plant.Position = plant.Position + Vector3.New(0, -1, 0)   -- 回到地底
    plot:SetAttribute("stage", "empty")              -- 记一笔: 空地
    print("[农场][server] 果实长出来了 · 这块地可以再种")
end

(第 03 课的收尾原来是一行 plant:Destroy(),本课把它换掉——函数的参数和调用位置都不变。)

还原一共四步,正好把第 02 课摘掉的东西一件件放回去:

要还原的怎么还原
互动按钮从属性里取回来,Parent 装回地块
种子大小Size 改回 0.5, 1, 0.5
种子位置往下移 1 格,回到地底
地块状态属性写成 "empty"

4. 每次都一样,有点无聊:给它加个概率 ​

现在每颗果实都长一个样,而且每颗都一样值分——太稳了,没什么惊喜。

先加意外:偶尔结出不一样的果实。数值照旧进配置表:

  • 文件:common/config.lua(新建) 操作:写入并保存
lua
-- common/config.lua —— 全课程的数值都在这儿改
return {
    GROW_TIME    = 3,      -- 多久后结果实(第 03 课用的)
    MUTATE_RATE  = 5,      -- 突变率(百分比): 每 100 颗大约出 5 颗特殊果实
    MUTATE_MULT  = 3,      -- 特殊果实的分数倍率
}

服务端顶部加一行:local CFG = require("common.config"),然后在结果实的地方掷一次骰子:

配置与数据组织:专题「数据 · 配置 · 资产」

lua
        -- 掷一次骰子: 中了才给它加身特效
        local lucky = math.random(1, 100) <= CFG.MUTATE_RATE

骰子必须在服务端掷。 客户端掷的话,每个人看到的都不一样,算分时谁也说不清。

5. 中了之后,给果实加点效果 ​

效果不一定要换模型——可以给它挂一个特效。先到特效预设里挑一个看着好看的,用它的预设 id 来创建:

lua
local effect = World:CreateAsset("official://preset/6158")   -- 特效预设的 id(挑一个你喜欢的)
effect[1].Parent = fruit[1]                                   -- 挂在果实身上
effect[1].Scale  = Vector3.New(3, 3, 3)                       -- 放大一点, 更醒目

从特效预设里挑一个,复制它的 id

特效的特性:挂在谁身上,就跟着谁的位置。 所以把它挂到果实下(Parent = fruit[1]),果实掉到哪、被搬到哪,特效都跟着走——不需要每帧去同步位置。

特效与声音:专题「声音与特效」

把上面的骰子和特效接进 spawnFruit:

lua
    if lucky then
        fruit[1]:SetAttribute("变异", true)                  -- 记住它是特殊的
        local fx = World:CreateAsset("official://preset/6158")
        fx[1].Parent = fruit[1]
        fx[1].Scale  = Vector3.New(3, 3, 3)
        print("[农场][server] 变异果实!")
    end

变化的办法不止一种:除了挂特效,还能改它的大小(Scale)、换个颜色、或者换一套模型——只要看得出来不一样都行。想给更多惊喜,可以准备几种,随机挑一个。

第 05 课尚未给果实挂变异特效。本课把创建特效的代码放进 if lucky then … end,使它只在掷骰命中时出现。想让它每次都出现,可以把 MUTATE_RATE 临时调成 100。

6. 结算时按标记翻倍 ​

回到第 05 课的放置区物件脚本,先在顶部加入 local CFG = require("common.config"),再把原来的固定加分改为按标记结算(分数仍记在玩家属性上):

lua
    local gain = SCORE_PER_FRUIT
    if otherUnit:GetAttribute("变异") then
        gain = gain * CFG.MUTATE_MULT          -- 变异果实: 分数翻三倍
    end

    local score = (player:GetAttribute("score") or 0) + gain
    player:SetAttribute("score", score)        -- 分数记在玩家身上
    RemoteEvent.sell:FireClient(player, { score = gain })   -- 飘字显示"这次涨了多少"
text
结果实时掷骰 → 中了就挂特效 + 打"变异"标记 → 搬进放置区 → 按标记翻倍结算

为什么标记打在果实身上? 因为它描述的是这一颗果实——特效跟着它、分数也按它算;果实被收走,标记自然一起消失。这就是第 05 课那句判据:这份数据的生命周期跟谁走。

运行并核对 ​

  1. 把 MUTATE_RATE 临时改成 50,种几株 → 频繁出现带特效的果实;改回 5。
  2. 带特效的果实搬进放置区 → 分数跳一大截(普通 3 分,变异 9 分)。
  3. 掷骰命中的那一刻,日志里出现 [农场][server] 变异果实!。

现象 → 可能原因:

  • 概率改了没变化 → 改的是不是 common/config.lua;改完同步了吗
  • 变异搬进去分数没翻倍 → 果实的 变异 标记有没有打上;结算那行有没有读它
  • 特效不跟着果实走 → 特效的 Parent 有没有挂到果实上

拓展点 / 自己改一处 ​

  • 加第二档稀有度:1% 出"彩虹"(另一个特效 + 更大的倍率)
  • 还原那四步里,给"种子回地底"加个 0.2 秒的小动画(提示:TweenService)
  • 想想:如果忘了把互动装回去,这块地会怎样?(这正是模块一那句"Parent = nil 是收起来,不是删掉"的意义)

保存进度 ​

  • 下一步:正式内容到这里就结束了。想继续练手,看 07 拓展与回顾——里面有天气和一整片田两个可选练习。
  • 通过标准:还原四步到位、可反复种;变异判定在服务端;结算按标记翻倍。
  • 恢复兜底:互动被摘坏了就从属性里拖回地块下;数值改乱就把 common/config.lua 恢复成课文里的版本。

API:Unit、AssetService、World。