Skip to content

生物实战案例 ​

如果你已经读完自定义基础生物和进阶生物开发,最好的下一步就是照着完整案例做一遍。本页给你 3 个偏实战的生物方案:巡逻幽灵、变体蘑菇兽、可骑乘运输兽。

阅读方式

建议你先完整跑通 1 个案例,再把其中的 identifier、贴图、掉落表和脚本逻辑替换成自己的玩法需求。下面所有示例都兼容中国版开发习惯:服务端使用 Python 2,动画扩展使用 query.mod.*,运行时状态同步优先使用 CreateModAttr。

案例一:巡逻幽灵 ​

这个案例适合学习 3 件事:

  • 用 minecraft:behavior.python_custom 做固定路线巡逻
  • 用 CreateModAttr 同步警戒状态
  • 用 query.mod.* 在客户端做半透明渐隐和警戒外观

效果目标 ​

  • 平时沿 4 个巡逻点来回移动
  • 玩家靠近时外观从淡化切到清晰
  • 进入战斗后切换为警戒贴图

BP:实体定义 ​

BP/entities/patrol_ghost.json

json
{
  "format_version": "1.10.0",
  "minecraft:entity": {
    "description": {
      "identifier": "mymod:patrol_ghost",
      "is_spawnable": true,
      "is_summonable": true,
      "is_experimental": false
    },
    "components": {
      "minecraft:type_family": {
        "family": ["ghost", "monster", "mob"]
      },
      "minecraft:collision_box": {
        "width": 0.8,
        "height": 1.9
      },
      "minecraft:health": {
        "value": 24,
        "max": 24
      },
      "minecraft:movement": {
        "value": 0.28
      },
      "minecraft:movement.basic": {},
      "minecraft:navigation.walk": {
        "can_walk": true,
        "avoid_water": true,
        "avoid_damage_blocks": true
      },
      "minecraft:physics": {},
      "minecraft:pushable": {
        "is_pushable": false,
        "is_pushable_by_piston": false
      },
      "minecraft:follow_range": {
        "value": 20,
        "max": 32
      },
      "minecraft:attack": {
        "damage": 5
      },
      "minecraft:behavior.python_custom:ghost_patrol": {
        "priority": 2,
        "control_flags": ["move", "look"]
      },
      "minecraft:behavior.nearest_attackable_target": {
        "priority": 1,
        "within_radius": 16,
        "entity_types": [
          {
            "filters": {
              "test": "is_family",
              "subject": "other",
              "value": "player"
            },
            "max_dist": 16
          }
        ]
      },
      "minecraft:behavior.melee_attack": {
        "priority": 0,
        "speed_multiplier": 1.15,
        "track_target": true
      },
      "minecraft:behavior.hurt_by_target": {
        "priority": 1
      },
      "minecraft:loot": {
        "table": "loot_tables/entities/patrol_ghost.json"
      }
    }
  }
}

RP:客户端实体 ​

RP/entity/patrol_ghost.entity.json

json
{
  "format_version": "1.10.0",
  "minecraft:client_entity": {
    "description": {
      "identifier": "mymod:patrol_ghost",
      "materials": {
        "default": "entity_alphatest"
      },
      "textures": {
        "fade_far": "textures/entity/patrol_ghost_fade_far",
        "fade_mid": "textures/entity/patrol_ghost_fade_mid",
        "normal": "textures/entity/patrol_ghost",
        "alert": "textures/entity/patrol_ghost_alert"
      },
      "geometry": {
        "default": "geometry.patrol_ghost"
      },
      "animations": {
        "idle": "animation.patrol_ghost.idle",
        "walk": "animation.patrol_ghost.walk",
        "alert": "animation.patrol_ghost.alert",
        "controller": "controller.animation.patrol_ghost"
      },
      "scripts": {
        "animate": ["controller"]
      },
      "render_controllers": [
        "controller.render.patrol_ghost"
      ],
      "spawn_egg": {
        "texture": "patrol_ghost_spawn_egg",
        "texture_index": 0
      }
    }
  }
}

RP/animation_controllers/patrol_ghost.ac.json

json
{
  "format_version": "1.10.0",
  "animation_controllers": {
    "controller.animation.patrol_ghost": {
      "initial_state": "default",
      "states": {
        "default": {
          "animations": [
            "idle",
            {
              "walk": "query.modified_move_speed > 0.01"
            }
          ],
          "transitions": [
            {
              "alert": "query.mod.alert > 0"
            }
          ]
        },
        "alert": {
          "animations": [
            "alert",
            {
              "walk": "query.modified_move_speed > 0.01"
            }
          ],
          "transitions": [
            {
              "default": "query.mod.alert <= 0"
            }
          ]
        }
      }
    }
  }
}

RP/render_controllers/patrol_ghost.rc.json

json
{
  "format_version": "1.10.0",
  "render_controllers": {
    "controller.render.patrol_ghost": {
      "arrays": {
        "textures": {
          "Array.fade": [
            "Texture.fade_far",
            "Texture.fade_mid",
            "Texture.normal"
          ]
        }
      },
      "geometry": "Geometry.default",
      "materials": [
        {
          "*": "Material.default"
        }
      ],
      "textures": [
        "query.mod.alert > 0 ? Texture.alert : Array.fade[query.mod.fade_level]"
      ]
    }
  }
}

Python:服务端巡逻与警戒状态 ​

behavior_pack/scripts/server/ghost_system.py

python
# -*- coding: utf-8 -*-
import mod.server.extraServerApi as serverApi

ServerSystem = serverApi.GetServerSystemCls()
CompFactory = serverApi.GetEngineCompFactory()


class GhostPatrolGoal(serverApi.GetCustomGoalCls()):

    def __init__(self, entityId, argsJson):
        super(GhostPatrolGoal, self).__init__(entityId, argsJson)
        self.entityId = entityId
        self.points = [
            (100, 64, 100),
            (108, 64, 100),
            (108, 64, 108),
            (100, 64, 108)
        ]
        self.index = 0

    def CanUse(self):
        actionComp = CompFactory.CreateAction(self.entityId)
        return not actionComp.GetAttackTarget()

    def CanContinueToUse(self):
        return self.CanUse()

    def Start(self):
        self.MoveNext()

    def Tick(self):
        posComp = CompFactory.CreatePos(self.entityId)
        pos = posComp.GetFootPos()
        target = self.points[self.index]
        dx = pos[0] - target[0]
        dz = pos[2] - target[2]
        if dx * dx + dz * dz <= 1.5:
            self.index = (self.index + 1) % len(self.points)
            self.MoveNext()

    def MoveNext(self):
        moveComp = CompFactory.CreateMoveTo(self.entityId)
        moveComp.SetMoveSetting(self.points[self.index], 1.0)


class GhostServerSystem(ServerSystem):

    def __init__(self, namespace, systemName):
        ServerSystem.__init__(self, namespace, systemName)
        serverApi.RegisterCustomGoal('ghost_patrol', GhostPatrolGoal)
        serverApi.AddEntityTickEventWhiteList('mymod:patrol_ghost')
        self.ListenForEvent(
            serverApi.GetEngineNamespace(),
            serverApi.GetEngineSystemName(),
            'EntityTickServerEvent',
            self,
            self.OnGhostTick
        )

    def OnGhostTick(self, args):
        entityId = args.get('entityId')
        if not entityId:
            return
        actionComp = CompFactory.CreateAction(entityId)
        targetId = actionComp.GetAttackTarget()
        modAttrComp = CompFactory.CreateModAttr(entityId)
        modAttrComp.SetAttr('ghost_alert', 1 if targetId else 0)

Python:客户端渐隐控制 ​

resource_pack/scripts/client/ghost_client_system.py

python
# -*- coding: utf-8 -*-
import mod.client.extraClientApi as clientApi

ClientSystem = clientApi.GetClientSystemCls()
CompFactory = clientApi.GetEngineCompFactory()


class GhostClientSystem(ClientSystem):

    def __init__(self, namespace, systemName):
        ClientSystem.__init__(self, namespace, systemName)
        self.ghosts = set()
        self.ListenForEvent(
            clientApi.GetEngineNamespace(),
            clientApi.GetEngineSystemName(),
            'AddEntityClientEvent',
            self,
            self.OnAddEntity
        )
        self.ListenForEvent(
            clientApi.GetEngineNamespace(),
            clientApi.GetEngineSystemName(),
            'OnScriptTickClient',
            self,
            self.OnScriptTickClient
        )

    def OnAddEntity(self, args):
        entityId = args.get('id')
        engineType = args.get('engineTypeStr')
        if engineType != 'mymod:patrol_ghost':
            return
        self.ghosts.add(entityId)
        queryComp = CompFactory.CreateQueryVariable(entityId)
        queryComp.Register('query.mod.fade_level', 2.0)
        queryComp.Register('query.mod.alert', 0.0)
        modAttrComp = CompFactory.CreateModAttr(entityId)
        modAttrComp.RegisterUpdateFunc('ghost_alert', self.OnGhostAlertChanged)

    def OnGhostAlertChanged(self, args):
        entityId = args.get('entityId')
        newValue = args.get('newValue', 0)
        queryComp = CompFactory.CreateQueryVariable(entityId)
        queryComp.Set('query.mod.alert', float(newValue))

    def OnScriptTickClient(self):
        playerId = clientApi.GetLocalPlayerId()
        playerPosComp = CompFactory.CreatePos(playerId)
        playerPos = playerPosComp.GetPos()
        for entityId in list(self.ghosts):
            posComp = CompFactory.CreatePos(entityId)
            ghostPos = posComp.GetPos()
            if not ghostPos:
                continue
            dx = ghostPos[0] - playerPos[0]
            dy = ghostPos[1] - playerPos[1]
            dz = ghostPos[2] - playerPos[2]
            distanceSq = dx * dx + dy * dy + dz * dz
            if distanceSq > 196:
                fadeLevel = 0.0
            elif distanceSq > 64:
                fadeLevel = 1.0
            else:
                fadeLevel = 2.0
            queryComp = CompFactory.CreateQueryVariable(entityId)
            queryComp.Set('query.mod.fade_level', fadeLevel)

测试步骤 ​

  1. 使用 /summon mymod:patrol_ghost 召唤幽灵。
  2. 观察幽灵是否按 4 个点循环巡逻;如果原地不动,优先检查路径点是否落在可寻路地面上。
  3. 从远处靠近,确认贴图会从淡化版本切到清晰版本。
  4. 进入仇恨范围后,确认 ghost_alert 被同步,客户端切到警戒贴图。

常见问题

如果你已经设置了 query.mod.fade_level,但外观没有变化,通常是客户端没有对该实体执行 Register('query.mod.xxx')。query.mod.* 必须先注册,再调用 Set。

案例二:变体蘑菇兽 ​

这个案例重点演示数据驱动变体:

  • 生成时随机红/棕两种体色
  • 进水后切换为潮湿状态
  • 潮湿状态叠加孢子覆盖层,并通过脚本播放粒子

效果目标 ​

  • 同一个实体只维护一套行为逻辑
  • query.variant 控制主体体色
  • query.mark_variant 控制额外覆盖层

BP:实体定义 ​

BP/entities/variant_moobloom.json

json
{
  "format_version": "1.10.0",
  "minecraft:entity": {
    "description": {
      "identifier": "mymod:variant_moobloom",
      "is_spawnable": true,
      "is_summonable": true,
      "is_experimental": false
    },
    "component_groups": {
      "mymod:red": {
        "minecraft:variant": {
          "value": 0
        }
      },
      "mymod:brown": {
        "minecraft:variant": {
          "value": 1
        }
      },
      "mymod:dry": {
        "minecraft:mark_variant": {
          "value": 0
        }
      },
      "mymod:wet": {
        "minecraft:mark_variant": {
          "value": 1
        },
        "minecraft:movement": {
          "value": 0.18
        }
      }
    },
    "components": {
      "minecraft:type_family": {
        "family": ["moobloom", "animal", "mob"]
      },
      "minecraft:health": {
        "value": 20,
        "max": 20
      },
      "minecraft:collision_box": {
        "width": 0.9,
        "height": 1.4
      },
      "minecraft:movement": {
        "value": 0.22
      },
      "minecraft:movement.basic": {},
      "minecraft:physics": {},
      "minecraft:navigation.walk": {
        "can_walk": true,
        "avoid_water": false,
        "avoid_damage_blocks": true
      },
      "minecraft:behavior.random_stroll": {
        "priority": 5,
        "speed_multiplier": 0.8
      },
      "minecraft:behavior.look_at_player": {
        "priority": 6,
        "look_distance": 8
      },
      "minecraft:loot": {
        "table": "loot_tables/entities/variant_moobloom.json"
      }
    },
    "events": {
      "minecraft:entity_spawned": {
        "sequence": [
          {
            "randomize": [
              {
                "weight": 1,
                "add": {
                  "component_groups": ["mymod:red"]
                }
              },
              {
                "weight": 1,
                "add": {
                  "component_groups": ["mymod:brown"]
                }
              }
            ]
          },
          {
            "add": {
              "component_groups": ["mymod:dry"]
            }
          }
        ]
      }
    }
  }
}

RP:客户端实体与渲染控制器 ​

RP/entity/variant_moobloom.entity.json

json
{
  "format_version": "1.10.0",
  "minecraft:client_entity": {
    "description": {
      "identifier": "mymod:variant_moobloom",
      "materials": {
        "default": "entity_alphatest"
      },
      "textures": {
        "red": "textures/entity/variant_moobloom_red",
        "brown": "textures/entity/variant_moobloom_brown",
        "dry_overlay": "textures/entity/variant_moobloom_overlay_dry",
        "wet_overlay": "textures/entity/variant_moobloom_overlay_wet"
      },
      "geometry": {
        "default": "geometry.variant_moobloom"
      },
      "render_controllers": [
        "controller.render.variant_moobloom"
      ]
    }
  }
}

RP/render_controllers/variant_moobloom.rc.json

json
{
  "format_version": "1.10.0",
  "render_controllers": {
    "controller.render.variant_moobloom": {
      "arrays": {
        "textures": {
          "Array.body": [
            "Texture.red",
            "Texture.brown"
          ],
          "Array.overlay": [
            "Texture.dry_overlay",
            "Texture.wet_overlay"
          ]
        }
      },
      "geometry": "Geometry.default",
      "materials": [
        {
          "*": "Material.default"
        }
      ],
      "textures": [
        "Array.body[query.variant]",
        "Array.overlay[query.mark_variant]"
      ]
    }
  }
}

Python:检测进水并切换潮湿状态 ​

behavior_pack/scripts/server/moobloom_system.py

python
# -*- coding: utf-8 -*-
import mod.server.extraServerApi as serverApi

ServerSystem = serverApi.GetServerSystemCls()
CompFactory = serverApi.GetEngineCompFactory()


class MoobloomServerSystem(ServerSystem):

    def __init__(self, namespace, systemName):
        ServerSystem.__init__(self, namespace, systemName)
        serverApi.AddEntityTickEventWhiteList('mymod:variant_moobloom')
        self.ListenForEvent(
            serverApi.GetEngineNamespace(),
            serverApi.GetEngineSystemName(),
            'EntityTickServerEvent',
            self,
            self.OnEntityTick
        )

    def OnEntityTick(self, args):
        entityId = args.get('entityId')
        if not entityId:
            return

        queryComp = CompFactory.CreateQueryVariable(entityId)
        inWaterResult = queryComp.EvalMolangExpression('query.in_water')
        inWater = bool(inWaterResult and inWaterResult.get('value', 0))

        eventComp = CompFactory.CreateEntityEvent(entityId)
        modAttrComp = CompFactory.CreateModAttr(entityId)

        if inWater:
            eventComp.RemoveActorComponentGroup('mymod:dry')
            eventComp.AddActorComponentGroup('mymod:wet')
            modAttrComp.SetAttr('spore_state', 1)
            self.EmitSporeParticle(entityId)
        else:
            eventComp.RemoveActorComponentGroup('mymod:wet')
            eventComp.AddActorComponentGroup('mymod:dry')
            modAttrComp.SetAttr('spore_state', 0)

    def EmitSporeParticle(self, entityId):
        posComp = CompFactory.CreatePos(entityId)
        pos = posComp.GetFootPos()
        if not pos:
            return
        cmdComp = CompFactory.CreateGame(serverApi.GetLevelId())
        cmdComp.SetCommand(
            '/particle mymod:moobloom_spore %.1f %.1f %.1f' % (
                pos[0], pos[1] + 1.0, pos[2]
            )
        )

测试步骤 ​

  1. 连续召唤多个 mymod:variant_moobloom,确认会随机出现红/棕两种体色。
  2. 把它推入水中,确认覆盖层切到 wet_overlay。
  3. 观察日志或粒子效果,确认进水时执行了 /particle mymod:moobloom_spore。
  4. 离水后确认 query.mark_variant 对应的覆盖层恢复为干燥版本。

设计建议

这个模式很适合做季节、地形、天气、成长阶段等外观切换。主体差异用 variant,附加状态用 mark_variant,通常最省文件数。

案例三:可骑乘运输兽 ​

这个案例适合做开放世界坐骑、商队单位或多人载具:

  • 1 个驾驶位 + 1 个乘客位
  • 运行时锁定骑乘者队列,避免下车后座位错位
  • 用 ExtraData 保存货物数量
  • 用 ModAttr 同步耐力值

效果目标 ​

  • 玩家可以骑乘并控制运输兽
  • 装货后切换为载货外观
  • 冲刺或持续移动时消耗耐力,休息时恢复
  • 耐力过低时进入疲劳状态并减速

BP:实体定义 ​

BP/entities/pack_beast.json

json
{
  "format_version": "1.10.0",
  "minecraft:entity": {
    "description": {
      "identifier": "mymod:pack_beast",
      "is_spawnable": true,
      "is_summonable": true,
      "is_experimental": false
    },
    "component_groups": {
      "mymod:normal": {
        "minecraft:movement": {
          "value": 0.24
        }
      },
      "mymod:tired": {
        "minecraft:movement": {
          "value": 0.14
        }
      },
      "mymod:empty_pack": {
        "minecraft:mark_variant": {
          "value": 0
        }
      },
      "mymod:loaded_pack": {
        "minecraft:mark_variant": {
          "value": 1
        }
      }
    },
    "components": {
      "minecraft:type_family": {
        "family": ["pack_beast", "mount", "mob"]
      },
      "minecraft:health": {
        "value": 36,
        "max": 36
      },
      "minecraft:collision_box": {
        "width": 1.2,
        "height": 1.8
      },
      "minecraft:physics": {},
      "minecraft:movement.basic": {},
      "minecraft:navigation.walk": {
        "can_walk": true,
        "avoid_water": true,
        "avoid_damage_blocks": true
      },
      "minecraft:rideable": {
        "seat_count": 2,
        "family_types": ["player"],
        "controlling_seat": 0,
        "crouching_skip_interact": true,
        "interact_text": "action.interact.mount",
        "rider_can_interact": false,
        "seats": [
          {
            "position": [0.0, 1.6, 0.2],
            "lock_rider_rotation": 90
          },
          {
            "position": [0.0, 1.6, -0.8],
            "lock_rider_rotation": 90
          }
        ]
      },
      "minecraft:behavior.random_stroll": {
        "priority": 5,
        "speed_multiplier": 0.8
      },
      "minecraft:behavior.look_at_player": {
        "priority": 6,
        "look_distance": 8
      }
    },
    "events": {
      "minecraft:entity_spawned": {
        "add": {
          "component_groups": ["mymod:normal", "mymod:empty_pack"]
        }
      }
    }
  }
}

RP:客户端实体与载货外观 ​

RP/entity/pack_beast.entity.json

json
{
  "format_version": "1.10.0",
  "minecraft:client_entity": {
    "description": {
      "identifier": "mymod:pack_beast",
      "materials": {
        "default": "entity_alphatest"
      },
      "textures": {
        "base": "textures/entity/pack_beast",
        "loaded": "textures/entity/pack_beast_loaded"
      },
      "geometry": {
        "default": "geometry.pack_beast"
      },
      "render_controllers": [
        "controller.render.pack_beast"
      ]
    }
  }
}

RP/render_controllers/pack_beast.rc.json

json
{
  "format_version": "1.10.0",
  "render_controllers": {
    "controller.render.pack_beast": {
      "arrays": {
        "textures": {
          "Array.pack": [
            "Texture.base",
            "Texture.loaded"
          ]
        }
      },
      "geometry": "Geometry.default",
      "materials": [
        {
          "*": "Material.default"
        }
      ],
      "textures": [
        "Array.pack[query.mark_variant]"
      ]
    }
  }
}

Python:骑乘、货物和耐力系统 ​

behavior_pack/scripts/server/pack_beast_system.py

python
# -*- coding: utf-8 -*-
import mod.server.extraServerApi as serverApi

ServerSystem = serverApi.GetServerSystemCls()
CompFactory = serverApi.GetEngineCompFactory()


class PackBeastServerSystem(ServerSystem):

    def __init__(self, namespace, systemName):
        ServerSystem.__init__(self, namespace, systemName)
        serverApi.AddEntityTickEventWhiteList('mymod:pack_beast')
        self.ListenForEvent(
            serverApi.GetEngineNamespace(),
            serverApi.GetEngineSystemName(),
            'AddEntityServerEvent',
            self,
            self.OnAddEntity
        )
        self.ListenForEvent(
            serverApi.GetEngineNamespace(),
            serverApi.GetEngineSystemName(),
            'EntityTickServerEvent',
            self,
            self.OnEntityTick
        )

    def OnAddEntity(self, args):
        entityId = args.get('id')
        engineType = args.get('engineTypeStr')
        if engineType != 'mymod:pack_beast':
            return

        rideComp = CompFactory.CreateRide(entityId)
        rideComp.SetEntityLockRider(True)

        exDataComp = CompFactory.CreateExtraData(entityId)
        exDataComp.SetExtraData('cargo_count', 0, False)
        exDataComp.SetExtraData('stamina', 100, False)
        exDataComp.SaveExtraData()

        modAttrComp = CompFactory.CreateModAttr(entityId)
        modAttrComp.SetAttr('pack_stamina', 100)

    def OnEntityTick(self, args):
        entityId = args.get('entityId')
        if not entityId:
            return

        exDataComp = CompFactory.CreateExtraData(entityId)
        stamina = exDataComp.GetExtraData('stamina')
        if stamina is None:
            stamina = 100

        rideComp = CompFactory.CreateRide(entityId)
        riders = rideComp.GetRiders() or []

        if riders:
            stamina = max(0, stamina - 1)
        else:
            stamina = min(100, stamina + 2)

        exDataComp.SetExtraData('stamina', stamina)

        eventComp = CompFactory.CreateEntityEvent(entityId)
        if stamina <= 20:
            eventComp.RemoveActorComponentGroup('mymod:normal')
            eventComp.AddActorComponentGroup('mymod:tired')
        else:
            eventComp.RemoveActorComponentGroup('mymod:tired')
            eventComp.AddActorComponentGroup('mymod:normal')

        modAttrComp = CompFactory.CreateModAttr(entityId)
        modAttrComp.SetAttr('pack_stamina', stamina)

    def LoadCargo(self, entityId, amount):
        exDataComp = CompFactory.CreateExtraData(entityId)
        cargoCount = exDataComp.GetExtraData('cargo_count') or 0
        cargoCount += amount
        exDataComp.SetExtraData('cargo_count', cargoCount)
        eventComp = CompFactory.CreateEntityEvent(entityId)
        eventComp.RemoveActorComponentGroup('mymod:empty_pack')
        eventComp.AddActorComponentGroup('mymod:loaded_pack')

    def UnloadCargo(self, entityId):
        exDataComp = CompFactory.CreateExtraData(entityId)
        exDataComp.SetExtraData('cargo_count', 0)
        eventComp = CompFactory.CreateEntityEvent(entityId)
        eventComp.RemoveActorComponentGroup('mymod:loaded_pack')
        eventComp.AddActorComponentGroup('mymod:empty_pack')

    def MountAsDriver(self, playerId, entityId):
        rideComp = CompFactory.CreateRide(entityId)
        rideComp.SetRiderRideEntity(playerId, entityId, 0)

    def MountAsPassenger(self, playerId, entityId):
        rideComp = CompFactory.CreateRide(entityId)
        rideComp.SetRiderRideEntity(playerId, entityId, 1)

测试步骤 ​

  1. 召唤 mymod:pack_beast,确认交互按钮可骑乘。
  2. 两名玩家依次上骑,确认第一位在驾驶位,第二位在乘客位。
  3. 让脚本调用 LoadCargo(entityId, 8),确认贴图切到载货版。
  4. 持续骑乘 5 到 10 秒,确认 pack_stamina 持续下降;低于 20 后移动速度应明显变慢。
  5. 下骑等待耐力恢复,再确认实体重新回到正常速度。

数据持久化

货物数量和耐力示例使用 ExtraData 保存,适合做存档持久化;如果你只关心实时同步到客户端,而不需要落盘,优先用 CreateModAttr 会更轻量。

你可以怎么改 ​

  • 把巡逻幽灵的路径点改成从配置文件或预设零件读取,做成地牢守卫
  • 把蘑菇兽的潮湿状态改成昼夜、群系或方块接触状态,扩展更多变体
  • 给运输兽补一个 UI 面板,把 cargo_count 和 pack_stamina 直接显示在界面上

下一步 ​