# 装备宠物与皮肤

# 01 可穿戴装备

(点击->高清B站视频) (opens new window)

视频中的代码如下: (使用时需注意:模型的名称需与代码中的名称保持一致)

world.onPlayerJoin(({entity}) => {
    entity.player.addWearable({
        bodyPart: Box3BodyPart.HEAD,
        mesh: 'mesh/三级盔.vb',            //使用代码时,此处的名称需与地图中的模型名称保持一致
        orientation: new Box3Quaternion(0,1,0,0).rotateY(-Math.PI/2),
        offset: new Box3Vector3(0,0.4,0.1),
    });
    entity.player.addWearable({
        bodyPart: Box3BodyPart.RIGHT_HAND,
        mesh: 'mesh/95式 [精美].vb',       //使用代码时,此处的名称需与地图中的模型名称保持一致
        orientation: new Box3Quaternion(0,1,0,0).rotateY(Math.PI),
        offset: new Box3Vector3(-0.05,-0.1,0.5),
        scale: Box3Vector3 = new Box3Vector3(0.3, 0.3, 0.3),
    });
});

# 02 可跟随小精灵

(点击->高清B站视频) (opens new window)

console.clear()
const mscale = 1 / 16
const Quat = new Box3Quaternion(0, 0, 0, 1)

function buddyFollow(entity, mesh, y) {
    const buddy = world.createEntity({
        mesh,
        position: entity.position,
        meshScale: [mscale, mscale, mscale],
        gravity: false, //不受重力影响
        fixed: false, //可推移
        collides: true, //可碰撞
        friction: 0, //无摩擦力
        mass: 0.01, //非常轻
    })

    const tgPos = entity.position//僚机的目标位置
    const budPos = buddy.position//僚机的当前位置
    const facing = entity.player.facingDirection//玩家的朝向
    const ratio = 0.3//追随的灵敏度, 最好设在0.5左右, 1.0表示立即移到玩家位置

    const dist = 2 //与玩家保持的距离
    const yOffset = y //y轴位移, 保持僚机在头顶或脚下

    const ticker = world.onTick(() => {

        //要让小精灵跟在玩家背后, 需要计算xz轴的位移: 玩家朝向的反方向
        const xOffset = -facing.x * dist
        const zOffset = -facing.z * dist

        //当前位置与目标位置在xyz轴的差距
        const xDiff = tgPos.x - budPos.x
        const yDiff = tgPos.y - budPos.y
        const zDiff = tgPos.z - budPos.z

        //计算xyz方向上 当前位置向目标位置靠拢的速度
        const vx = (xDiff + xOffset) * ratio
        const vy = (yDiff + yOffset) * ratio
        const vz = (zDiff + zOffset) * ratio

        buddy.velocity.set(vx, vy, vz)//设置僚机速度

        if (buddy.velocity.sqrMag() > 0.005) {//速度要足够大, 才触发转向, 防止抖动
            buddy.meshOrientation = Quat.rotateY(Math.atan2(zDiff, xDiff)) //让小精灵一直面向玩家
        }
    })

    return () => {
        ticker.cancel() //关掉tick循环
        buddy.destroy() //移除僚机实体
    }
}

world.onPlayerJoin(({ entity }) => {
    entity.setPet = buddyFollow(entity, 'mesh/皮卡丘.vb', -1) //给玩家增加宠物,名称需与地图内放置的模型的名称一致
})

world.onPlayerLeave(({ entity }) => {
    //玩家离开地图时, 切记一定要关掉tick循环以及销毁小精灵实体, 否则随着人数增加, 服务器积累到一定程度就会崩溃
    entity.setPet() //清除掉宠物
})

# 03 皮肤

(点击->高清B站视频) (opens new window)

开始之前,记得先把所有的部位做成对应的模型放入地图中!代码如下:

1.隐藏某个部位,以头部为例:

world.onPlayerJoin(({ entity }) => {
    /**
     * 以下代码是穿戴(addWearable) API结合 隐藏身体部位(skinInvisible) 功能的展示效果代码
     */
    // ----------------------------从这里开始--------------------------------------

    // API: addWearable - 指在玩家某身体部位附上穿戴配件物体
    // 戴上南瓜头
    entity.player.addWearable({
        bodyPart: Box3BodyPart.HEAD,  // 南瓜头要加到“头”这个部位上
        mesh: 'mesh/小南瓜4.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.3, 0),
    });

    //使用 “隐藏身体部件” 功能,把指定的身体部位隐藏掉!
    entity.player.skinInvisible.head = true;
})

其他的部件也是用同样的方式穿在身上

2.全身部件隐藏教程

// 需要提前把模型放到地图场景里!并且名称与代码中提到的部位名称保持一致,否则会出错
// 其中 offset、rotate 和 scale 分别代表:模型的偏移量、旋转角度和缩放值,需要根据具体的模型去自行调整数值,直接用的话可能导致模型角度不对的情况哦
const bodyData = [
 { bodyPart: Box3BodyPart.HEAD, name: '头', mesh: 'mesh/部件教程-头.vb', offset: [0, 0.4, -0.1], rotate: [0, 450, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.TORSO, name: '躯干', mesh: 'mesh/部件教程-躯干.vb', offset: [0, 0.1, 0], rotate: [0, 90, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.TORSO, name: '臀部', mesh: 'mesh/部件教程-臀部.vb', offset: [0, -0.25, 0], rotate: [0, 90, 0], scale: [0.5, 0.7, 0.4] },
 { bodyPart: Box3BodyPart.LEFT_UPPER_ARM, name: '左上臂', mesh: 'mesh/部件教程-左上臂.vb', offset: [0.1, 0.07,-0.02], rotate: [-128, 120, -85], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.RIGHT_UPPER_ARM, name: '右上臂', mesh: 'mesh/部件教程-右上臂.vb', offset: [-0.1, 0.07,-0.02], rotate: [-45, -120, -85], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.LEFT_LOWER_ARM, name: '左下臂', mesh: 'mesh/部件教程-左下臂.vb', offset: [0, 0.11, 0], rotate: [-128, 120, -85], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.RIGHT_LOWER_ARM, name: '右下臂', mesh: 'mesh/部件教程-右下臂.vb', offset: [0, 0.09, 0], rotate: [-45, -120, -85], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.LEFT_UPPER_LEG, name: '左上腿', mesh: 'mesh/部件教程-左上腿.vb', offset: [0, -0.05, 0], rotate: [0, 90, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.RIGHT_UPPER_LEG, name: '右上腿', mesh: 'mesh/部件教程-右上腿.vb', offset: [0, -0.1, 0], rotate: [0, 90, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.LEFT_LOWER_LEG, name: '左下腿', mesh: 'mesh/部件教程-左下腿.vb', offset: [0, 0.07, 0], rotate: [0, 90, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.RIGHT_LOWER_LEG, name: '右下腿', mesh: 'mesh/部件教程-右下腿.vb', offset: [0, 0.07, 0], rotate: [0, 90, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.LEFT_FOOT, name: '左脚', mesh: 'mesh/部件教程-左脚.vb', offset: [0, 0.06, 0.05], rotate: [0, 90, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.RIGHT_FOOT, name: '右脚', mesh: 'mesh/部件教程-右脚.vb', offset: [0, 0.06, 0.05], rotate: [0, 90, 0], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.LEFT_LOWER_ARM, name: '左手', mesh: 'mesh/部件教程-左手.vb', offset: [0.16, 0.16, 0.09], rotate: [-56, -64, 90], scale: [0.5, 0.5, 0.5] },
 { bodyPart: Box3BodyPart.RIGHT_LOWER_ARM, name: '右手', mesh: 'mesh/部件教程-右手.vb', offset: [-0.16, 0.05, 0.1], rotate: [-138, 60, 95], scale: [0.5, 0.5, 0.5] },
    ]
    world.onPlayerJoin(({ entity }) => {

        // 隐藏所有身体部件!
        for (const bodyPart in entity.player.skinInvisible) {
            entity.player.skinInvisible[bodyPart] = true;
        }

        // 根据配置穿戴身体各个部件
        for (const data of bodyData) {
            addWearable(entity, data)
        }
    });

    // 添加部件
    function addWearable(entity, data) {
        // 这一步是把角度转成弧度
        const orientation = new Box3Quaternion(0, 0, 0, 1)
                .rotateZ(data.rotate[2] * Math.PI / 180) 
                .rotateX(data.rotate[0] * Math.PI / 180) 
                .rotateY(data.rotate[1] * Math.PI / 180) 
        // 将上面声明的配置一一对应地传递给传递API
        entity.player.addWearable({
            bodyPart: data.bodyPart,
            mesh: data.mesh,
            orientation: orientation,
            scale: data.scale,
            offset: data.offset,
        })
    }

⚠️注意

其中 offsetrotatescale 分别代表:模型的偏移量、旋转角度和缩放值

需要根据具体的模型去自行调整数值,直接用的话可能导致模型角度不对的情况哦