API

生物

自定义生物借用游戏中某个生物的身体(模型、动画、体型和移动方式),在上面绘制你的纹理,配上你的属性、行为和战利品。声明方式和其他内容一样:一组属性。

声明生物

content/creatures.craftCraftScript
creature "ghost" {
    body: "minecraft:zombie"
    texture: "entity/ghost"
    health: 40
    speed: 0.3
    attack: 4
    behaviour: ["swim", "melee_attack", { wander: 0.8 }, { look_at_players: 10 }, "look_around"]
    targets: ["hurt_by", "minecraft:pig"]
    loot: [ { item: "ghostmod:ecto", count: [1, 2] } ]
}
属性 取值 默认值
body 游戏中某个生物的 id:minecraft:zombie、minecraft:wolf、minecraft:cow…… 必填
texture 纹理路径,绘制在身体的模型上:entity/ghost 即模组中的 textures/entity/ghost.png 身体原本的外观
model 你自己的 3D 模型:entity/robot 即模组的 models/entity/robot.json(见下文) 身体的模型
size 以方块为单位的 [width, height]:它的碰撞范围和被点击的范围(见下文) 身体原本的值
health 生命值 身体原本的值
speed 行走速度(僵尸为 0.23,狼为 0.3) 身体原本的值
attack 近战攻击伤害 身体原本的值
follow_range 在多少格外发现目标 身体原本的值
behaviour 行为列表,从最重要到最不重要(见下文) 身体原本的行为
targets 攻击谁,按优先级排列(见下文) 无
loot [{ item, count, chance }, …],与方块相同:每个物品单独掷骰 无
spawns 自然出现的群系,含权重与群体大小(见下) 不会自然生成
breed_item 能让两只繁殖的物品(见下) 不能繁殖
tame_item 能驯服它的物品(见下) 无法驯服
rideable true 允许玩家骑乘(见下) false

行为

列表顺序就是优先级:两个行为同时想执行时,靠前的胜出。每个行为可以写成名称、写成 { 名称: 数字 } 来设置主要参数,或写成包含全部参数的映射。

行为 作用 参数
"swim" 在水中保持漂浮。
"look_around" 空闲时四处张望。
"wander"、{ wander: 0.8 } 四处游荡。 speed,默认 1
"look_at_players"、{ look_at_players: 10 } 注视附近的玩家。 range(格),默认 8
"melee_attack"、{ melee_attack: 1.2 } 追击并攻击目标。 speed,默认 1
"panic"、{ panic: 1.5 } 受到伤害时逃跑。 speed,默认 1.25
{ avoid: "minecraft:wolf", distance: 8, speed: 1.2 } 远离该生物。 distance(格)、speed
{ follow: "minecraft:wheat", speed: 1 } 跟随手持该物品的人。 speed
{ ranged_attack: "minecraft:snowball", interval: 20, range: 14, damage: 3 } 向目标发射投射物(见下)。 projectile, interval, range, speed, power, damage
"breed", { breed: 1 } 喂食后,与同类繁殖。 speed
"follow_parent", { follow_parent: 1.1 } 幼崽待在父母身边。 speed
{ follow_owner: 1.2, distance: 8 } 被驯服的生物跟随主人。 speed、distance(格数)
{ fly_around: 1 } 四处飞行;适用于会飞的身体(鹦鹉、蜜蜂、悦灵)。 speed
{ can_start: …, tick: … } 你自己的代码(见下)。 can_start, tick, keep_going, moves

速度数值是生物基础速度的倍数:{ wander: 0.8 } 游荡时比正常行走稍慢。

攻击目标

targets 说明它攻击谁,从最高优先级到最低。"hurt_by" 是对它造成伤害的人;实体 id 表示最近的该类实体("minecraft:player" 表示玩家,"minecraft:pig" 表示猪)。要真正发起攻击,生物还需要一个执行攻击的行为,例如 "melee_attack"。

生成生物

commands/haunt.craftCraftScript
command haunt requires "ghostmod.haunt" describe "在你所站的位置召唤一只幽灵" {
    if player == null { return "仅限玩家使用" }
    world().spawn("ghostmod:ghost", player.location)
    return "&7一只幽灵出现了……"
}

id 属于你的模组:ghostmod:ghost。游戏命令 /summon ghostmod:ghost 同样可用。

让它们自然生成

添加 spawns,生物就会像游戏自带的生物一样在世界中自然出现。

content/ghost.craftCraftScript
creature "ghost" {
    body: "minecraft:zombie"
    spawns: [ { biomes: ["minecraft:dark_forest", "#minecraft:is_forest"], weight: 20, group: [1, 2] } ]
}

每一项列出它出现的 biomes(一个 id,或用 #标签 指代整组群系)、weight(权重:相对于该群系其他生物的出现频率,牛是 8,僵尸是 95)以及 group 的数量范围。

它遵循其身体的生成规则:僵尸身体在黑暗中生成,牛身体在有光的草地上生成。无论是世界生成时还是之后,都是如此。

刷怪蛋。 每个生物还会得到一个刷怪蛋,其 id 加上 _spawn_egg(ghost_spawn_egg),位于创造模式的刷怪蛋标签页。如果模组里有 textures/item/ghost_spawn_egg.png,就用它作纹理;否则外观与其身体的刷怪蛋相同。

Kotlin 中:

content/Creatures.ktKotlin
content.creature("ghost") {
    body = "minecraft:zombie"
    spawn("minecraft:dark_forest", "#minecraft:is_forest", weight = 20, group = 1..2)
}

用代码操控生物

你声明的生物以及其他任何生物都是 Creature。你可以在事件、命令或计时器中向它们下达指令:

成员 作用
target 它正在攻击的对象。可读取,也可设置。
moveTo(location, speed), move_to 走到某个位置。speed 会乘以它的正常速度。
stop() 停止行走。
isMoving, is_moving 是否正在走向某处。
lookAt(location), look_at 转头看向某个位置。
canSee(entity), can_see 与该实体之间是否有畅通的视线。
isBaby 是否为幼崽。
ownerId 被驯服后,其主人的 id。
tame(player) 让该玩家成为它的主人。
isSitting 是否正坐着。
playAnimation(name), play_animation 播放其模型的一个动画(见下)。

每个实体都有 velocity,这是一个以每刻多少格计的 Vector。读取或设置它即可推动实体:entity.velocity = vector(0, 0.6, 0) 会把它向上弹起。

在 CraftScript 中,带类型的 let 会把实体收窄为生物,运行时会进行检查:

commands/pup.craftCraftScript
command pup requires "dogmod.pup" describe "Get a pet dog" {
    if player == null { return "Players only" }
    let dog: Creature = world().spawn("dogmod:dog", player.location)
    dog.tame(player)
    dog.look_at(player.location)
    dog.velocity = vector(0, 0.6, 0)      // a little hop
    return "&7Good dog!"
}

你自己的行为

当现成的行为不够用时,就用自己的代码写一个。它和其他行为一样放进 behaviour 列表,位置就是它的优先级。

content/herder.craftCraftScript
creature "herder" {
    body: "minecraft:wolf"
    targets: ["minecraft:pig"]
    behaviour: [
        "swim",
        {
            can_start: fun(c) { return c.target != null && c.can_see(c.target) },
            tick: fun(c) {
                if c.target != null {
                    c.look_at(c.target.location)
                    if !c.is_moving { c.move_to(c.target.location, 1.2) }
                }
            }
        },
        { wander: 1 }
    ]
}
  • can_start:每隔几刻被询问一次;返回是否应该开始。
  • tick:行为运行期间每刻执行一次。
  • keep_going:返回是否继续。省略时与 can_start 相同。
  • moves:默认 true。会移动的行为不会与另一个会移动的行为同时运行,列表中靠前的优先。只观察或只做出反应的行为请用 false。

Kotlin 中: Behaviour.Custom(canStart = { … }, tick = { … }, keepGoing = …, moves = true)

动物:繁殖、驯服、骑乘与飞行

几个属性和行为就能把生物变成动物。这只生物用苹果繁殖,用骨头驯服,还可以骑乘:

content/mooble.craftCraftScript
creature "mooble" {
    body: "minecraft:cow"
    tame_item: "minecraft:bone"
    breed_item: "minecraft:apple"
    rideable: true
    behaviour: ["swim", { breed: 1 }, { follow_parent: 1.1 }, { follow_owner: 1.2, distance: 8 }, { wander: 0.8 }]
}
  • 繁殖。 breed_item 加上行为 "breed":用该物品喂食后,两只同类会生出同类的幼崽。需要动物身体(牛、狼、鸡……)。幼崽用 "follow_parent" 跟在父母身边。
  • 驯服。 tame_item:每次尝试有三分之一的成功率。主人会被记住,被驯服的生物绝不会以主人为目标。潜行并空手使用,可让它坐下或站起。{ follow_owner: 1.2, distance: 8 } 让它跟随主人。
  • 骑乘。 rideable: true:空手使用即可骑上去(驯服后只有主人可以)。用移动键和鼠标操控,潜行下来。
  • 飞行。 会飞的身体(鹦鹉、蜜蜂、悦灵)用 { fly_around: 1 }。身体会飞且 rideable 的生物,骑乘者抬头时会爬升,低头时会俯冲。

Kotlin 中:breedItem = …、tameItem = …、rideable = true,以及行为 Behaviour.Breed()、Behaviour.FollowParent()、Behaviour.FollowOwner(speed, distance) 和 Behaviour.FlyAround()。

自己的投射物

除了游戏自带的投射物,模组也可以声明自己的投射物。它有自己的伤害、重力,以及落地时发生的事:

content/frost_bolt.craftCraftScript
projectile "frost_bolt" {
    damage: 4
    gravity: false
    on_hit: fun(e) {
        let at = e.entity?.location ?? e.block?.location
        if at != null { e.projectile.world.spawn_particle("minecraft:snowflake", at.add(0.5, 1, 0.5), 12, 0.4, 0.02) }
    }
}
  • damage:对命中目标造成的伤害。
  • gravity:设为 false 则沿直线飞行。
  • on_hit:fun(e),落地时调用,参数 e 与 hit 事件相同:e.projectile、e.shooter,以及 e.entity 或 e.block。
  • texture:投射物飞行时外观就是它的物品,所以默认使用 textures/item/<id>.png;设置 texture 可改用其他纹理。

像其他投射物一样使用:world.shoot("mymod:frost_bolt", from, velocity, shooter),在生物中则是 { ranged_attack: "mymod:frost_bolt" }。在 Kotlin 中:

content/FrostBolt.ktKotlin
content.projectile("frost_bolt") {
    damage = 4.0
    gravity = false
    onHit = { e -> /* … */ }
}

远程攻击与投射物

行为 ranged_attack 让生物开火。它会靠近,直到目标进入射程且在视线内,然后射击。

content/frost_spirit.craftCraftScript
creature "frost_spirit" {
    body: "minecraft:snow_golem"
    behaviour: [{ ranged_attack: "minecraft:snowball", interval: 20, range: 14, damage: 3 }, { wander: 0.6 }]
    targets: ["minecraft:zombie", "hurt_by"]
}

on hit(e) {
    if e.shooter?.type == "frostmod:frost_spirit" && e.entity != null {
        e.entity.velocity = vector(0, 0.2, 0)      // knocked up
    }
}
设置 含义
ranged_attack ranged_attack:投射物(minecraft:arrow、minecraft:snowball、minecraft:small_fireball、minecraft:wind_charge 等)。
interval interval:两次射击之间的刻数。
range range:开始射击的距离(格)。
speed speed:靠近时的行走速度。
power power:投射物的飞行速度。
damage damage:命中时的额外伤害。

若要从代码中发射(用于生物或玩家的能力),使用 world().shoot(projectile, from, velocity, shooter, damage)。

在 Kotlin 中:

content/FrostSpirit.ktKotlin
creature("frost_spirit") {
    body = "minecraft:snow_golem"
    behaviour(Behaviour.RangedAttack(Identifier.parse("minecraft:snowball"), interval = 20, range = 14.0, damage = 3.0))
    target(TargetRule.Nearest(Identifier.parse("minecraft:zombie")))
}

// From code, e.g. a player's ability:
world.shoot(Identifier.parse("minecraft:small_fireball"), from, Vector(0.0, 0.0, 1.5), shooter = player, damage = 4.0)

投射物落点命中时会触发 hit 事件(ProjectileHitEvent)。它带有 e.projectile、e.shooter,以及 e.entity 或 e.block(取决于击中了什么)。取消它,投射物就会穿过去。见事件。

注意事项

  • **每种身体都有限制。**有些游戏生物缺少某项属性:牛没有攻击伤害。如果你设置了它,会被忽略,Crafting 会在日志中发出警告。
  • **不会行走的身体。**无法自行寻路的身体会忽略行走类行为(游荡、追击、逃跑、跟随、躲避);其他行为照常生效。
  • **不写 behaviour 时,**生物的行为与它所用的身体相同。一旦写了列表,它就只做列表中的事。
  • **模型只改变外观。**移动和行为始终来自身体,体型也是,除非你设置了 size。
  • 自定义生物和其他生物一样是活体实体:它会触发 spawn、damage 和 entity_death 事件,拥有 data,可以用世界与玩家中的内容来操作它。

Kotlin 中

content/Creatures.ktKotlin
package com.example.rubies.content

object Creatures {
    fun register(content: ContentBuilder) {
        content.creature("ruby_golem") {
            body = "minecraft:zombie"
            texture = "entity/ruby_golem"
            health = 60.0
            speed = 0.25
            attackDamage = 6.0
            behaviour(Behaviour.Swim)
            behaviour(Behaviour.MeleeAttack())
            behaviour(Behaviour.Wander(0.6))
            behaviour(Behaviour.LookAtPlayers(12f))
            behaviour(Behaviour.LookAround)
            target(TargetRule.HurtBy)
            target(TargetRule.Nearest(Identifier.parse("minecraft:player")))
            drop(Items.RUBY, 2..4)
        }
    }
}

行为:Behaviour.Swim、LookAround、Wander(speed)、LookAtPlayers(range)、MeleeAttack(speed)、Panic(speed)、Avoid(entity, distance, speed)、Follow(item, speed)。攻击目标:TargetRule.HurtBy 和 TargetRule.Nearest(id)。用 drop(item, count, chance) 添加战利品,与方块相同。

你自己的 3D 模型

添加 model 后,生物就会用你的模型而不是身体的模型来绘制。体型、移动和行为仍然来自身体,模型只改变外观。

content/robot.craftCraftScript
creature "robot" {
    body: "minecraft:pig"
    model: "entity/robot"
    texture: "entity/robot"
    behaviour: ["swim", { wander: 0.8 }, { look_at_players: 10 }]
}

模型是模组的 models/entity/robot.json,它的纹理是 textures/entity/robot.png。在 Kotlin 中:creature("robot") { body = "minecraft:pig"; model = "entity/robot"; texture = "entity/robot" }。该格式在不同游戏版本之间保持一致。

models/entity/robot.jsonJSON
{ "texture_size": [64, 64],
  "bones": [
    { "name": "body", "pivot": [0, 6, 0], "cubes": [ { "origin": [-5, 6, -3], "size": [10, 10, 6], "uv": [0, 0] } ] },
    { "name": "head", "parent": "body", "pivot": [0, 16, 0], "cubes": [ { "origin": [-4, 16, -4], "size": [8, 8, 8], "uv": [0, 16] } ] },
    { "name": "left_leg", "pivot": [2.5, 6, 0], "cubes": [ { "origin": [1, 0, -1.5], "size": [3, 6, 3], "uv": [32, 0] } ] },
    { "name": "right_leg", "pivot": [-2.5, 6, 0], "cubes": [ { "origin": [-4, 0, -1.5], "size": [3, 6, 3], "uv": [32, 16] } ] }
  ],
  "animations": { "head": "look", "left_leg": "walk", "right_leg": "walk_opposite", "body": "idle" } }

尺寸以像素为单位(16 为一格),y 轴从生物的脚底向上。

  • 骨骼:name、可选的 parent(随它一起移动)、pivot(它转动所绕的点)和 cubes。
  • 立方体:origin(最低的角)、size 和 uv(它的纹理在图片中的起点)。布局是常见的盒子展开方式:宽 w、高 h、深 d 的立方体占用 2(w+d) × (d+h) 像素的区域。
  • 动画按骨骼设置,写成一个名称或名称列表:
动画 作用
look 转向生物注视的方向;用于头部。
walk、walk_opposite 行走时摆动;后者相位相反,用于另一侧的肢体。
idle 轻微摇摆。
spin 持续旋转。

如果模型无法绘制,生物会显示为身体的样子,日志中会说明原因。

体型

生物的大小与它的身体一致。当你的模型更小或更大时,用 size: [width, height](以方块为单位)设置,让碰撞范围和被点击的范围与玩家看到的样子一致:

content/drake.craftCraftScript
creature "drake" {
    body: "minecraft:bee"
    model: "entity/drake"
    texture: "entity/drake"
    size: [1.4, 1.1]
}

在 Kotlin 中:size = 1.4 to 1.1。

关键帧动画

如需超出现成动画的动作,在 bones 和 animations 旁边添加 clips 部分:

models/entity/robot.jsonJSON
{ "bones": [ … ],
  "animations": { … },
  "clips": {
    "idle": { "length": 2.0, "bones": { "head": { "position": { "0": [0, 0, 0], "1": [0, 0.6, 0], "2": [0, 0, 0] } } } },
    "walk": { "length": 1.0, "bones": { "body": { "rotation": { "0": [0, 0, -6], "0.5": [0, 0, 6], "1": [0, 0, -6] } } } },
    "roar": { "length": 1.5, "bones": {
      "head": { "rotation": { "0": [0, 0, 0], "0.4": [-35, 0, 0], "1.1": [-35, 0, 0], "1.5": [0, 0, 0] },
                "scale": { "0": 1, "0.4": 1.35, "1.1": 1.35, "1.5": 1 } },
      "body": { "position": { "0": [0, 0, 0], "0.3": [0, 4, 0], "0.6": [0, 0, 0] } } } }
  } }
  • 每个片段有以秒计的 length(时长),每根骨骼最多有三个通道:rotation(度)、position(像素,y 向上)和 scale(一个数或 [x, y, z])。
  • 关键帧的键是以秒计的时间。值在关键帧之间按直线过渡;给片段加上 "interpolation": "smooth",则改为沿曲线穿过各关键帧。通道也可以只写一个值,用于整个片段。
片段 何时播放
walk 行走时循环播放,与脚步同步。
idle 站立不动时循环播放。
attack 出击时播放。
其他任何名称 代码请求时播放:c.play_animation("roar")。附近装有 Crafting 的玩家能看到。
Smooth clipJSON
"idle": { "length": 1.2, "interpolation": "smooth", "bones": {
  "left_wing": { "rotation": { "0": [0, 0, -20], "0.6": [0, 0, 25], "1.2": [0, 0, -20] } } } }

由代码启动的片段会叠加在 walk、idle 和 attack 之上播放,而不是替换它们,并且效果会相加:生物可以一边行走一边咆哮。

拥有自己模型的生物,其幼崽按一半大小绘制。

纹理

没有模型时,纹理的布局与所选身体(僵尸、狼……)相同,所以你可以从该身体的模板开始在上面绘制。它放在模组内部,和物品、方块的纹理一样:

模组 位置
CraftScript(文件夹) mods/ghostmod/textures/entity/ghost.png
Kotlin 或 Java src/main/resources/assets/ghostmod/textures/entity/ghost.png