座る位置を自動調整する椅子のClusterScript
この記事は「Cluster Script Advent Calendar 2024」の16日目です
昨日は獏星さんのClusterScriptでQuaternionのデータサイズをダイエットする小細工でした!
とても大事!!!!!我々の使える帯域は、細いっ…!!!
こんばんは!!かおもです!!!!
BOOTHで配布している「座る位置を自動調整する椅子」のスクリプトについて簡単に解説をしたいと思います。
BOOTHの方にも書いてありますが、改変可、ワールドに組み込み可、クラフトアイテムに仕込んでの配布可です!!
非ベータなので安心
PlayerScript.getHumanoidBonePositionがベータ抜けしたので、実現できました!!!
しかし、PlayerHandle.getHumanoidBonePositionの方はCCK2.30.0時点でベータなので、少々面倒なことをやっています。
ScriptableItem側は座り判定と調整
早速ですがスクリプト全文です。
アイデアの出発点は椅子の前側の角の位置が分かれば、座る位置を調整できるのでは? です。
あとは、足の長さを計算すれば実際に位置調整できます。
ScriptableItem側
//Seatと角の位置と、足の長さから座る位置を調整する
//RidabelItemのSeat
const seat = $.subNode("Seat");
//椅子の前側の角
const edge = $.subNode("Edge");
$.onStart(() => {
$.state.seatInitial = seat.getPosition();
$.state.seatGap = edge.getPosition().z - seat.getPosition().z;
});
$.onRide((isGetOn, player) => {
if(!isGetOn){
//椅子から降りたら戻す
resetSeatPosition();
return;
}
//足の長さの計測を依頼する
//PlayerHandle.getHumanoidBonePositionがベータ抜けしたら、依頼せずに自前で可能
$.setPlayerScript(player);
player.send("?legLength", "");
});
$.onReceive((messageType, arg, sender) => {
if(messageType === "legLength"){
//足の長さに基づいてSeatを移動させる
adjustSeatPosition(arg);
}
}, {player: true, item: true});
const resetSeatPosition = () => {
seat.setPosition($.state.seatInitial);
};
const adjustSeatPosition = legLength => {
const seatOffset = new Vector3(0, 0, $.state.seatGap - legLength);
seat.setPosition(seat.getPosition().add(seatOffset));
};
ざっくりとした流れです。
- 座る位置と椅子の角の位置を把握する。
- 座ったらPlayerScriptに足の長さの計測を依頼する。
- (PlayerScriptは足の長さを計測して、ScriptableItemに返す。)
- 足の長さが返ってきたら、椅子の角からの距離と比較して、位置調整をする。
項番3はPlayerScript側に記述されています。
1. 座る位置と椅子の角の位置を把握する。
//RidabelItemのSeat
const seat = $.subNode("Seat");
//椅子の前側の角
const edge = $.subNode("Edge");
$.onStart(() => {
$.state.seatInitial = seat.getPosition();
$.state.seatGap = edge.getPosition().z - seat.getPosition().z;
});
subNodeのEdgeは実際の椅子のメッシュに合わせて、GameObjectの位置を変更する必要があります。
このseatとedgeの間の距離(seatGap) と、後で出てくる足の長さの差を計算して、Seatの前後位置を調整する仕組みになっています。
2. 座ったらPlayerScriptに足の長さの計測を依頼する。
$.onRide((isGetOn, player) => {
if(!isGetOn){
//椅子から降りたら戻す
resetSeatPosition();
return;
}
//足の長さの計測を依頼する
//PlayerHandle.getHumanoidBonePositionがベータ抜けしたら、依頼せずに自前で可能
$.setPlayerScript(player);
player.send("?legLength", "");
});
上の方のif(!isGetOn)
あたりの処理は、一応降りたときに丁寧に戻しているだけです。
座ったらどうせ移動するので、省いても大丈夫な処理です。
面倒なのはその後の処理で、座ったプレイヤーにplayer.send
で足の長さを計測するように依頼をしています。
コメントにもありますが、PlayerHandle.getHumanoidBonePosition
がベータ抜けしたら$.setPlayerScript
もplayer.send
も不要です。
余談ですが、筆者は応答すべき何かをsendするときに、messageType
の頭に?
をつけるのが好きです。
?legLength
でsendしたらlegLength
でonReceiveできるという寸法です。
4. 足の長さが返ってきたら、椅子の角からの距離と比較して、位置調整をする。
$.onReceive((messageType, arg, sender) => {
if(messageType === "legLength"){
//足の長さに基づいてSeatを移動させる
adjustSeatPosition(arg);
}
}, {player: true, item: true});
legLength
で足の長さが返ってきたら位置調整(adjustSeatPosition
)をします。
PlayerHandle.getHumanoidBonePosition
がベータ抜けしたら、先のsend
とこのonReceive
を削除して、onRide
内で直接adjustSeatPosition
を動かせばOKです。
細かいところですが、PlayerScriptとのやり取りがあるので{player: true, item: true}
の指定を忘れないで下さい。
adjustSeatPosition
の中身です。
const adjustSeatPosition = legLength => {
const seatOffset = new Vector3(0, 0, $.state.seatGap - legLength);
seat.setPosition(seat.getPosition().add(seatOffset));
};
項番1で計算したseatGap
と、足の長さlegLength
の差の分だけSubNodeのseatを移動させる、という処理になっています。
PlayerScript側は足の長さの測定
早速ですがスクリプト全文です。
ScriptableItem側
_.onReceive((messageType, arg, sender) => {
if(messageType === "?legLength"){
//足の長さを計測して送り返す
const legLength = getLegLength();
_.sendTo(sender, "legLength", legLength);
}
});
const getLegLength = () => {
const upperLeg = _.getHumanoidBonePosition(HumanoidBone.RightUpperLeg);
const lowerLeg = _.getHumanoidBonePosition(HumanoidBone.RightLowerLeg);
const length = upperLeg.sub(lowerLeg).length();
return length;
};
こっちは足の長さを計測しているだけです。
onReceiveで依頼を受けて、sendToで足の長さを送り返しています。
UpperLegは股関節あたりの位置で、LowerLegは膝の位置になります。
なので、その差が足の長さ(座る時の前後位置に影響する長さ)ということになります。
課題や改変ポイント
VRoidStudio製とclusterのアバターメイカー製で、ボーンに対してのメッシュの付き方がかなり異なるので、この辺を何とかできたら嬉しいですね。
あとは両足の間隔から足の太さ?を予測してSeat高さを調節したりとかも面白いかもしれません。
おわり
よきClusterScriptライフを!!!
明日の記事はヒカゲさんの「スクリプトつながりで、CCK本体(C#)について。回線速度が遅く、何度もアップデートに失敗する人の解決を目指します」です!!
テスト用は1つだからいいけど、本番のは地味に詰まるんですよねw
Discussion