Files
netease-modsdk-wiki/docs/mconline/60-我的世界创造营教程/SDK大师教程/1-惊变系统/3-怪物生成.md
boybook 760c2dd9ad 2.6
2025-12-01 20:59:16 +08:00

249 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 怪物生成
> 温馨提示:开始阅读这篇指南之前,我们希望你对《我的世界》基岩版附加包有一定了解,有能力撰写 JSON 数据格式,对 Python 进行模组开发有了解,并能够独立阅读《我的世界》开发者官网-开发指南或其他技术引用文档。
本文将带你了解生物生成的规则。
在本教程中,您将学习以下内容。
- ✅实体生成规则;
- ✅如何使用代码生成自定义实体;
## 实体生成规则
在原版行为包下的 `spawn_rules` 目录下,定义了许多实体的生成规则,也就是如何生成实体在世界中。当你希望自定义实体自然生成时,你就需要使用生成规则。不同的组件允许可以定义实体不同的生成时间、位置和方式。
一般来说,我们都会选择与原版实体非常相似的方式来生成。比如,像牛一样生成在牛群中、像僵尸一样出生在晚上,或者像鱼一样只出生在水里。
### 基础结构
当你要使用原版的生成规则时,首先,需要在行为包的 `spawn_rules` 目录下新建一个 `identifier.json` (这里的 `identifier` 代指生物标识符)的新文件。该文件的内容应该如下所示:
```json
{
"format_version": "1.8.0",
"minecraft:spawn_rules": {
"description": {
"identifier": "tutiroal:identifier",
"population_control": "animal"
},
"conditions": []
}
}
```
`minecraft:spawn_rules` 内部,我们需要考虑两件事:**人口控制**和**条件**。
`description` description 定义文件的基本属性。 `identifier` identifier 应该匹配我们实体的标识符。 `population_control` population_control 定义游戏如何知道要生成多少生物,并且稍微复杂一些。
### 人口控制
我的世界有不同的实体池。当此处定义的池被认为已满时,游戏将不再生成该池中的生物。 共有以下几种不同的选择(一般我们使用前面三种即可):
- **"animal"**:被动生物,如牛和猪
- **"water_animal"**:热带鱼、海豚等水生生物
- **"monster"**:敌对生物,如骷髅和僵尸
- **"villager"**:村民专属。
- **"ambient"**:蝙蝠专属。
- **"cat"**:猫专属。
- **"pillager"**:掠夺者专属。
### 条件
`conditions` 是一系列允许生物在世界中生成的可能条件。 每个条件分别尝试在世界中生成生物。 每个条件都由一组组件组成,这些组件定义何时生产或不生成生物。
比如,我们可以查看**原版僵尸**的生成条件:
```json
{
"format_version": "1.8.0",
"minecraft:spawn_rules": {
"description": {
"identifier": "minecraft:zombie",
"population_control": "monster"
},
"conditions": [
{
"minecraft:spawns_on_surface": {},
"minecraft:spawns_underground": {},
"minecraft:brightness_filter": {
"min": 0,
"max": 7,
"adjust_for_weather": true
},
"minecraft:difficulty_filter": {
"min": "easy",
"max": "hard"
},
"minecraft:weight": {
"default": 100
},
"minecraft:herd": {
"min_size": 2,
"max_size": 4
},
"minecraft:permute_type": [
{
"weight": 95
},
{
"weight": 5,
"entity_type": "minecraft:zombie_villager"
}
],
"minecraft:biome_filter": {
"test": "has_biome_tag", "operator": "==", "value": "monster"
}
}
]
}
}
```
| 组件名称 | 描述 |
| :-------------------------------- | :----------------------------------------------------------- |
| `minecraft:spawns_on_surface` | 生物在地表生成 |
| `minecraft:spawns_on_underground` | 生物在地底生成 |
| `minecraft:brightness_filter` | 仅以特定亮度生成实体。 接受三个选项,`min``max``adjust_for_weather`。 亮度级别范围从 0 到 15。 如果 `adjust_for_weather` 设置为 `true`,则将考虑由于下雨和雷暴导致的亮度降低。 |
| `minecraft:difficulty_filter` | 难度选择器,`min``max` 定义了最低和最高难度。实体仅会在定义的难度内生成。 |
| `minecraft:weight` | 实体生成时的权重。 数字越大,生物生成的频率越高。 |
| `minecraft:herd` | 设置在同一生成规则上一起生成的实体数。也可以理解为群体生成。 |
| `minecraft:permute_type` | 给生成的实体一定概率变异为其他实体。 |
| `minecraft:biome_filter` | 群系选择器。仅会在通过条件的群系生成。 |
### 所有已知组件
下列是所有已知的用于控制实体生成的条件组件:
```text
minecraft:weight
minecraft:density_limit
minecraft:spawns_on_block_filter
minecraft:spawns_on_block_prevented_filter
minecraft:spawns_above_block_filter
minecraft:herd
minecraft:permute_type
minecraft:brightness_filter
minecraft:height_filter
minecraft:spawns_on_surface
minecraft:spawns_underground
minecraft:spawns_underwater
minecraft:disallow_spawns_in_bubble
minecraft:spawns_lava
minecraft:biome_filter
minecraft:difficulty_filter
minecraft:distance_filter
minecraft:is_experimental
minecraft:world_age_filter
minecraft:delay_filter
minecraft:mob_event_filter
minecraft:is_persistent
minecraft:player_in_village_filter
```
我们仅需要对上述组件有一个大概印象就可以,除了一些比较特殊的 `minecraft:is_persistent` 之类的,我们在原版中看不到例子之外,其他的都可以在原版行为包中搜索到相关的应用。我们只对下面两个比较特殊常用的来做一些说明。
#### spawns_above_block_filter
```JSON
"minecraft:spawns_above_block_filter": {
"blocks": "minecraft:stone",
"distance": 10
}
```
这个组件会垂直检测设定距离内的方块,如果条件满足,则实体生成。生成在指定的方块上。
#### spawns_on_block_prevented_filter
```json
"minecraft:spawns_on_block_prevented_filter": [
"minecraft:nether_wart_block",
"minecraft:shroomlight"
]
```
正好与上面一个组件相反,该组件的作用是让实体永远不会在列表内的方块上生成。
## 代码生成
虽然原版的生成规则已经能够满足大部分的需求,但有时我们也会想要自己生成一些怪物以应对特殊情况。比如,在惊变中,血月会导致怪物的生成速度和数量大幅增加:
![](./assets/image-20231122192922106.png)
这可能就需要我们代码来执行额外的生成逻辑了。
### 一切的基础
生成怪物,目前官方只有一个 [API](https://mc.163.com/dev/mcmanual/mc-dev/mcdocs/1-ModAPI/%E6%8E%A5%E5%8F%A3/%E4%B8%96%E7%95%8C/%E5%AE%9E%E4%BD%93%E7%AE%A1%E7%90%86.html#createengineentitybytypestr)`CreateEngineEntityByTypeStr`,用法也很简单:
```python
import mod.server.extraServerApi as serverApi
ServerSystem = serverApi.GetServerSystemCls()
class MyServerSystem(ServerSystem):
def createMob(self):
# 在主世界(050)的位置创建一个朝向为(0, 0)的尸壳
entityId = self.CreateEngineEntityByTypeStr('minecraft:husk', (0, 5, 0), (0, 0), 0)
```
### 示例:在玩家周围生成指定实体
API 很简单,最重要的就是对于坐标的选择了。我们想要实体生成在目标周围,但也要考虑一些特殊情况,比如当玩家在水中、空中、或者洞穴中能否支持生成。如果考虑得更加复杂一点,当前的控件是否支持实体的碰撞箱、光照强度等.....
这里提供一个简化之后的示例代码:
```python
def SpawnEntityAround(self, targetId, spawnEntityIdentifier, num=1, minRadius=6, maxRadius=12):
"""
生成实体在目标周围
:param targetId:
:param spawnEntityIdentifier:
:param num:
:param minRadius:
:param maxRadius:
:return:
"""
entityList = []
targetX, targetY, targetZ = CompFactory.CreatePos(targetId).GetFootPos()
dimensionId = CompFactory.CreateDimension(targetId).GetEntityDimensionId()
for _num in xrange(num):
_randomPos = (
targetX + random.randint(minRadius, maxRadius) * math.sin(random.uniform(0, 2 * math.pi)),
targetY,
targetZ + random.randint(minRadius, maxRadius) * math.cos(random.uniform(0, 2 * math.pi))
)
# 上面的 _randomPos 只是平面上的一个坐标,我们为了合理生成在地面上,需要 y 在 [-20, 20] 之间有一个合适的坐标
_firstAirPos = self._RetFirstAirBlockPos(_randomPos, dimensionId, -20, 20)
if _firstAirPos:
entityId = self.CreateEngineEntityByTypeStr(spawnEntityIdentifier, _firstAirPos, (0, random.randint(-180, 180)),
dimensionId)
entityList.append(entityId)
return entityList
# 范围内的第一个空气方块
def _RetFirstAirBlockPos(self, basePos, dimensionId, step, maxStep):
blockInfoComp = CompFactory.CreateBlockInfo(serverApi.GetLevelId())
while step < maxStep:
finalPos = (basePos[0], basePos[1] + step, basePos[2])
blockDict = blockInfoComp.GetBlockNew(finalPos, dimensionId)
step += 1
if blockDict and blockDict['name'] == 'minecraft:air':
return finalPos
return None
```
整体逻辑也很简单,画个图来解释一下:
![](./assets/image-20231122194716435.png)
先在平面上选择一个随机坐标,然后再从 y 方向上下手,寻找第一个空气方块,这个就是适合实体(简化)的坐标。
## 课后作业
本次课后作业,内容如下:
- 使用自定义的生成规则生成僵尸;
- 使用自己的代码来自定义生成指定的生物;