📓
【cluster】CICrafterとかで使いやすいスクリプト置き場
Unityを使わず、動く床アイテムを作成
の記事みたいな感じで、CICrafterで使えるスクリプトをただポンポン置いていきます。
(別にCICrafterでなくても、普通にクラフトアイテムとかで使えますが)
01くるくる回転
SubNodeを使う必要があります(SubNodeの入れ方は上記記事参照)。
hayasaとjikuは変更可能。
const froce = 30;
const hayasa = 72.0;
const jiku = new Vector3(0.0, 1.0, 0.0);
const node = $.subNode("SubNode000");
$.onStart(() => {
$.state.tick = 0;
});
$.onUpdate(deltaTime => {
$.state.tick += deltaTime*hayasa;
let kaku = jiku.clone().multiplyScalar($.state.tick);
let quat = new Quaternion().setFromEulerAngles(kaku);
node.setRotation(quat);
});
02 ベータ機能を使わない「プレイヤーを追いかける」
【ベータ機能】近くのプレイヤーについてくるペットをつくる(プレイヤー情報取得)
こちらの記事に近いものを、ベータ機能を使わずに実現するスクリプトです。
元々はforceは30でしたが、こちらでは1くらいがいいでしょうね。
const force = 1;
const maxDistance = 10;
const minDistance = 1;
const rad2deg = (rad) => rad * 180 / Math.PI;
$.onUpdate(deltaTime => {
let position = $.getPosition()
let players = $.getPlayersNear(position, maxDistance);
let targetPlayer = null;
let targetDistance = Infinity;
// それぞれのプレイヤーとの距離を計算し、最も近いものを探す
players.forEach(player => {
let playerPosition = player.getPosition();
let distance = playerPosition.clone().sub(position).length();
if (distance < targetDistance) {
targetDistance = distance;
targetPlayer = player;
}
});
let player = targetPlayer;
if (player == null) return;
// 追跡中のプレイヤーがログアウトなどでいなくなっていた場合
if (player.exists() == false) return;
let position = $.getPosition();
// プレイヤーがいる方向の角度を計算してY軸回転
let direction = player.getPosition().clone().sub(position);
let angle = rad2deg(Math.atan2(direction.x, direction.z)); // atan2は弧度法での角度を返すため、度数法に変換する
let rotation = new Quaternion().setFromEulerAngles(new Vector3(0, angle, 0));
$.setRotation(rotation);
// プレイヤーと一定より離れているときに、正面方向に押し出す力を加えて前進
if (direction.length() > minDistance) {
$.setPosition(position.add(new Vector3(0, 0, force*deltaTime).applyQuaternion(rotation)));
}
});
03 近づくと追いかけてきて、話しかけてくるNPC (Unity向け?)
これはテキストを使う関係でUnityで作ったほうがいいかもしれません。CICrafterでもできるとは思います。
https://cluster.mu/w/fce9f2be-3dca-4763-b6fe-1ff9089217ce
サンプルはムダにホラーっぽい。どうしてこうなった。
↑こんな感じで、ScriptableItemの子に、「text」という名のTextViewがついた「子」をつけておく必要があります。
ScriptableItemには、セットでMovableItemもつけてください(動くので)。セットでつけられるRigidBodyの「重力を使用」はOFF、IsKinematicはONです。重力ではなく、スクリプトで全部動きをコントロールする時の定番設定ですね。
そして、以下のスクリプトをScriptableItemにつけます。
talkListの内容は好きにいじってください。
ある程度近くなるとユーザーに近づいてきて、ランダムでテキストを表示し続けます。
//表示するテキストの候補(増やすことも可能。カンマで区切るのを忘れずに)
const talkList = [
"こんにちは",
"何か面白いことはありましたか?",
"私は今日も元気です!",
"ようこそ!",
"お疲れ様です!",
"ゆっくりしていってください!"
];
const forwardSpeed = 0.5; //移動スピード
const talkBorder = 3.0; //どの距離で話しかけるか
const nearLimit = 1.5; //どの距離まで近づくか
const findLimit = 5.0; //どの距離まで探すか
const talkTickPer = 3.0; //話しかける間隔
//テキストを表示する「子」
const textObj = $.subNode("text");
//テキストを表示したとき、どれだけずらすか
const textMoveVec = new Vector3(0, -0.3, 0);
$.onStart(() => {
$.state.findTick = 0;
$.state.talkTick = 0;
$.state.chosenTalkNo = -1;
$.state.textMoveTick = 0;
$.state.textFirstPos = textObj.getPosition();
$.state.textClearTick = 0;
});
$.onUpdate((deltaTime) => {
let currentPos = $.getPosition();
let targetPos = null;
let distance = Infinity;
$.state.findTick += deltaTime;
//0.3秒に1回だけ近くのプレイヤーを探す
if ($.state.findTick >= 0.3) {
$.state.findTick -= 0.3;
let players = $.getPlayersNear($.getPosition(), findLimit);
if (players.length > 0) {
let result = null;
players.forEach((player) => {
let pPos = player.getPosition();
let d = currentPos.clone().sub(pPos).length();
if (d < distance) {
distance = d;
result = player;
targetPos = pPos;
}
});
$.state.player = result;
} else {
//誰も見つからなかったなら、終わり
$.state.player = null;
return;
}
} else {
//まだ誰も見つかっていないなら、終わり
if ($.state.player == null) {
return;
}
//すでに見つけたプレイヤーがいるなら、その位置を取得
targetPos = $.state.player.getPosition();
distance = currentPos.clone().sub(targetPos).length();
}
//atan2で角度(ラジアン)を求める(くるくる回るだけなので、XとZの差から角度を求める)
let angle = Math.atan2(targetPos.x - currentPos.x, targetPos.z - currentPos.z);
//向きを変更(角度をラジアンから度に変換し、Yの値を求める)
$.setRotation(new Quaternion().setFromEulerAngles(new Vector3(0, angle * 180 / Math.PI, 0)));
//移動する長さはdeltaTimeで移動速度を調整。deltaTimeには前回のUpdateからの時間が入っている
//deltaTimeは、たいていは約0.03(30FPS)、約0.015(60FPS)などの値が入る
//これを考慮しないと、モバイル版など30FPSの環境とPCなど60FPSの環境で移動速度が変わってしまう
let moveLength = forwardSpeed * deltaTime;
//近づきすぎないようにする
if (distance - moveLength < nearLimit) {
moveLength = distance - nearLimit;
}
//そもそも移動の必要がないほど近づいているなら、何もしない
if (moveLength > 0) {
//角度から進行方向を求める
let forward = new Vector3(0, 0, 1).applyQuaternion($.getRotation());
//現在の位置に進行方向をかけて移動
let movedPos = currentPos.add(forward.multiplyScalar(moveLength));
$.setPosition(movedPos);
}
//話しかける距離に近づいたら、話しかける
if (distance <= talkBorder) {
talkProc(deltaTime);
}
//テキストが表示される度に、テキストを動かす
if ($.state.textMoveTick > 0) {
$.state.textMoveTick -= deltaTime * 5;
if ($.state.textMoveTick <= 0) {
$.state.textMoveTick = 0;
}
//徐々に初期位置に近づけていく
let easedVal = easeOutQuint($.state.textMoveTick);
//最初はtextMoveVecぶんだけ丸ごとズレているが、それを徐々に元の位置に戻す
//そのとき、イージング関数を使って、なめらかに値を戻すようにする
let textMoveCurrentVec = textMoveVec.clone().multiplyScalar(easedVal);
textObj.setPosition($.state.textFirstPos.clone().add(textMoveCurrentVec));
}
//一定時間が経過したら、テキストを消す
if ($.state.textClearTick > 0) {
$.state.textClearTick -= deltaTime;
if ($.state.textClearTick <= 0) {
textObj.setText("");
}
}
});
//なめらかに移動するためのイージング関数
const easeOutQuint = (x) => {
return 1 - Math.pow(1 - x, 5);
}
const talkProc = (deltaTime) => {
$.state.talkTick -= deltaTime;
//まだ話しかける時間ではないなら、終わり
if ($.state.talkTick > 0) {
return;
}
$.state.talkTick += talkTickPer;
let no;
//リストからランダムに選ぶ
//ただし、前回と同じものを選ばないようにする
do {
no = Math.floor(Math.random() * talkList.length);
} while (no == $.state.chosenTalkNo);
$.state.chosenTalkNo = no;
let chosenText = talkList[no];
//テキストを表示
textObj.setText($.state.player.userDisplayName + "さん、" + chosenText);
//テキストを動かす+消すための変数を設定
$.state.textMoveTick = 1;
$.state.textClearTick = 5;
};
Discussion