STAFF BLOG スタッフブログ

ボーン入り3Dキャラクターを地形の上で動かす

WebGLに入門した記録です(2)

こんにちは。
前回の記事を書いてから、自力で実装しようと頑張っていましたが、世の中には良いものがあったので早速そっちに切り替えていこうと思います。
meshwalk.js
このライブラリはr74時代のものなので、こちらを見ながら最新版r88に対応させておきます。

サンプルの通りに作ればデモとかサンタウォークスルーみたいなのが作れてしまうのですが、一瞬で記事が終わってしまいますし、せっかくなのでゲームらしいフィールドを作って走り回れるようにしてみたいと思います。

はじめに神は天と地とを創造された

完全なランダムをもって地形を生成すると、突拍子もない地形が生成されてしまうことが予想されますので、今回はパーリンノイズを利用して比較的なだらかな地形を作りたいと思います。

// フィールドの広さを決定(端から端まで)
var field_min = new THREE.Vector2(-32, -32);
var field_max = new THREE.Vector2(32, 32);
var field_map = new Array();
var field_rule = new Array();
var elms = new Array();

// 陸地ルールの定義(ルール合計が1000になるようにしてみた)
var rule = [900, 985, 995, 998, 1000];

// 初期生成
var i, j;
var i_len = field_max.x - field_min.x + 1;
var j_len = field_max.y - field_min.y + 1;
var x_offset = 0 - field_min.x;
var z_offset = 0 - field_min.y;

createHeightMap();
createLandRule();

function createHeightMap() {
	// シードを生成
	noise.seed(Math.random());

	var min_height = 0;
	for (var i = 0; i < i_len; i++) {
		field_map[i] = new Array();
		for (var j = 0; j < j_len; j++) {
			var value = noise.simplex2(i / 100, j / 100);
			value = Math.floor(value * 8);
			if(value < min_height){
				min_height = value;
			}
			field_map[i][j] = value;
		}
	}

	// ある程度底上げ
	for (var i = 0; i < i_len; i++) {
		for (var j = 0; j < j_len; j++) {
			field_map[i][j] = field_map[i][j] + Math.abs(min_height) -5;
		}
	}

}

function createLandRule() {
	for(i = 0; i < i_len; i++){
		field_rule[i] = [];
		for(j = 0; j < j_len; j++){
			var r = Math.floor(Math.random() * 1000) + 1;
			var s;
			if(field_map[i][j] >= 0) {
				if(r <= rule[0]){
					s = 0;
				} else if(r > rule[0] && r <= rule[1]){
					s = 1;
				} else if(r > rule[1] && r <= rule[2]){
					s = 2;
				} else if(r > rule[2] && r <= rule[3]){
					s = 3;
				} else if(r > rule[3] && r <= rule[4]){
					s = 4;
				}
				field_rule[i][j] = s;			
			}
		}
	}
}

1000枚陸地タイルがあったら、900枚は平地(何もない)、85枚は木、10枚はリンゴ……というイメージです。

地形メッシュを作る

参考にしたサンプル

最初のうち、座標ごとにキューブを生成し、高さを変えて並べることで地形を作ろうとしていました。

が、すぐに行き詰まります。重すぎる。

どうすれば高速動作するようになるのか調べてみたところ、とてもminecraftっぽいデモを見つけました。何これめっちゃ軽快に動くじゃないですか!

早速真似できるところは真似します。
上面と側面でテクスチャを貼り分けたいので、それぞれ別のメッシュを作ってシーン追加時に階段状のひとつの形状に見えるようにしました。
ジオメトリの結合による高速化についてはこのあたりを参考にさせていただきました。

神は「光あれ」と言われた

普通にライトを追加しただけなので省略。
各オブジェクトには影を出す+影を受ける設定を追加しておきます。

天の下の水は一つ所に集まり、かわいた地が現れよ

このサンプルをそのまま組み込みます
THREE.jsのサンプル集に含まれているwebgl_shaders_ocean.htmlをそのまま組み込んでいきます。
このサンプルにはスカイボックスも含まれているので、一緒に組み込みます。

地は生き物を種類にしたがっていだせ

できあがった地形に陸地ルールに従って各オブジェクトを配置していきます。
/* ================================================ 各配置オブジェクトをモデリング(複製用) */
// 木
tree = new THREE.Mesh(
	tree_data.geometry,
	new THREE.MultiMaterial( tree_data.materials )
);
tree.scale.set(1.5, 1.5, 1.5);
tree.castShadow = true;
tree.name = 'Tree';
scene.add(tree);
elms[1] = tree;

このようにオブジェクトの複製元を生成しておいて……

/* ================================================ 生成したマップに従って要素を配置 */
for(i = 0; i < i_len; i++){
	for(j = 0; j < j_len; j++){
		var s = field_map[i][j];
		var n = field_rule[i][j];
		if(s >= 0 && n > 0 && n < 5){
			//要素をオリジナルからクローンして配置
			var elm = elms[n].clone();
			elm.position.set(i - x_offset, elm.position.y + s + 1, j - z_offset);
			scene.add(elm);
		}
	}
}

複製して配置していき、最終的には複製元を非表示にしました。

プレイヤーキャラクターを動かそう

ずいずい

meshwalk.jsの本領であるmeshwalk.TPS.jsも組み込んで、ウカンちゃんを動かせるようになりました!

ずいずい歩きます。

びょーん
ジャンプすれば段差もらくらく飛び越えます。
ずいずい
水辺に入ってもずいずい歩けます。
※実際にゲームにするなら、泳ぐモーションに変えたり船に乗せてもいいと思います。
意外なジャンプ能力!
視点もぐりぐり動かせます

まとめと展望

WebGL入門編ということで、フリーモデリングソフトBlenderでモデル作成~JSON書き出し、フィールドの自動生成、meshwalk.jsを用いたTPS風インタフェースの実装までを駆け足で紹介してまいりました(何から何まで既存ライブラリに頼っていますが……)

これを発展させてそのままゲームを作ってもよし、建物や敷地のウォークスルー体験を作ってもよし、VRに対応させてもよし、夢はいろいろ広がります。

皆様も是非、WebGLで遊んでみてください!

次はVRの話をしてみたいです。
どうぞよろしくお願いいたします。

INDEXブログ一覧へ
CONTACTお問い合わせはこちら