跳转至

Box3Entity / GameEntity 实体

这是一个服务端API

该API仅在服务端脚本使用

查阅官方文档
查阅官方文档(Arena)
查阅社区文档(Arena)
/ 实体是Box3中的游戏对象,用于对物体、玩家等的控制。
/ 无法(很难)被实例化,但可以通过其他方法获取其实例化对象

常用

id
player? isPlayer
mesh
position
motion
collides fixed gravity
friction

addTag hasTag removeTag
destroy
say

属性

基础

id:
实体名称,可重复
destroyed:

实体是否已被销毁

说明

destroyed属性在此处表明只读,是因为该属性一般只读取不写入,销毁实体应使用destroy方法,尝试写入可能会发生意外结果1

position: /
实体的位置
player?: /
/ 的入口,用于控制实体对应玩家属性
若实体是玩家,才会有该属性
isPlayer:
实体是否是玩家

外观

mesh:

实体的外形。格式为'mesh/*.vb'

玩家的该属性的值为''

提示与技巧

通过对玩家修改该属性,可以实现变形和骑乘效果
不过玩家本体仍会显示,可以修改玩家的player.invisiblefalse来解决玩家本体显示问题

示例
// 实现玩家变形效果
world.onPlayerJoin(({ entity }) => {
    entity.mesh = 'mesh/车.vb';
    entity.player.invisible = false; // 只显示车
});
meshOrientation: /
实体的旋转角度
meshScale: /
实体的缩放比例。其xyz1时,一个像素点为一个方块大
meshOffset: /
实体的位移
meshColor: /

实体的颜色,rgba的范围都是0~1

注意是RGBA颜色

提示与技巧

修改meshColor.a的值,可以让实体实现半透明的效果

meshMetalness:
实体的金属感
meshEmissive:
实体的发光度
meshShininess:
实体的反光度
motion:

实体动作控制器,具体见

Arena 独有

该方法仅在Arena编辑器中使用

物理

collides: = true
实体是否可碰撞

该属性的默认值为true,使用代码创建实体时应注意

fixed: = false

实体的位置是否固定不动

该属性的默认值为false,使用代码创建实体时应注意

一个很有意思的特性

众所周知,若该属性为false,实体和方块重叠时实体会被弹开
但如果在有方块的地方创建一个collidestruefixedfalsegravityfalse的实体(实体没有完全被方块包裹,一部分在空气中),且实体的速度为0,实体和方块可以在一个地方共存,除非受到外力或实体具有速度才会被弹开
若先创建实体,在放置方块之后修改实体速度为0有同样效果

另一个很有意思的特性

众所周知,若该属性为falsecollidestruegravityfalse,实体速度为0,实体和方块重叠时会形成一种不稳定的共存状态
但如果实体完全被埋在方块里面(其中一个表面和方块表面重合),这时你从这个实体与方块外接触的表面推动这个实体,这个实体和方块有概率可以共存,实体有概率弹出来
若使用代码更改速度,可以使实体稳定的在方块内移动,除非移动到方块边界
若实体没有表面和方块外面接触,使用代码更改速度,也可以使实体稳定的在方块内移动,除非移动到方块边界

gravity: = true

如果fixedfalse,则控制实体是否会因重力下落

警告

该属性的默认值为true,同时fixed的默认值为false,使用代码创建实体时应注意,避免应未修改属性导致实体被方块和其他实体推动

mass:
实体的质量(不是好坏那个质量)。数值越大,实体越重
friction: = 0

实体的摩擦系数,数值越大,摩擦力越大

警告

该属性的默认值为0
也就是说,默认实体和方块/其他实体没有任何摩擦,实体不会因方块摩擦而减速,也不会被传送带推动,使用代码创建实体时应注意

restitution: = 0
实体的弹性,数值越大,弹性越大
bounds: /

实体边界框的大小
无论OBB碰撞(仅Arena)是否开启,此处都是AABB碰撞箱计算结果

说明

bounds属性在此处表明只读,是因为该属性每帧都会重新计算,尝试写入可能什么也不会发生1

提示

2024/7/20 经实际测试,需要将计算结果数乘2才是实际碰撞箱大小
举个例子,在地面上放一个长、宽、高都为4格的实体,其bounds的值为{ x:2, y:2, z:2 }

velocity: /
实体的速度
contactForce: /
实体受到的碰撞力(合力)
entityContacts: [] / []
正在和玩家/实体发生碰撞的全部实体列表
voxelContacts: [] / []
正在和玩家/实体发生碰撞的全部方块列表
fluidContacts: [] / []
正在被玩家/实体触碰的全部液体方块列表

互动

enableInteract: = false
实体是否可互动
interactRadius: = 16
实体的可互动范围,默认值为16
interactHint: = ""
实体的互动提示
interactColor: /

实体的互动提示文字颜色

注意是RGB颜色

战斗相关

enableDamage: = false

实体是否可受到伤害
若为false,实体不会显示血量条,也不会因hurt方法血量发生变化,触发Box3DamageEvent / GameDamageEventBox3DieEvent / [GameDieEvent]事件

警告

该属性的默认值为false,无论使用代码创建还是手动创建,玩家也是这样,创建实体时应注意

hp: = 100

实体的当前生命值

提示

这个属性可以大于maxHp,也可以小于0,但是显示上会溢出

提示

这个属性一旦与maxHp的值不同,血条就会显示,无论大于还是小于

警告

直接修改属性不会触发受伤/死亡/复活(如果实体为玩家)事件

maxHp: = 100

实体的最大生命值

提示

这个属性并不会更改实体的血条显示长度

showHealthBar: = true

实体是否显示血量条

疑似无效

2024/7/21测试,血条的是否显示和该属性无关,该属性无效

粒子效果

particleRate: = 0

实体平均每秒产生粒子的数量

如果希望实体停止释放粒子,可以将该属性改为0

particleRateSpread: = 0

增加实体每秒产生粒子数量的随机性
如果设定了该属性的值,实体每一秒产生粒子的数量将不再是个固定值,而是从区间\([\)particleRate, particleRate \(+\) particleRateSpread\()\)里随机选取的一个整数。
例如,假设 particleRate0particleRateSpread3,每秒产生的粒子数量是\([0, 0+3)\) ,即\([0, 3)\)区间里的一个随机 整数,也就是可能为\(0\)\(1\),或\(2\)

\([,]\)\((,)\)\([,)\)\((,]\)

这是数学中用来表示范围的一种方法,中括号表示包括,小括号表示不包括
举几个例子,\([0, 10]\) 表示\(0\le...\le10\)这个区间
\([0, 10)\) 表示\(0\le...<10\)这个区间
\((0, 10]\) 表示\(0<...\le10\)这个区间
\((0, 10)\) 表示\(0<...<10\)这个区间

particleLimit: = 100
实体可产生的粒子总数的上限
particleLifetime: = 10
粒子的存活时间,以 为单位
particleLifetimeSpread: = 0
增加实体所产生粒子存活时间的随机性 如果设定了该属性的值,粒子的存活时间将不再是固定值,而是区间 \([\)particleLifetime, particleLifetime \(+\) particleLifetimeSpread\()\) 里的一个随机数,可能为小数
例如,假设 particleLifetime10particleLifetimeSpread5,粒子的存活时间是\([10, 10+5)\) ,即\([10, 15)\)区间里的一个随机数,也就是可能为\(10\)\(11\)\(12\)\(13\),或\(14\)
particleSize: [] = [1, 1, 1, 1, 1]
粒子的大小
该属性的值是一个长度为0 5的数组,分别对应粒子从产生到消失的五个阶段,第一个阶段为产生时的大小,第五个阶段为消失时的大小
若数组的长度小于5,则数组剩余部分对应的阶段粒子会显示为最小大小(可见,不是0)
若数组中填入了转换类型为的值,对应阶段粒子也会会显示为最小大小
数组多余部分无效
particleSizeSpread:
增加实体所产生粒子大小的随机性
如果设定了该属性,但没设定 particleSize 的值,每产生一个粒子,会从区间\([0,\) particleSizeSpread\()\)里选取的一个随机数作为它的大小
如果同时设定了 particleSizeparticleSizeSpread 两个属性,每产生一个粒子,从区间\([0,\) particleSizeSpread\()\)里选取一个随机数x,这个粒子第i个阶段的大小将为particleSize[i]\(+\)x
particleColor: [] = [ new Box3RGBColor(1, 1, 1), new Box3RGBColor(1, 1, 1), new Box3RGBColor(1, 1, 1), new Box3RGBColor(1, 1, 1), new Box3RGBColor(1, 1, 1) ] / [] = [ new GameRGBColor(1, 1, 1), new GameRGBColor(1, 1, 1), new GameRGBColor(1, 1, 1), new GameRGBColor(1, 1, 1), new GameRGBColor(1, 1, 1) ]

粒子颜色
该属性的值是一个长度为0 5的数组,分别对应粒子从产生到消失的五个阶段,第一个阶段为产生时的颜色,第五个阶段为消失时的颜色。粒子在阶段之间颜色会平滑地过渡
若数组的长度小于5,则数组剩余部分对应的阶段粒子会显示为白色
若数组中填入了非 / 类型的值,对应阶段粒子也会会显示为白色,同时控制台会报错刷屏
数组多余部分无效

Bug

若将颜色设为白色(new Box3RGBColor(1, 1, 1) / new GameRGBColor(1, 1, 1)),实际颜色只有#D7D7D7

particleVelocity: /
该实体产生的所有粒子的初始速度
particleVelocitySpread: /
增加该实体产生的所有粒子初始速度的不确定性
示例

// 蓄力发射
world.onPlayerJoin(({ entity }) => {
    Object.assign(entity, {
        particleRate: 100,
        particleSize: [2, 2, 2, 2, 10],
        particleLifetime: 2,
        particleVelocity: new Box3Vector3(0, 0, 50),
        particleVelocitySpread: new Box3Vector3(30, 2, 2),
    });
});
// 玩家向上播撒粒子
world.onPlayerJoin(({ entity }) => {
    entity.particleRate = 100;
    entity.particleLifetime = 30;
    entity.particleSize = [1, 2, 3, 4, 5]
    entity.particleVelocity.set(0, 1, 0);
    entity.particleVelocitySpread.set(5, 0, 5);
});

// 从玩家的位置扇形发射粒子
world.onPlayerJoin(({ entity }) => {
    Object.assign(entity, {
        particleRate: 100,
        particleSize: [2, 2, 2, 2, 10],
        particleLifetime: 2,
        particleVelocity: new GameVector3(0, 0, 50),
        particleVelocitySpread: new GameVector3(30, 2, 2),
    });
});
// 玩家向上播撒粒子
world.onPlayerJoin(({ entity }) => {
    entity.particleRate = 100;
    entity.particleLifetime = 30;
    entity.particleSize = [1, 2, 3, 4, 5]
    entity.particleVelocity.set(0, 1, 0);
    entity.particleVelocitySpread.set(5, 0, 5);
});

particleDamping:
  • 如果该属性的值为正数,会短暂减少该实体所产生粒子的初始速度,数值越大,减少初始速度的效果持续得越久
  • 如果为负值,会短暂增加粒子的初始速度,数值越小,增加初始速度的效果越明显
示例
// 蓄力发射
world.onPlayerJoin(({ entity }) => {
    Object.assign(entity, {
        particleRate: 100,
        particleSize: [2, 2, 2, 2, 10],
        particleLifetime: 2,
        particleVelocity: new Box3Vector3(0, 0, 50),
        particleVelocitySpread: new Box3Vector3(30, 2, 2),
        particleDamping: 3,
    });
});
// 蓄力发射
world.onPlayerJoin(({ entity }) => {
    Object.assign(entity, {
        particleRate: 100,
        particleSize: [2, 2, 2, 2, 10],
        particleLifetime: 2,
        particleVelocity: new GameVector3(0, 0, 50),
        particleVelocitySpread: new GameVector3(30, 2, 2),
        particleDamping: 3,
    });
});
particleAcceleration: /

粒子的加速度,单位为\(格/tick^2\)

加速度

加速度是用于描述物体速度变化快慢的物理量
一般用字母\(a\)表示,公式为\(v=v_0+at\),其中\(v_0\)是初始速度,\(v\)是瞬时速度,\(t\)为时间

示例
// 玩家牌喷泉
world.onPlayerJoin(({ entity }) => {
    entity.particleRate = 300;
    entity.particleLimit = 1000;
    entity.particleLifetime = 1.8;
    entity.particleDamping = -1;
    entity.particleSize = [1, 2, 4, 4, 4];
    entity.particleColor[0].set(1, 1, 1);
    entity.particleColor[1].set(0.5, 0.5, 1);
    entity.particleColor[2].set(0.1, 0.1, 1);
    entity.particleColor[3].set(0, 0, 1);
    entity.particleColor[4].set(0, 0, 1);
    entity.particleVelocity.set(0, 9, 0);
    entity.particleVelocitySpread.set(5, 1, 5);
    entity.particleAcceleration.set(0, -10, 0);
});
particleNoise:
粒子扰动幅度,数值越大,各个粒子的运动相对原有方向的偏离越明显
particleNoiseFrequency:
粒子扰动频率,数值越大,各个粒子的运动方向越没有规律
particleTarget: / |
粒子目标实体
particleTargetWeight:
粒子目标权重

声音

chatSound: /
实体说话时,播放的音效
chatSound.sample默认为'audio/chat.mp3'
hurtSound: /
实体触发受伤事件时,播放的音效
hurtSound.sample默认为'audio/hurt.mp3'
dieSound: /
实体触发死亡事件时,播放的音效
dieSound.sample默认为'audio/die.mp3'
interactSound: /
实体进行互动时,播放的音效
interactSound.sample默认为''

方法

基础

destroy(): []
销毁该实体
tags(): []

获取实体的所有标签

警告

尽管其命名非常像一个属性,但这其实是一个方法

addTag(tag: ):
给实体添加标签
hasTag(tag: ):
检查实体是否有某个标签
removeTag(tag: ):
给实体移除某个标签

外观

lookAt(targetPosition: , meshFacing?: "X" | "Y" | "Z", up?: ):

将实体旋转至面向指定位置的方向

Arena 独有

该方法仅在Arena编辑器中使用

提示

通过此方法进行的旋转会瞬时发生,仅影响实体的朝向,不会影响实体的运动状态。

参数 类型 说明
targetPosition 世界坐标,希望让实体朝向的位置
meshFacing "X" | "Y" | "Z" 定义模型在未旋转状态下的正方向
处理模型设计时未朝向Z轴时的情况:
当取X、Z时,定义模型的正方向分别为X、Z轴正方向,上方向为 Y 轴正方向
当取Y时,定义模型的正方向为Y轴正方向,上方向为Z轴正方向
默认值为Z,即模型设计时朝向Z轴正方向
up 上向量,默认取 Y 轴正方向

聊天

say(message: ):
让实体说话
会播放chatSound音效

战斗相关

hurt(amount: number, options?: < / >):

对实体造成伤害
与直接修改属性不同,该方法会触发受伤/死亡事件,实体也会有发光效果

提示

若实体的血量小于等于0,这个方法并不会有任何效果,也不会触发任何事件
amount大于实体的hp属性,实体的血量只会减小到0,即使实体伤害显示上的数值仍为amount的值

提示与技巧

amount的值为Infinity,伤害显示上的数值为0,但可以做到立刻击杀实体且能触发事件的效果(设置hp = 0不会触发事件)

动画

animate (keyframes: < / >[], playbackInfo?: < / >): / < / , / >
创建一个关键帧动画 /

Bug

2024/7/22在Arena编辑器中测试,使用动画修改meshColor属性时若使用类而不是数组,实体会变成完全透明,且和属性中a的值无关
直接修改属性(不使用动画)无问题
旧版编辑器中未知
这就是为什么,下面的例子中meshColor的属性使用数组

示例
    // 需要提前在编辑器中添加'单元方块-1'实体。 
    const vox = world.querySelector('#单元方块-1');          // 获取实体
    vox.meshScale = vox.meshScale.scale(4);                 // 让实体变大4倍
    const ani = vox.animate([
        { position: new Box3Vector3(0, 12, 0), meshColor: [1, 1, 0, 1] },
        { position: new Box3Vector3(0, 12, 127), meshColor: [1, 0, 0, 1] },
        { position: new Box3Vector3(127, 12, 127), meshColor: [0, 1, 0, 1] },
        { position: new Box3Vector3(127, 12, 0), meshColor: [0, 0, 1, 1] }
    ], {
        iterations: 3,                              //兜3圈
        direction: Box3AnimationDirection.WRAP,     // 从终点回到起点
        duration: 16 * 5                            //兜一圈5秒(每秒16帧)
    });
    ani.onReady(() => {             //当动画开始播放时
        world.say('开始兜圈');
    });
    ani.onFinish(() => {            //当动画结束播放时
        world.say('兜圈结束');
    });
    // 需要提前在编辑器中添加'单元方块-1'实体。 
    const vox = world.querySelector('#单元方块-1');          // 获取实体
    vox.meshScale = vox.meshScale.scale(4);                 // 让实体变大4倍
    const ani = vox.animate([
        { position: new GameVector3(0, 12, 0), meshColor: [1, 1, 0, 1] },
        { position: new GameVector3(0, 12, 127), meshColor: [1, 0, 0, 1] },
        { position: new GameVector3(127, 12, 127), meshColor: [0, 1, 0, 1] },
        { position: new GameVector3(127, 12, 0), meshColor: [0, 0, 1, 1] }
    ], {
        iterations: 3,                              //兜3圈
        direction: GameAnimationDirection.WRAP,     // 从终点回到起点
        duration: 16 * 5                            //兜一圈5秒(每秒16帧)
    });
    ani.onReady(() => {             //当动画开始播放时
        world.say('开始兜圈');
    });
    ani.onFinish(() => {            //当动画结束播放时
        world.say('兜圈结束');
    });
getAnimations(): / < / , / >[]
获取该实体的所有动画

声音

sound(spec: {sample: , radius?: , gain?: , pitch?: } | ):

在指定位置播放声音

参数 类型 说明
spec 声音路径
spec 声音播放参数
sample 声音路径
radius = 32 声音范围。距离实体越近,声音听的越清晰。超出范围的玩家则听不到声音
gain = 1 音量增益。正常为 1,数值越大,声音越大
pitch = 1 音高增益。正常为 1,大于 1,音调越高,播放速度越快

事件

交互

onClick: / < / >
nextClick: / < / >
当玩家点击该实体(或未来)触发
onInteract: / < / >
nextInteract: / < / >
当玩家与该实体互动(或未来)触发

物理

onEntityContact: / < / >
nextEntityContact: / < / >
当该实体碰撞其他实体(或未来)触发
onEntitySeparate: / < / >
nextEntitySeparate: / < / >
当该实体和其他实体分开(或未来)触发
onVoxelContact: / < / >
nextVoxelContact: / < / >
当该实体碰到方块(或未来)触发
onVoxelSeparate: / < / >
nextVoxelSeparate: / < / >
当该实体离开方块(或未来)触发
onFluidEnter: / < / >
nextFluidEnter: / < / >
当该实体进入液体(或未来)触发
onFluidLeave: / < / >
nextFluidLeave: / < / >
当该实体离开液体(或未来)触发

战斗相关

onTakeDamage: / < / >
nextTakeDamage: / < / >
当该实体收到伤害(或未来)触发
onDie: / < / >
nextDie: / < / >
当该实体死亡(或未来)触发

  1. 未测试 

评论区