实体与交互 · LSE¶
本页覆盖三组能力:
entity*(37 个):协议层自定义实体 —— 用原版实体类型在客户端凭空造一只实体,适合做 NPC 壳、装饰生物、盔甲架布景。服务端没有这只实体:没有 AI、没有碰撞、不落存档,也不参与任何服务端逻辑。ghost*(2 个):交互事件轮询 —— 玩家点到上面这类协议实体时,库把点击路由成事件放进队列,脚本自己轮询取走。particle*(22 个):粒子形状 —— 用原版粒子摆出点、线、矩形边框、填充面、长方体框、六面、多面体,并支持平移、平滑移动、旋转、自旋、缩放、跟随玩家。
三组都只负责"画出来"和"接住点击",业务逻辑(命令、权限、持久化)写在你的脚本里。
本节示例里,
id指实体创建函数返回的库内 id,pid指粒子创建函数返回的形状 id。两者是两套互不相干的编号:实体 id 从 1 开始自增(也可指定或随机),粒子 id 从 1 开始自增。
快速开始¶
const entityCreate = ll.import("HologramLib", "entityCreate");
const entitySetNametag = ll.import("HologramLib", "entitySetNametag");
const ghostPollInteractions = ll.import("HologramLib", "ghostPollInteractions");
const particleCreateBox = ll.import("HologramLib", "particleCreateBox");
// 1. 在主世界 (100.5, 64, 100.5) 造一只盔甲架(dim:0=主世界 1=下界 2=末地)
const id = entityCreate("armor_stand", 100.5, 64, 100.5, 0);
// 2. 给它挂个名字牌(§ 颜色码直接写在文本里)
entitySetNametag(id, "§e守卫");
// 3. 每 200 毫秒取一次点击事件 —— 事件进队列,必须自己轮询
setInterval(() => {
for (const line of ghostPollInteractions()) {
logger.info(line); // 例:player=Steve action=2 domain=entity id=1
}
}, 200);
// 4. 在它身上套一个粒子方框(interval=2:每 2 tick 重发一次;lifetime=0:永久)
const pid = particleCreateBox("demo", 0, 100.5, 64.5, 100.5, 1.5, 1.5, 1.5, 1, "minecraft:endrod", 2, 0);
自定义实体 · 创建¶
这一组负责把实体造出来。造出来之后它会在满足可见条件的玩家客户端上出现:同维度、在视距内、enabled 为开、且在白名单内(没设白名单就是全员)。
创建一个自定义实体¶
entityCreate(identifier, x, y, z, dim)
- 参数:
identifier:String
实体类型,如minecraft:cow、armor_stand;写短名(cow)会自动补minecraft:前缀x:Float
世界坐标 X(浮点,.5落在方块中心)y:Float
世界坐标 Y(浮点,.5落在方块中心)z:Float
世界坐标 Z(浮点,.5落在方块中心)dim:Integer
维度,0主世界 /1下界 /2末地- 返回值:库内实体 id(后续所有
entity*都用它);identifier为空返回-1 - 返回值类型:
Integer -
示例:
创建时设好朝向缩放名字牌¶
entityCreateAdvanced(identifier, x, y, z, dim, yaw, pitch, scale, nametag)
- 参数:
identifier:String
实体类型,同entityCreatex:Float
世界坐标 X,同entityCreatey:Float
世界坐标 Y,同entityCreatez:Float
世界坐标 Z,同entityCreatedim:Integer
维度,同entityCreateyaw:Float
偏航角(度),水平朝向pitch:Float
俯仰角(度),抬头/低头scale:Float
整体缩放;发包时被钳制到0.0625 ~ 10nametag:String
名字牌文字,支持§颜色码;空串 = 不要名字牌- 返回值:库内实体 id;
identifier为空返回-1 - 返回值类型:
Integer -
示例:
用随机 id 创建实体¶
entityCreateRandom(identifier, x, y, z, dim)
- 参数:
identifier:String
实体类型,同entityCreatex:Float
世界坐标 X,同entityCreatey:Float
世界坐标 Y,同entityCreatez:Float
世界坐标 Z,同entityCreatedim:Integer
维度,同entityCreate- 返回值:库内实体 id,取值在
[0x10000000, 0x7FFFFFFF)区间(与自增段长期隔离,不会撞车);取不到空闲 id 返回-1 - 返回值类型:
Integer -
示例:
用指定 id 创建实体¶
entityCreateWithId(identifier, x, y, z, dim, desiredId)
- 参数:
identifier:String
实体类型,同entityCreatex:Float
世界坐标 X,同entityCreatey:Float
世界坐标 Y,同entityCreatez:Float
世界坐标 Z,同entityCreatedim:Integer
维度,同entityCreatedesiredId:Integer
想占用的库内 id,必须> 0- 返回值:成功时就是
desiredId;desiredId <= 0或这个 id 已被占用返回-2 - 返回值类型:
Integer -
示例:
自定义实体 · 属性¶
改属性全部走"下一个 tick 生效":结构性改动(类型、姿势、装备、flags 等)合并成一次重建,坐标/朝向/名字牌/缩放/隐身走增量包 —— 两种情况都不会闪。
更换实体类型¶
entitySetIdentifier(id, identifier)
- 参数:
id:Integer
实体 ididentifier:String
新的实体类型;短名自动补minecraft:;空串被拒绝- 返回值:改成功
true;id 不存在或identifier为空false - 返回值类型:
Boolean -
示例:
移动实体到新坐标¶
entitySetPosition(id, x, y, z, dim)
- 参数:
id:Integer
实体 idx:Float
新的世界坐标 Xy:Float
新的世界坐标 Yz:Float
新的世界坐标 Zdim:Integer>= 0时把实体放到该维度;< 0= 只改坐标、维度不动- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置实体朝向¶
entitySetRotation(id, yaw, pitch)
- 参数:
id:Integer
实体 idyaw:Float
偏航角(度),水平朝向pitch:Float
俯仰角(度)- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置头顶名字牌¶
entitySetNametag(id, text)
- 参数:
id:Integer
实体 idtext:String
名字牌文字,支持§颜色码;空串 = 清除名字牌- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置实体缩放¶
entitySetScale(id, scale)
- 参数:
id:Integer
实体 idscale:Float
目标缩放(模型与体积等比);<= 0直接拒绝;实际发包时被钳制到客户端有效域0.0625 ~ 10- 返回值:
true成功;scale <= 0或 id 不存在false - 返回值类型:
Boolean -
示例:
按倍数相对缩放¶
entityScaleBy(id, factor)
- 参数:
id:Integer
实体 idfactor:Float
倍数,> 1放大、0 < factor < 1缩小;结果自动钳制到0.0625 ~ 10- 返回值:
true成功;factor <= 0或 id 不存在false - 返回值类型:
Boolean -
示例:
设置主变种编号¶
entitySetVariant(id, variant)
- 参数:
id:Integer
实体 idvariant:Integer
变种编号(皮肤/亚种);同一个数字在不同实体上含义不同(例如热带鱼的体型、河豚的鼓胀档),库不做校验- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置二级变种编号¶
entitySetMarkVariant(id, markVariant)
- 参数:
id:Integer
实体 idmarkVariant:Integer
二级变种编号,含义由实体类型决定(例如热带鱼的花纹、马的斑纹)- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置实体颜色索引¶
entitySetColorIndex(id, colorIndex)
- 参数:
id:Integer
实体 idcolorIndex:Integer
颜色编号,用于羊、项圈这类会染色的实体- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
覆写原始 flags 位掩码¶
entitySetFlags(id, flags)
- 参数:
id:Integer
实体 idflags:Integer
位掩码原值(高级用法),如0x01= 着火、0x20= 隐身;是整块覆盖,不会与旧值合并- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
切换实体隐身¶
entitySetInvisible(id, on)
- 参数:
id:Integer
实体 idon:Booleantrue隐身 /false现身;只动隐身这一位;和当前值相同时也返回true(重复调用无副作用)- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置实体姿势¶
entitySetPose(id, pose)
- 参数:
id:Integer
实体 idpose:Integer
姿势编号(PoseIndex),只对按它摆姿的模型有效;0..13(0=站立、1=无底板、2=显示手臂,3..13是坐姿/睡姿/跳舞等);越界自动钳进这个范围- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置装备槽里的物品¶
entitySetEquipmentSlot(id, slot, name, aux, nbt)
- 参数:
id:Integer
实体 idslot:Integer0=主手1=副手2=头3=胸4=腿5=脚;其它值返回falsename:String
物品 id,短名自动补minecraft:;空串 = 清空该槽aux:Integer
物品的数据值,具体取值随物品而定nbt:String
额外数据,SNBT 文本;空串 = 不带附加数据- 返回值:
true成功;slot越界或 id 不存在false - 返回值类型:
Boolean -
示例:
自定义实体 · 可见性¶
谁能看到这只实体,由维度 + 视距 + 白名单 + 总开关共同决定,四个条件是叠加的。
开关实体总可见性¶
entitySetEnabled(id, enabled)
- 参数:
id:Integer
实体 idenabled:Booleanfalse= 从所有客户端上撤掉,true= 重新发出去- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
设置逐玩家可见半径¶
entitySetViewDistance(id, dist)
- 参数:
id:Integer
实体 iddist:Float
距离(方块);<= 0= 不做距离限制。创建时的默认值是 64 格- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
用白名单限定可见玩家¶
entitySetVisiblePlayers(id, playerNames)
- 参数:
id:Integer
实体 idplayerNames:Array<String>
玩家名数组(按 LSE 的player.realName匹配);空数组 = 清除限制、恢复全员可见- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
只让一名玩家看到实体¶
entitySetVisiblePlayer(id, playerName)
- 参数:
id:Integer
实体 idplayerName:String
唯一的可见玩家名;替换整份名单,不是追加。想看多人请用entitySetVisiblePlayers;脚本侧推荐用这个(不用拼数组)- 返回值:
true成功;id 不存在false - 返回值类型:
Boolean -
示例:
清除白名单恢复全员可见¶
entityClearVisiblePlayers(id)
- 参数:
id:Integer
实体 id- 返回值:
true成功(本来就没设白名单也返回true);id 不存在false - 返回值类型:
Boolean -
示例:
自定义实体 · 骑乘与动画¶
让实体骑到玩家头上¶
entitySetRidePlayer(id, playerName)
- 参数:
id:Integer
实体 idplayerName:String
玩家名,必须在线;空串 = 清除骑乘链接- 返回值:
true成功(本来就没有链接、空串清除也算true);id 不存在或玩家不在线false - 返回值类型:
Boolean -
示例:
让实体骑到另一只实体上¶
entitySetRideEntity(id, vehicleEntityId)
- 参数:
id:Integer
实体 id(乘客)vehicleEntityId:Integer
载具实体的库内 id;0= 清除链接- 返回值:
true成功;载具不存在、载具就是自己、或 id 不存在false - 返回值类型:
Boolean -
示例:
解除骑玩家与骑实体的链接¶
entityClearRide(id)
- 参数:
id:Integer
实体 id- 返回值:
true成功(本来就没链接也返回true);id 不存在false - 返回值类型:
Boolean -
示例:
给已见过实体的玩家放动画¶
entityPlayAnimation(id, animation, stopExpression, durationTicks)
- 参数:
id:Integer
实体 idanimation:String
资源包里的动画标识符,如animation.humanoid.base_pose;空串被拒绝stopExpression:String
Molang 停止表达式,表达式为真时停止;空串 = 常驻不自动停durationTicks:Integer> 0时到这么多 tick 后自动补发停止;0= 不限时- 返回值:
true表示已排队发出(约 2 tick 后到客户端);id 不存在、animation为空、实体被entitySetEnabled关掉、或当前没有任何玩家见过这只实体时false - 返回值类型:
Boolean -
示例:
自定义实体 · 逐客户端外观¶
同一个实体,让不同玩家看到不同的名字牌、缩放和装备。覆盖只在你指定的那个玩家客户端上生效,其他人仍然看到通用配置。
改单个玩家看到的名字牌¶
entitySetPlayerNametag(id, playerName, text)
- 参数:
id:Integer
实体 idplayerName:String
观看者名字,必须在线且已经见过这只实体text:String
只有这个玩家能看到的文字;空串 = 清除该玩家的覆盖,回到通用名字牌- 返回值:
true成功;id 不存在、玩家不在线、或该玩家还没见过这只实体false - 返回值类型:
Boolean -
示例:
改单个玩家看到的缩放¶
entitySetPlayerScale(id, playerName, scale)
- 参数:
id:Integer
实体 idplayerName:String
观看者名字,必须在线且已经见过这只实体scale:Float
该玩家看到的缩放,钳制到0.0625 ~ 10;<= 0= 清除覆盖、回到通用缩放- 返回值:
true成功;id 不存在、玩家不在线、或该玩家还没见过这只实体false - 返回值类型:
Boolean -
示例:
改单个玩家看到的装备¶
entitySetPlayerEquipmentSlot(id, playerName, slot, name, aux, nbt)
- 参数:
id:Integer
实体 idplayerName:String
观看者名字,必须在线且已经见过这只实体slot:Integer0=主手1=副手2=头3=胸4=腿5=脚;其它值返回falsename:String
物品 id;空串 = 这个槽对该玩家回退到通用装备aux:Integer
物品的数据值,具体取值随物品而定nbt:String
额外数据,SNBT 文本;空串 = 不带附加数据- 返回值:
true成功;slot越界、id 不存在、玩家不在线、或该玩家还没见过这只实体false - 返回值类型:
Boolean -
示例:
清除单个玩家的外观覆盖¶
entityClearPlayerAppearance(id, playerName)
- 参数:
id:Integer
实体 idplayerName:String
观看者名字,必须在线- 返回值:
true表示确实清掉了覆盖;该玩家本来就没有任何覆盖、id 不存在、或玩家不在线返回false - 返回值类型:
Boolean -
示例:
自定义实体 · 查询与销毁¶
找最近的自定义实体¶
entityFindNearest(x, y, z, dim, maxDist)
- 参数:
x:Float
参考点世界坐标 Xy:Float
参考点世界坐标 Yz:Float
参考点世界坐标 Zdim:Integer
只在同一个维度的实体里找(维度不同的直接跳过)maxDist:Float
最大距离(方块);<= 0= 不限制- 返回值:最近实体的库内 id;范围内没有实体返回
-1 - 返回值类型:
Integer -
示例:
读取实体运行态摘要¶
entityGetInfo(id)
- 参数:
id:Integer
实体 id- 返回值:摘要文字(诊断探针;id、维度、坐标、类型、开关、缩放、姿势、装备数、视距、白名单、已见玩家数、骑乘);id 不存在返回
"not_found" - 返回值类型:
String -
示例:
判断实体是否存在¶
entityExists(id)
- 参数:
id:Integer
实体 id- 返回值:存在
true,不存在false - 返回值类型:
Boolean -
示例:
判断 id 是否被占用¶
entityIsIdUsed(id)
与 entityExists 查的是同一份记录,名字更贴合"创建前先探一下"的用法。
- 参数:
id:Integer
实体 id- 返回值:已占用
true,空闲false - 返回值类型:
Boolean -
示例:
列出全部实体 id¶
entityGetAllIds()
- 返回值:id 数组;一个实体都没有时是空数组
- 返回值类型:
Array<Integer> -
示例:
销毁一只实体¶
entityDestroy(id)
- 参数:
id:Integer
实体 id- 返回值:确实销毁了
true;id 不存在false。客户端上那一只也跟着消失 - 返回值类型:
Boolean -
示例:
销毁全部自定义实体¶
entityDestroyAll()
- 返回值:没有返回值
- 返回值类型:
Null -
示例:
交互事件(ghost*)¶
ghost* 只有两个函数,但用法和其他域不一样,先看这三条:
- 事件进队列,没有回调。 玩家点击库内协议实体(自定义实体 / 物品悬浮 / 假玩家 NPC)时,库把这次交互放进一个待处理队列,你必须自己按 tick 或定时器调
ghostPollInteractions()取走;不取就一直堆在队列里。 - 事件来源于客户端交互包。 库在收包路径上挂了一层钩子,把客户端发来的交互包按目标反查回库内 id,再格式化成字符串入队。只有点到库内协议实体的交互会进这个队列 —— 打到服务端真实实体(原版生物、玩家)的点击不会出现在这里。
- 队列有上限。 最多存 256 条,满了以后新事件会把最旧的挤掉;不轮询 = 丢事件。
取走并清空交互事件¶
ghostPollInteractions()
- 返回值:每条是一个事件字符串,队列空时是空数组
- 返回值类型:
Array<String>
格式:player=<玩家名> action=<1..6> domain=<entity|itemDisplay|npc> id=<库内 id>,客户端带了坐标时(InteractUpdate 等)末尾再追加 pos=(x,y,z)。
action 取值:1=Interact(右键交互)2=Attack(左键攻击)3=StopRiding 4=InteractUpdate 5=NpcOpen 6=OpenInventory。处理前先判断 action,不要假设只有 1 和 2。
domain 说明点的是哪一类:entity = 自定义实体,itemDisplay = FMBE 物品悬浮,npc = 假玩家 NPC。
- 示例:
```js
const ghostPollInteractions = ll.import("HologramLib", "ghostPollInteractions");
setInterval(() => {
for (const line of ghostPollInteractions()) {
const m = line.match(/player=(\S+) action=(\d+) domain=(\S+) id=(-?\d+)/);
if (!m) continue;
const [, who, action, domain, targetId] = m;
if (action === "1" && domain === "entity") logger.info(`${who} 右键了实体 ${targetId}`);
if (action === "2" && domain === "entity") entityDestroy(Number(targetId)); // 左键 = 打掉
}
}, 100);
```
丢弃未处理的交互事件¶
ghostClearInteractions()
- 返回值:没有返回值
- 返回值类型:
Null -
示例:
注意事项¶
ghost*必须轮询。 交互事件不进回调,只在队列里;不调ghostPollInteractions()就永远收不到。队列上限 256 条,满了丢最旧的。- 只有点到库内协议实体的交互会入队。 自定义实体、物品悬浮、假玩家 NPC 的点击会带
domain与库内id;打到服务端真实实体(原版生物、玩家)的点击不会出现在ghostPollInteractions()的返回值里。 - 自定义实体不是"创建即出现"。 谁能看到由库定期重算,所以新建的实体最迟约 1 秒内出现在客户端;改一次属性会顺带触发一次重算,通常更快。
- 协议实体不参与服务端逻辑。 它没有 AI、没有碰撞、不落存档:打它不会掉血,走过去不会撞到,重启后全部消失 —— 需要持久化就自己存 id,再用
entityCreateWithId恢复。 - 可见性是四个条件的交集。 维度 + 视距 + 白名单 +
entitySetEnabled开关都满足的玩家才看得到;白名单不是"追加",entitySetVisiblePlayer会替换整份名单。 - 名字牌默认只在准星对准时显示。 LSE 侧没有切换"常显"的函数;要和全息字一样始终可见,用
holo*域另挂一行字。 - 逐客户端朝向没有 LSE 导出。 "每个玩家看到不同朝向"(
setPlayerRotation一族)只有 C++ 接口,本页的逐客户端函数只覆盖名字牌、缩放和装备。 entityPlayAnimation只发一次包、不持久化。 玩家后来才看到这只实体时不会自动补发动画;entitySetEnabled关掉时调用会直接返回false。- 粒子形状没有"销毁全部"和"列出全部 id"的导出。 脚本要自己保存创建时返回的 id,收尾时逐个
particleDestroy。 - 粒子白名单按 uuid,并且和维度叠加。 白名单里一个有效玩家都不在形状所在维度时,这个形状会被自动移除(不会留着等人);形状所在维度为空只是空转保留。
- 粒子是瞬态视觉,靠重发维持。
interval决定观感与发包量:间隔越大越省带宽、越容易看出"一批一批"的闪烁;step越小粒子越密,面形状尤其要注意粒子数量。 - 纯视觉、不改服务端状态。 这些能力都只在客户端出现,服务端的真实世界、存档与逻辑完全不受影响。