Box3Player / GamePlayer 玩家
这是一个服务端API
该API仅在服务端脚本使用
- 查阅官方文档
查阅官方文档(Arena)
查阅社区文档(Arena) - / 指的是进入游戏的用户,此接口可用定义游戏中的玩家属性、操作等等。玩家属于一种特殊的实体。
/ 无法(很难)被实例化,但可以通过.player / .player获取其实例化对象
属性¶
基础¶
- name:
- 玩家的昵称,无法通过代码修改
- boxId:
- 玩家的Box ID(3-15字符)。无法通过代码修改。 游客的boxID值为空"" 。
在旧岛中为玩家个人主页链接/u/
之后内容
在新岛中类似userKey,但是内容不同 - userKey:
- 玩家的唯一识别码(16字符),可以用于存储玩家信息到数据库,无法通过代码修改。 游客的userKey值为空"" 。
在新岛中,若玩家的账户是由旧岛迁移过来,玩家的userKey不变 - userId:
-
玩家的用户名,无法通过代码修改
在新岛中,为玩家个人主页的ID(链接中/profile/
之后内容)
虽然为一串数字,但仍然为类型 编者吐槽:我也不知道这东西为什么要隐藏Arena 独有
该属性仅在Arena编辑器中使用
- url:
-
获取该玩家进入地图时所用的URL链接地址,无法通过代码修改
提示
可以通过URL参数, 以便区别对待进来的玩家
- avatar:
-
玩家头像URL,无法通过代码修改
Arena 独有
该属性仅在Arena编辑器中使用
- spawnPoint: /
- 玩家的出生点
- movementBounds: /
- 玩家的活动范围限制,如超出此范围,则传回出生点spawnPoint
- navigator:
- 用户设备相关的接口
外观¶
- color: /
-
玩家的颜色
- invisible:
-
玩家是否隐身
只会影响玩家模型和穿戴配件 - skin:
-
此玩家的皮肤配置,用于管理当前玩家皮肤的展示。
当皮肤名称不存在于项目皮肤库或不符合类型定义时,无事发生,并会在控制台打印警告。Arena 独有
该属性仅在Arena编辑器中使用
提示
通过此处修改皮肤不会影响皮肤的隐藏状态,设置前是隐藏的,设置后也还是隐藏着。
示例
//...假设以某种方式得到了player对象,项目皮肤库中有 Example 和 Example2 两套皮肤 // 设置玩家头部使用一个叫 Example 的皮肤套装 player.skin.head = 'Example'; // 设置玩家躯干使用一个叫 Example2 的皮肤套装 player.skin.torso = 'Example2'; // 不小心把head又设置了一个项目中没有的皮肤套装名 WrongSkin player.skin.head = 'WrongSkin'; // 这里会在控制台打印警告,且不会产生任何效果 // 获取玩家当前皮肤 const skin = player.skin; // 此时 skin 为 { head:'Example', torso: 'Example2'} // 表现为一个头部为Example,躯干为Example2,剩余部位为玩家自己的皮肤。 // 而设置错误的皮肤套装名不会生效,也不会覆盖原有的值。 player.skin.head = undefined; // 将头部展示为玩家自己的皮肤。
- skinInvisible: /
-
隐藏玩家模型部件接口
- showName: =
true
- 是否显示玩家名称
- showIndicator: =
false
-
是否显示玩家标记
Arena 独有
该属性仅在Arena编辑器中使用
- scale: =
1
-
玩家的缩放比例,
1
为正常大小 - emissive:
-
玩家的发光度,范围\([0, 1]\)
- metalness:
-
玩家的金属感
- shininess:
-
玩家的反光度
聊天¶
- muted:
-
玩家是否被禁言
不适用于Arena编辑器
该属性在Arena编辑器已弃用
战斗相关¶
- dead: =
false
-
玩家是否已死亡(触发过死亡事件且还没触发过重生事件)
摄像机相关¶
- cameraEntity: /
- 玩家第一人称视角(
'fps'
)、第三人称跟随视角('follow'
)或相对视角('relative'
)下,玩家视角所跟随的实体 - cameraMode: /
- 玩家的相机视角模式
- cameraPosition: /
- 玩家固定视角(
'fixed'
)或相对视角('relative'
)下,镜头的位置 - cameraTarget: /
- 玩家固定视角(
'fixed'
)或相对视角('relative'
)下,镜头所朝向的目标点 - cameraUp: =
new Box3Vector3(0, 1, 0)
/ =new GameVector3(0, 1, 0)
- 玩家固定视角(
'fixed'
)或相对视角('relative'
)下,镜头向上的矢量 - cameraFovY: =
0.25
- 玩家垂直方向的视场角,在任何相机视角模式都适用
- cameraFreezedAxis: =
''
/ =''
- 玩家在相对视角(
'relative'
)下,冻结的摄像机轴 - cameraDistance:
- 第三人称跟随视角(
'follow'
)下,玩家摄像机到玩家实体的距离 - colorLUT:
- 玩家的画面滤镜,格式为
lut/*.lut
- freezedForwardDirection: / |
- 将玩家摄像机的方向沿y轴固定,默认为
若为,不固定摄像机;否则玩家的摄像机只能沿设定方向上下移动 - cameraPitch:
-
玩家视角准心绕x轴(视角的上下移动)的旋转**弧度**
Arena 独有
该属性仅在Arena编辑器中使用
- cameraYaw:
-
玩家视角准心绕y轴(视角的左右移动)的旋转**弧度**
Arena 独有
该属性仅在Arena编辑器中使用
输入¶
- enableAction0: =
true
- 是否启用Action0键
电脑端为鼠标左键,移动端为“A”按钮 - enableAction1: =
true
- 是否启用Action1键
电脑端为鼠标右键,移动端为“B”按钮 - enableJump: =
true
- 是否允许玩家跳跃(按下跳跃键)
- enableCrouch: =
true
-
是否允许玩家蹲下(按下蹲下键)
Arena 独有
该属性仅在Arena编辑器中使用
- enableDoubleJump: =
true
- 是否允许玩家二段跳跃
- action0Button:
- 玩家是否按下Action0键 电脑端为鼠标左键,移动端为“A”按钮
- action1Button:
- 玩家是否按下Action1键 电脑端为鼠标右键,移动端为“B”按钮
- crouchButton:
- 玩家是否按下蹲下键
- jumpButton:
- 玩家是否按下跳跃键
- disableInputDirection: /
- 若玩家为移动端,禁用指定方向的摇杆输入偏移量,当横纵两个方向均被禁用时,将不显示此玩家的触屏虚拟摇杆。
若玩家为电脑端,禁用指定方向的方向键 - swapInputDirection: =
false
- 是否交换垂直和水平的输入方向
- reverseInputDirection: =
false
- 是否反转输入方向
- enable3DCursor: =
false
- 是否启动玩家的3D光标
运动相关¶
- canFly: =
false
- 是否允许玩家飞行
- spectator: =
false
- 是否启用旁观者模式
若为true
,则玩家可以穿墙和飞行(什么飞行挂) - walkSpeed: =
0.22
- 最大步行速度
- walkAcceleration: =
0.19
- 步行加速度
- runSpeed: =
0.4
- 最大跑步速度
- runAcceleration: =
0.35
- 跑步加速度
- crouchSpeed: =
0.1
- 最大潜行速度
- crouchAcceleration: =
0.09
- 潜行加速度
- flySpeed: =
2
- 最大飞行速度
- flyAcceleration: =
2
- 飞行加速度
- swimSpeed: =
0.4
- 最大游泳速度
- swimAcceleration: =
0.1
- 游泳加速度
- jumpPower: =
0.96
- 跳跃力度
- jumpSpeedFactor: =
0.85
- 跳跃时的水平速度
- jumpAccelerationFactor: =
0.55
- 跳跃加速率
- doubleJumpPower: =
0.9
- 二段跳跃力度
- moveState: /
- 只读,玩家的移动状态
- walkState: /
- 只读,玩家的步行状态
声音¶
- action0Sound: /
- 当玩家按下Action0(鼠标左键/移动端A键)键时,播放的声音
action0Sound.sample默认为''
- action1Sound: /
- 当玩家按下Action1(鼠标右键/移动端B键)键时,播放的声音 action1Sound.sample默认为
''
- crouchSound: /
- 当玩家按下蹲下键时,播放的声音 crouchSound.sample默认为
''
- jumpSound: /
- 当玩家按下跳跃键时,播放的声音 jumpSound.sample默认为
'audio/jump.mp3'
- doubleJumpSound: /
- 当玩家二段跳时,播放的声音 doubleJumpSound.sample默认为
'audio/double_jump.mp3'
- landSound: /
- 当玩家落地时,播放的声音 landSound.sample默认为
'audio/land.mp3'
- enterWaterSound: /
- 当玩家进入液体时,播放的声音 enterWaterSound.sample默认为
'audio/dive.mp3'
- leaveWaterSound: /
- 当玩家离开液体时,播放的声音 leaveWaterSound.sample默认为
'audio/splash.mp3'
- swimSound: /
- 当玩家游泳时,播放的声音;若玩家在水中停留,则不会播放声音 swimSound.sample默认为
'audio/swim.mp3'
- spawnSound: /
- 当玩家重生时,播放的声音 spawnSound.sample默认为
'audio/land.mp3'
- stepSound: /
- 当玩家行走时,播放的声音 stepSound.sample默认为
'audio/step.mp3'
- startFlySound: /
- 当玩家开始飞行时,播放的声音 startFlySound.sample默认为
'audio/land.mp3'
- stopFlySound: /
- 当玩家结束飞行时,播放的声音 stopFlySound.sample默认为
'audio/land.mp3'
- music: /
-
该玩家背景音乐,仅该玩家能听见
警告
若同时在设置中设置了背景音乐和music的值,两种音乐会同时播放
方法¶
基础¶
- kick() =>
-
将该玩家移出地图
警告
该方法十分危险,对编辑端和游玩端都有效,在地图运行前需要检查代码中的该方法,否则可能会造成无法进入地图的问题!
外观¶
- setSkinByName(skinName: ):
-
将指定皮肤套装应用到此玩家上。
当皮肤名称不存在于项目皮肤库或不符合类型定义时,无事发生,并会在控制台打印警告。Arena 独有
该属性仅在Arena编辑器中使用
提示
通过此处修改皮肤不会影响皮肤的隐藏状态,设置前是隐藏的,设置后也还是隐藏着。
- resetToDefaultSkin():
- 重置此玩家的皮肤配置为默认皮肤配置,效果同setSkinByName传入了默认皮肤套装名称。
- clearSkin():
- 清除地图对此玩家应用的皮肤配置,将此玩家的皮肤配置为仅展示玩家自己的皮肤。
内容待补充
未来将补充resetToDefaultSkin和clearSkin的区别
- addWearable(spec: < / >): /
-
在玩家某身体部位附上穿戴配件物体
警告
该函数返回的值才是玩家实际穿上的穿戴配件(你可以理解为你填入的spec本身没有穿到玩家身上,穿到玩家身上的是spec的复制品,该函数返回该复制品)
若想移除该穿戴配件,应该在removeWearable中填入该函数的返回值,而不是你填入的参数示例
- removeWearable(wearable: / ):
-
在玩家某身体部位移除穿戴配件物体
警告
wearable参数应该填写的是addWearable或wearables方法的返回值,而不是参数!!!
示例
// 在玩家进入液体时把穿戴配件'潜水镜'在玩家头上附带 world.onFluidEnter(({ entity }) => { if (!entity.isPlayer) return; entity.player.addWearable({ bodyPart: Box3BodyPart.HEAD, mesh: 'mesh/潜水镜.vb', orientation: new Box3Quaternion(0, 1, 0, 0), scale: new Box3Vector3(1, 1, 1), offset: new Box3Vector3(0, 0, 0.5), }); }); // 在玩家离开液体时把在玩家头上的穿戴配件删除 world.onFluidLeave(({ entity }) => { if (!entity.isPlayer) return; const headWears = entity.player.wearables(Box3BodyPart.HEAD); // 假设只有1个装备 `headWears[0]` entity.player.removeWearable(headWears[0]); });
// 在玩家进入液体时把穿戴配件'潜水镜'在玩家头上附带 world.onFluidEnter(({ entity }) => { if (!entity.isPlayer) return; entity.player.addWearable({ bodyPart: GameBodyPart.HEAD, mesh: 'mesh/潜水镜.vb', orientation: new GameQuaternion(0, 1, 0, 0), scale: new GameVector3(1, 1, 1), offset: new GameVector3(0, 0, 0.5), }); }); // 在玩家离开液体时把在玩家头上的穿戴配件删除 world.onFluidLeave(({ entity }) => { if (!entity.isPlayer) return; const headWears = entity.player.wearables(GameBodyPart.HEAD); // 假设只有1个装备 `headWears[0]` entity.player.removeWearable(headWears[0]); });
错误示例
// 尝试玩家进入地图后把穿戴配件'潜水镜'在玩家头上附带,5s后移除 world.onPlayerJoin(async ({ entity }) => { let wearable = { bodyPart: Box3BodyPart.HEAD, mesh: 'mesh/潜水镜.vb', orientation: new Box3Quaternion(0, 1, 0, 0), scale: new Box3Vector3(1, 1, 1), offset: new Box3Vector3(0, 0, 0.5), } entity.player.addWearable(wearable); // 可以正常添加 await sleep(5e3); entity.player.removeWearable(wearable); // 由于填写的参数wearable是addWearable方法的参数而不是返回值,无法移除穿戴配件 });
// 尝试玩家进入地图后把穿戴配件'潜水镜'在玩家头上附带,5s后移除 world.onPlayerJoin(async ({ entity }) => { let wearable = { bodyPart: GameBodyPart.HEAD, mesh: 'mesh/潜水镜.vb', orientation: new GameQuaternion(0, 1, 0, 0), scale: new GameVector3(1, 1, 1), offset: new GameVector3(0, 0, 0.5), } entity.player.addWearable(wearable); // 可以正常添加 await sleep(5e3); entity.player.removeWearable(wearable); // 由于填写的参数wearable是addWearable方法的参数而不是返回值,无法移除穿戴配件 });
正确示例
// 尝试玩家进入地图后把穿戴配件'潜水镜'在玩家头上附带,5s后移除 world.onPlayerJoin(async ({ entity }) => { let wearable = { bodyPart: Box3BodyPart.HEAD, mesh: 'mesh/潜水镜.vb', orientation: new Box3Quaternion(0, 1, 0, 0), scale: new Box3Vector3(1, 1, 1), offset: new Box3Vector3(0, 0, 0.5), } let w = entity.player.addWearable(wearable); // 可以正常添加 await sleep(5e3); entity.player.removeWearable(w); // 由于填写的参数w是addWearable方法的返回值,能够移除穿戴配件 });
// 尝试玩家进入地图后把穿戴配件'潜水镜'在玩家头上附带,5s后移除 world.onPlayerJoin(async ({ entity }) => { let wearable = { bodyPart: GameBodyPart.HEAD, mesh: 'mesh/潜水镜.vb', orientation: new GameQuaternion(0, 1, 0, 0), scale: new GameVector3(1, 1, 1), offset: new GameVector3(0, 0, 0.5), } let w = entity.player.addWearable(wearable); // 可以正常添加 await sleep(5e3); entity.player.removeWearable(w); // 由于填写的参数w是addWearable方法的返回值,能够移除穿戴配件 });
- wearables(bodyPart?: / ): [] / []
-
列举在玩家上所有的穿戴配件物体
该方法获取的穿戴部件可以用于removeWearable方法参数 类型 说明 bodyPart? / 选填,若填写,则只列举所填部位的所有穿戴部件 返回值 类型 说明 [] / [] 获取的所有穿戴配件 警告
虽然这个方法的命名很像一个属性,但其实这是一个方法
具体见下面的示例错误示例
正确示例
聊天¶
- directMessage(message: []) =>
- 向玩家私信
对话框¶
- dialog: /
-
打开一个对话框
示例最多的一次
该方法的示例是全文档中最多的,共9个正确示例+1个错误示例
错误示例
world.onPlayerJoin(({ entity }) => { var result = entity.player.dialog({ // 此处无await,对话框还没关闭就会执行之后的代码;同时result是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗`, options: ['让我看看!', '下次一定'] }); if(result && result.index === 0) { // 此处也没有await,result是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 entity.player.dialog({ // 此处没有await,下一个对话框会在这个对话框弹出之后立刻弹出,而不是这个对话框关闭后弹出 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }); entity.player.dialog({ // 此处没有await,之后代码会在这个对话框弹出之后立刻执行,而不是这个对话框关闭后执行 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); } });
正确示例
下面是上面的错误示例的几种正确写法
实际使用时,可以将await
和搭配使用,而不是像下面只使用一种world.onPlayerJoin(async ({ entity }) => { // 注意此处有个async,否则后面不能用await var result = await entity.player.dialog({ // 此处有await,result是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗`, options: ['让我看看!', '下次一定'] }); if(result && result.index === 0) { await entity.player.dialog({ // 此处若没有await,下一个对话框会在这个对话框弹出之后立刻弹出,而不是这个对话框关闭后弹出。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }); await entity.player.dialog({ // 此处若没有await,之后代码会在这个对话框弹出之后立刻执行,而不是这个对话框关闭后执行。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); } });
world.onPlayerJoin(async ({ entity }) => { // 注意此处有个async,否则后面不能用await var result = entity.player.dialog({ // 此处没有await,result是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗`, options: ['让我看看!', '下次一定'] }); if((await result) && (await result).index === 0) { // 此处有await,(await result)是选择结果。注意:这会堵塞下面的代码,只有在对话框关闭之后才会继续执行下面的代码 await entity.player.dialog({ // 此处若没有await,下一个对话框会在这个对话框弹出之后立刻弹出,而不是这个对话框关闭后弹出。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }); await entity.player.dialog({ // 此处若没有await,之后代码会在这个对话框弹出之后立刻执行,而不是这个对话框关闭后执行。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); } });
world.onPlayerJoin(async ({ entity }) => { // 注意此处有个async,否则后面不能用await var dialog = entity.player.dialog({ // 此处没有await,result是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗(5s后关闭)`, options: ['让我看看!', '下次一定'] }); // 不能放在await dialog之后,因为await dialog会堵塞代码,也就是说,放在await dialog之后才会启动setTimeout setTimeout(() => { // 这里也可以换成异步函数 dialog.cancel(); }, 5e3); let result = await dialog; // 此处有await,result是选择结果。注意:这会堵塞下面的代码,只有在对话框关闭之后才会继续执行下面的代码 if(result && result.index === 0) { await entity.player.dialog({ // 此处若没有await,下一个对话框会在这个对话框弹出之后立刻弹出,而不是这个对话框关闭后弹出。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }); await entity.player.dialog({ // 此处若没有await,之后代码会在这个对话框弹出之后立刻执行,而不是这个对话框关闭后执行。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); } });
world.onPlayerJoin(async ({ entity }) => { // 注意此处有个async,否则后面不能用await var dialog = entity.player.dialog({ // 此处没有await,result是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗(5s后关闭)`, options: ['让我看看!', '下次一定'] }); (async () => { if((await dialog) && (await dialog).index === 0) { // 此处有await,(await result)是选择结果。注意:这会堵塞下面的代码,只有在对话框关闭之后才会继续执行下面的代码 await entity.player.dialog({ // 此处若没有await,下一个对话框会在这个对话框弹出之后立刻弹出,而不是这个对话框关闭后弹出。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }); await entity.player.dialog({ // 此处若没有await,之后代码会在这个对话框弹出之后立刻执行,而不是这个对话框关闭后执行。文本对话框是否有await应根据实际情况而定 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); } })(); // 这时就可以放到下面了 setTimeout(() => { // 这里也可以换成异步函数 dialog.cancel(); }, 5e3); });
world.onPlayerJoin(({ entity }) => { // 此处无需async var dialog = entity.player.dialog({ // 此处没有await,dialog是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗`, options: ['让我看看!', '下次一定'] }); dialog.then((result) => { // 此处不会堵塞代码的运行,下面的代码可以继续运行 if(result && result.index === 0) { entity.player.dialog({ // 对话框会依次打开 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }).then((resolve) => { entity.player.dialog({ type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); }); } }); });
world.onPlayerJoin(({ entity }) => { // 此处无需async var dialog = entity.player.dialog({ // 此处没有await,dialog是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗`, options: ['让我看看!', '下次一定'] }); dialog.then((result) => { // 此处不会堵塞代码的运行,下面的代码可以继续运行 if (result && result.index === 0) return result; else throw ""; }).then(() => entity.player.dialog({ // 对话框会依次打开 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true })).then(() => entity.player.dialog({ type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false })).catch(() => { console.log('关闭对话框'); }); });
world.onPlayerJoin(({ entity }) => { // 此处无需async var dialog = entity.player.dialog({ // 此处没有await,dialog是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗`, options: ['让我看看!', '下次一定'] }); dialog.then((result) => { // 此处不会堵塞代码的运行,下面的代码可以继续运行 if (result && result.index === 0) return result; else throw ""; }).then(() => { entity.player.dialog({ // 对话框会一起打开 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }); return; }).then(() => { entity.player.dialog({ type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); return; }).catch(() => { console.log('关闭对话框'); }); });
world.onPlayerJoin(({ entity }) => { // 此处无需async var dialog = entity.player.dialog({ // 此处没有await,dialog是一个Promise & (Box3DialogCancelOption / GameDialogCancelOption),而不是选择结果 type: 'select', title: '系统', content: `${entity.player.name},你想看看box3-docs的更新日志吗(5s后关闭)`, options: ['让我看看!', '下次一定'] }); dialog.then((result) => { // 此处不会堵塞代码的运行,下面的代码可以继续运行 if (result && result.index === 0) return result; else throw ""; }).then(() => { entity.player.dialog({ // 对话框会一起打开 type: 'text', title: 'box3-docs 更新日志', content: "新增Box3World / GameWorld页面\n新增Box3Entity / GameEntity页面\n新增Box3Player / GamePlayer页面\n新增db & Box3Database页面", hasArrow: true }); return; }).then(() => { entity.player.dialog({ type: 'text', title: 'box3-docs 更新日志', content: "新增Box3Vector3 / GameVector3页面\n新增Box3Bounds3 / GameBounds3页面\n新增Box3RGBColor / GameRGBColor页面\n新增Box3RGBAColor / GameRGBAColor页面", hasArrow: false }); return; }).catch(() => { console.log('关闭对话框'); }); // 放在dialog.then上面也行 setTimeout(() => { // 这里也可以换成异步函数 dialog.cancel(); }, 5e3); });
示例
// 先在场景中放置一个名称为 NPC 的实体。 const npc = world.querySelector('#npc'); npc.enableInteract = true; npc.interactHint = npc.id; npc.interactRadius = 16; // 玩家与实体进行交互时触发 npc.onInteract(async ({ entity }) => { const result = await entity.player.dialog({ type: 'select', title: npc.id, lookTarget: npc, content: `${entity.player.name},你想尝试挑战这个游戏吗?`, options: ['当然', '下次吧。'] }); console.log(`选择了第 index: ${result.index} 个选项: ${result.value}`) // 如果选了第一个选项,也就是'当然'。就会执行特定事件 if (result.index === 0) { npc.say(`${result.value}?那就来吧!`); } });
- cancelDialogs():
- 关闭玩家所有打开的对话框
= (
(params: ) => < | > & |
(params: ) => < | > & |
(params: ) => < | > &
)
= (
(params: ) => < | > & |
(params: ) => < | > & [] |
(params: ) => < | > &
)
复活¶
- forceRespawn():
- 将玩家的血量回满,并将玩家传送回出生点
会触发 / 事件
摄像机相关¶
- setCameraPitch(v: ):
-
设置玩家视角准心绕x轴(视角的上下移动)的旋转 弧度
Arena 独有
该方法仅在Arena编辑器中使用
- setCameraYaw(v: ):
-
设置玩家视角准心绕y轴(视角的左右移动)的旋转 弧度
Arena 独有
该方法仅在Arena编辑器中使用
动画¶
animate (keyframes: < / >[], playbackInfo?: < / >): / < / , / >- 创建一个关键帧动画 /
- getAnimations(): / < / , / >[]
- 获取该玩家的所有动画
网页 & Web¶
- link(href: , options?: {isConfirm?: , isNewTab?: }):
-
在玩家弹出一个“传送门”窗口,可以跳转到其他地图或任意链接
目前仅支持神奇代码岛、编程猫、微信文章、Bilibili视频等链接
参数 类型 说明 href 要跳转的链接 options? 传送门窗口配置选项 isConfirm 是否显示确定对话框 isNewTab 是否在新标签页打开 警告
该方法若使用不当,可能会直接跳转到其他链接,使用时应注意
- postMessage(event: {type: , value: , isOld: }):
- 向 iframe 父对象发布消息
- addEventListener(type: , listener: (event: {data: }) => ):
- 为 iframe 父对象的消息事件添加监听器
声音¶
- sound(spec: {sample: , gain?: , pitch?: } | ):
-
对玩家播放声音
参数 类型 说明 spec 声音路径 spec 声音播放参数 sample 声音路径 gain = 1
音量增益。正常为 1,数值越大,声音越大 pitch = 1
音高增益。正常为 1,大于 1,音调越高,播放速度越快
社交¶
- share(content: ):
-
为该玩家显示分享弹窗
可以通过content自定义分享内容,上限40字符。Arena 独有
该方法仅在Arena编辑器中使用
自动标签
通过此 API 调用弹出的分享弹框的内容末尾,会自动新起一行添加
#神奇代码岛 #地图
标签,自动添加的内容不计入限制长度内。编辑模式下分享的地址是编辑器的地址
- querySocial(socialType: ): <[]>
-
查询当前玩家的社交关系,返回玩家 ID 列表
Arena 独有
该方法仅在Arena编辑器中使用
- openUserProfileDialog(userId: ):
-
对当前玩家,调起指定ID玩家的个人主页
Arena 独有
该方法仅在Arena编辑器中使用
提示
虽然userId所标类型为,但实际上也可使用
商业化¶
- openMarketplace(productIds: ()):
-
打开游戏商店对话框,根据玩家传入的“商品ID”显示相应的产品
Arena 独有
该方法仅在Arena编辑器中使用
- getMiaoShells() => <>
-
获取此用户在当前地图下累计打赏的喵贝壳
Arena 独有
该方法仅在Arena编辑器中使用
事件¶
聊天¶
复活¶
- onRespawn : / < / >
- nextRespawn : / < / >
- 玩家复活(或未来)触发
输入¶
- onPress : / < / >
- nextPress : / < / >
- 当玩家按下按键(或未来)触发
- onRelease : / < / >
- nextRelease : / < / >
- 当玩家松开按键(或未来)触发
- onKeyDown : <>
-
当玩家按下键盘按键触发
Arena 独有
该事件仅在Arena编辑器中使用
- onKeyUp : <>
-
当玩家松开键盘按键触发
Arena 独有
该事件仅在Arena编辑器中使用