跳转至

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:

玩家是否隐身
只会影响玩家模型和穿戴配件

示例

// 让玩家隐形
world.onPlayerJoin(({ entity }) => {
    entity.player.invisible = true;
});
// 实现玩家变形效果
world.onPlayerJoin(({ entity }) => {
    entity.mesh = 'mesh/车.vb';
    entity.player.invisible = false; // 只显示车
});

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: /

隐藏玩家模型部件接口

示例
// 隐藏玩家的头部
world.onPlayerJoin(({ entity }) => {
    entity.player.skinInvisible.head = true;
});
showName: = true
是否显示玩家名称
showIndicator: = false

是否显示玩家标记

Arena 独有

该属性仅在Arena编辑器中使用

scale: = 1

玩家的缩放比例,1为正常大小

示例
// 玩家进入游戏的时候缩小到0.25倍
world.onPlayerJoin(({ entity }) => {
    entity.player.scale = 0.25;
});
emissive:

玩家的发光度,范围\([0, 1]\)

警告

该属性只会影响玩家本身会显示的模型(通过invisible控制是否显示的那个模型),而不是mesh那个模型

示例
// 使玩家发光
world.onPlayerJoin(({ entity }) => {
    entity.player.emissive = 1;
});
metalness:

玩家的金属感

警告

该属性只会影响玩家本身会显示的模型(通过invisible控制是否显示的那个模型),而不是mesh那个模型

shininess:

玩家的反光度

警告

该属性只会影响玩家本身会显示的模型(通过invisible控制是否显示的那个模型),而不是mesh那个模型

聊天

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():
清除地图对此玩家应用的皮肤配置,将此玩家的皮肤配置为仅展示玩家自己的皮肤。

内容待补充

未来将补充resetToDefaultSkinclearSkin的区别

addWearable(spec: < / >): /

在玩家某身体部位附上穿戴配件物体

警告

该函数返回的值才是玩家实际穿上的穿戴配件(你可以理解为你填入的spec本身没有穿到玩家身上,穿到玩家身上的是spec的复制品,该函数返回该复制品)
若想移除该穿戴配件,应该在removeWearable中填入该函数的返回值,而不是你填入的参数

示例
world.onPlayerJoin(({ entity }) => {
    entity.player.addWearable({
        bodyPart: Box3BodyPart.TORSO,
        mesh: 'mesh/黄色书包.vb',
        orientation: new Box3Quaternion(0, 1, 0, 0).rotateY(Math.PI/2),
        scale: new Box3Vector3(0.5, 0.5, 0.5),
        offset: new Box3Vector3(0, 0, -0.45),
    });
});
world.onPlayerJoin(({ entity }) => {
    entity.player.addWearable({
        bodyPart: GameBodyPart.TORSO,
        mesh: 'mesh/黄色书包.vb',
        orientation: new GameQuaternion(0, 1, 0, 0).rotateY(Math.PI/2),
        scale: new GameVector3(0.5, 0.5, 0.5),
        offset: new GameVector3(0, 0, -0.45),
    });
});
removeWearable(wearable: / ):

在玩家某身体部位移除穿戴配件物体

警告

wearable参数应该填写的是addWearablewearables方法的返回值,而不是参数!!!

示例
// 在玩家进入液体时把穿戴配件'潜水镜'在玩家头上附带
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? / 选填,若填写,则只列举所填部位的所有穿戴部件
返回值 类型 说明
[] / [] 获取的所有穿戴配件

警告

虽然这个方法的命名很像一个属性,但其实这是一个方法
具体见下面的示例

错误示例
// 玩家按下右键时,告诉玩家所有穿戴配件的数量
world.onPress(({ entity, button }) => {
    if(button === 'action1') {
        entity.player.directMessage(`你有 ${entity.player.wearables.length} 个穿戴配件`);
    }
});
正确示例
// 玩家按下右键时,告诉玩家所有穿戴配件的数量
world.onPress(({ entity, button }) => {
    if(button === 'action1') {
        entity.player.directMessage(`你有 ${entity.player.wearables().length} 个穿戴配件`);
    }
});
示例
// 玩家按下右键时,告诉玩家头上穿戴配件的数量
world.onPress(({ entity, button }) => {
    if(button === 'action1') {
        entity.player.directMessage(`你头上有 ${entity.player.wearables('head').length} 个穿戴配件`);
    }
});

聊天

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编辑器中使用

事件

聊天

onChat : / < / >
nextChat : / < / >
当玩家发言(或未来)触发

复活

onRespawn : / < / >
nextRespawn : / < / >
玩家复活(或未来)触发

输入

onPress : / < / >
nextPress : / < / >
当玩家按下按键(或未来)触发
onRelease : / < / >
nextRelease : / < / >
当玩家松开按键(或未来)触发
onKeyDown : <>

当玩家按下键盘按键触发

Arena 独有

该事件仅在Arena编辑器中使用

onKeyUp : <>

当玩家松开键盘按键触发

Arena 独有

该事件仅在Arena编辑器中使用

评论区