🐈

【cluster】乗り物に乗ってるときにポーズをとらせる(簡易版)

に公開

(簡易版なので、ちょっとスクリプトを触った人じゃないとわからないかもしれません~)


こんな風に、乗り物に乗ったときにアニメーションさせる方法について。

clusterで乗り物に乗っているとき、フツーは両手の位置を固定するだけです(Right GripとLeft Grip)。
あとAvater Override Animationというのもあって、固定で1つのアニメーションを再生することも可能ですね。


常に同じアニメーションをさせるだけならAvater Override Animationに何か設定するだけで十分

もうちょっと柔軟にポーズをとらせるには?



2つ以上のアニメーションを切りかえる、アニメーションの再生速度を変える、とかしたい場合はスクリプトとHumanoid Animation Listが必要になります。


あとPlayer Scriptの助けも必要なので、Scriptable Itemと別につけておきましょう。

メインのスクリプトの中身

とりあえずScriptable Itemにして、こんなスクリプトをつけておきます。

$.onRide((isGetOn, player) => {
    if(isGetOn) {
        $.state.driver = player;
        $.setPlayerScript(player);
    } else {
        $.state.driver?.send("getOff", null);
        $.state.driver = null;
    }
});

$.onSteer((input, player) => {
    $.state.steerInput = input;
});

$.onUpdate((deltaTime) => {
    if(!$.state.driver) {
        return;
    }

    let steerForward = $.state.steerInput.y;
    if (steerForward > 0.7) {
        $.state.driver.send("steer", 1);
    } else if (steerForward < -0.7) {
        $.state.driver.send("steer", 2);
    } else {
        $.state.driver.send("steer", 0);
    }
});

やっていることは、

  • この乗り物に乗ったときPlayerScriptを設定する
  • そのPlayerを保存($.state.driverに)
  • 前後の入力を$.state.steerInputに保存(左右も取っているけど使ってない)
  • 誰かが乗っているなら、前後入力をPlayerScriptに毎フレーム送る
  • 降りたときには降りたメッセージをPlayerScriptに送る

PlayerScriptの中身

ここでは3つのアニメーションを登録しています。で、上記のスクリプトからの前後の受信にあわせてアニメーションを切りかえます。

const animAr = [_.humanoidAnimation("anim0"), _.humanoidAnimation("anim1"), _.humanoidAnimation("anim2")];
const animLengthAr = [];

let animVal = 0;
let animTick = 0.0;
let speed = 1.0;

for (let i = 0; i < animAr.length; i++) {
    animLengthAr.push(animAr[i].getLength());
}

_.onFrame((deltaTime) => {
    if(animVal < 0) {
        return;
    }
    
    animTick += deltaTime * speed;
    if (animTick > animLengthAr[animVal]) {
        animTick -= animLengthAr[animVal];
    }

    const pose = animAr[animVal].getSample(animTick);
    _.setHumanoidPoseOnFrame(pose);
});

_.onReceive((message, args, sender) => {
    if (message === "steer") {
        animVal = args;
    } else  if (message === "getOff") {
        animVal = -1;
    }
});

何かの入力に合わせてspeedを変更すれば再生速度を変えることも可能!(自転車のスピードにあわせてこぐアニメーションを速くするとかはまさにソレですね)
攻撃ボタンを押すたびにanimTickを0に戻し、攻撃モーションを頭からやり直す…とかもできますね!

なお、ワールドアイテムなら「ワールド内の全てのアイテムから3000回/秒」制限なので上記のスクリプトであまり問題ないです。
しかし、クラフトアイテムではアイテムあたり10回/秒制限だったりしますし、こういうアイテムをたくさんワールドに置いたりする場合もメッセージを送る頻度をうまく調整する必要があります(ChatGPTあたりがやり方を教えてくれるでしょう)。

Discussion