セカンドライフでジャンプ台を作る

セカンドライフ制作
スポンサーリンク

はじめに

はじめまして。

セカンドライフ技術系 Advent Calendar 2015」の12月10日(木)担当の なたで です。
今回、初めての参加ですがよろしくお願いします。

今日は、セカンドライフでジャンプ台のオブジェクトを作ります。

baned001
上から乗るとぼよーんとするジャンプ台です。

解説の要点

  • ジャンプ台のバネの部分を、Metasequoia でモデリングしよう
  • ジャンプ台の上から押したときにだけ、ぼよーんとするスクリプトを書こう

ジャンプ台のバネの部分を、モデリングしよう

Metasequoia を作ってモデリングしていきます。

bane001bane002
ドーナッツ型の基本図形からバネを作成していきます。
細かいほうが綺麗に作れますが、単純化のために8角形でいきます。

bane003
bane004
8角形のうち、7つの角を選択して、上に 10 上げます。
複数の選択は、Shiftを押しながら選択でするとできます。

bane005
bane006
先ほど選択した 7つの角のうち、端っこの選択を解除して、上に 10 上げます。
解除は、Ctrlを押しながら選択すると解除できます。
端から 1 つずつ選択を解除しては 10 上げる作業を繰り返していきます。

bane007
これで、すこしラセンっぽくなりました。
この形をたくさんつなげれば、バネができそうです。

bane008
2回転にするために、上にコピーしましょう。

bane009
段差が変な部分の面を削除して、

bane010
上下のラセンのオブジェクトの断面を、面を繋げていきます。

bane011
これでバネが完成しました。

bane012bane013
あとは、木のような台を上下にくっつけて、ジャンプ台のモデルは完成です。
物理判定用のオブジェクトは適当に立方体で作成しておきます。
なお、この時点で、セカンドライフ上の軸と合うように回転させておきましょう。
右が赤のX座標。前が緑のY座標。上が青のZ座標です。
(→詳しくは、 軸についての記事

bane014
これをアップロードします。
アップロード直後は、
プレビューのように予め初期値で回転されているので、回転をもどして…

bane015
できました。


ジャンプ台のスクリプトを書こう

ジャンプ台のスクリプトを書いていきます。。

衝突時の判定は「collision_start」を使用するとよさそうです。
衝突したアバターを、指定した方向へ「llPushObject」で飛ばせばいいのです。
(※土地の設定を、プッシュ許可にしておくこと)

上記の関数を使って「衝突したアバターを、上へ飛ばす」プログラムを書いていくのですが、
単純に実装しようとすると、いくつか問題があることに気がつきます。

baned002
左右からジャンプ台にぶつかった場合でも上へ飛んでしまうのでは?

baned003
ジャンプ台が傾いていた場合に、上から踏んだ場合はどのようにすれば?

……などなど。

解決策

上記のあらゆる問題を解決するために、ジャンプ台で飛ばす方向に対して、
衝突したアバターの「めり込み具合」を調べて、正しく判断させようと思います。

今回は、ベクトルの計算が必要です。
ベクトルというのは、(x, y, z)など、たくさんの値をもつデータのようなものです。
なお、通常の1つしか値ない場合は、スカラーと呼びます。

今回は2種類のベクトルを使用します。

baned004
1つ目に、法線ベクトルがあります。
法線ベクトルは、物体の表面から垂直に伸びたベクトルです。

今回は、バネの向いている方向を、法線ベクトルとして考えたいと思います。
このベクトルは N と呼び、長さ(ノルム)を1とし、単位ベクトルとします。

なお、長さを1にすることで、純粋に方向のみを表すことができます。
よく使用するテクニックなので覚えておくといいかもしれません。
具体的には、「ベクトル」÷「ベクトルの長さ」の計算をすると、長さを1にできます。

baned005
次に、速度ベクトルVです。
衝突したオブジェクトがどの方向へどの速度で向かっているかとなります。
方向はオブジェクトが向かっている方向、長さは速度に相当します。

baned006
衝突したある点からの法線ベクトル N と、衝突時の速度ベクトル V があったときに、
このめり込み具合 d の長さを計算します。

実は、簡単に計算ができる方法あります。
baned007
こんな式です。

こんな計算で、dが求められるのと思うかもしれませんが、求められます。
ベクトル同士を「・」すると、内積となり、
細かいことはおいておいて、dが求まります。

baned008

Nが単位ベクトル(長さ=1)となっているところがポイントです。
すこし難しいですが、ベクトルVを、単位ベクトルであるNに射影したベクトルのノルムが内積となります。

さて、スクリプトは次のようになります。

integer i;

list		get_details			= [ PRIM_SIZE, PRIM_POSITION, PRIM_ROTATION ];
vector		object_size			= <0, 0, 0>;
vector		object_position		= <0, 0, 0>;
rotation	object_rotation		= <0, 0, 0, 1>;
vector		target_velocity		= <0, 0, 0>;
vector		object_normal		= <0, 0, 0>;
vector		object_normal_inv	= <0, 0, 0>;
vector		refrect_vector		= <0, 0, 0>;

// 設定値
float		power					= 3.0;	// 少しの力でもある程度飛ばす
float		rebound					= 2.0;	// 何倍の力で戻すか
float		threshold				= 0.5;	// この閾値より低いと吹き飛ばさない

getData() {
	list list_details = llGetPrimitiveParams(get_details);
	object_size		= llList2Vector(list_details, 0);
	object_position	= llList2Vector(list_details, 1);
	object_rotation	= llList2Rot(list_details, 2);
	object_normal		= <0, 0, 1> * object_rotation; // バネの上の向きベクトル
	object_normal_inv	= <0, 0, -1> * object_rotation; // バネの下の向きベクトル
}

onStartCollision(integer num_detected) {
	// 使用するオブジェクトのデータの取得
	getData();
	
	// めり込み度を計算(内積)
	float merikomi		= target_velocity * object_normal_inv;
	
	// あまり押されていなかったら、処理しない
	if(merikomi < threshold) {
		return;
	}
	
	// 跳ね返すベクトル
	refrect_vector	= (object_normal * (llFabs(merikomi) + power) ) * rebound;
	
	// プッシュ!
	for(i = 0; i < num_detected; i++) {
		llPushObject(llDetectedKey(i), refrect_vector, <0, 0, 0>, FALSE);
	}
	
	// オブジェクトが一瞬縮んで、押された感じにする
	vector object_position_new	= object_position + object_normal_inv * (object_size.z * 0.5);
	vector object_scale_new		= <object_size.x, object_size.y, object_size.z * 0.5>;
	llSetScale(object_scale_new);
	llSetPos(object_position_new);
	llSleep(0.1);
	llSetPos(object_position);
	llSetScale(object_size);
}

default {
	collision_start(integer num_detected ) {
		// 即速度を取得しないと、止まった時の速度を取得してしまう
		target_velocity = llDetectedVel(0);
		// 後はこの中で処理
		onStartCollision(num_detected);
	}
}

以上、「ジャンプ台を作る」の解説を終えます!
お疲れ様でした!

コメント

タイトルとURLをコピーしました