知らない人の家に勝手に入ってはいかん!
今日も、太郎と次郎は街を徘徊していたんですが、そのとき、太郎がダンジョンを発見した様子をコード化しました。
今回は、Three.jsを使って発見したダンジョンを表示したいと思います。
構成
マインクラフトみたいにCubeで構成してみます。Cubeは壁、扉、天井、床の4種類を用意します。そのCubeを3層(天井、自分のいる階層、床)で並べます。
var mapData = {
ceiling : [
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■"
],
wall : [
"■■■■■■■■■■",
"■ ■ ■",
"■ ■□■■ ■",
"■■□■■ ■ ■",
"■■ ■■ ■ ■",
"■■ ■■■□■ ■",
"■ □ ■",
"■■■■■■■■■■"
],
floor : [
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■■■■■■■■■■",
"■ ■■■■■■■■",
"■■■■■■■■■■"
]
};
■ と □ の部分にCubeを配置します。それぞれの層に合わせて ■ の部分にそれぞれの層に合わせたCubeを配置します。
Cubeの作成準備
形状(Geometry)を決めて、その表面素材(Material)を用意し、これを使って物体(Mesh)を作成するという手順になります。
var blockSize = 10;
var geometry = new THREE.CubeGeometry(blockSize, blockSize, blockSize);
これで、1辺の長さが10の立方体の形状が出来上がります。今回はすべてCubeなので、形状は1つだけ作成しておきます。
今度は、それぞれのCubeが壁、扉、天井、床に見えるように素材を用意します。これについて、今回はpng画像を使います。
まず、png画像を読み込みます。そのあと、素材の作成になります。
var textureFile = {
wall : "img/wall.png",
door : "img/door.png",
ceiling : "img/ceiling.png",
floor : "img/floor.png",
};
var loader = new THREE.TextureLoader();
var texture = {
wall :loader.load(textureFile.wall),
door :loader.load(textureFile.door),
ceiling:loader.load(textureFile.ceiling),
floor :loader.load(textureFile.floor)
};
var material = {
wall : new THREE.MeshPhongMaterial({map:texture.wall, bumpMap:texture.wall, bumpScale: 0.05}),
door : new THREE.MeshPhongMaterial({map:texture.door, bumpMap:texture.door, bumpScale: 0.08}),
ceiling: new THREE.MeshPhongMaterial({map:texture.ceiling, bumpMap:texture.ceiling, bumpScale: 0.09}),
floor : new THREE.MeshPhongMaterial({map:texture.floor, bumpMap:texture.floor, bumpScale: 0.09})
};
今回の画像が岩肌っぽいので、バンプマッピングで凸凹感を出します。
さて、形状の定義と素材の準備ができました。構成で示したようにCubeを並べていきます。
シーン
作成したCubeをどこに並べるかというと、空間(Scene)に並べます。なので、空間を作っておく必要があります。
var scene = new THREE.Scene();
Cubeの配置
配置データを参照しつつ、空間に配置します。
壁の配置はこんな感じになります。
for (y = 0; y < mapData.wall.length; y++) {
for (x = 0; x < mapData.wall[y].length; x++) {
if (mapData.wall[y].charAt(x) == "■") {
var cube = new THREE.Mesh(geometry, material.wall);
cube.position.set(blockSize * x, 0, blockSize * y);
scene.add(cube);
}
}
}
あとは、こんな感じでしょうか...
// 扉の配置
if (mapData.wall[y].charAt(x) == "□") {
var cube = new THREE.Mesh(geometry, material.door);
cube.position.set(blockSize * x, 0, blockSize * y);
scene.add(cube);
}
// 天井の配置
if (mapData.ceiling[y].charAt(x) == "■") {
var cube = new THREE.Mesh(geometry, material.ceiling);
cube.position.set(blockSize * x, blockSize, blockSize * y);
scene.add(cube);
}
// 床の配置
if (mapData.floor[y].charAt(x) == "■") {
var cube = new THREE.Mesh(geometry, material.floor);
cube.position.set(blockSize * x, -blockSize, blockSize * y);
scene.add(cube);
}
太郎と次郎
配置データの上を北向きとして、下図の位置に二人がいるとします。
// 方角
var direction = {
north : 0,
west : 1,
south : 2,
east : 3
};
// プレイヤー情報
var Player = {
x : 6,
y : 1,
direction : direction.west,
};
場所とどっち向いてるかを記憶しておきます。
さて、Cubeを空間に配置して、二人の位置が決まったので、実際にどう見えるのか?が問題です。
これが、カメラです。カメラから見たCubeの配置が投影されるわけで、このカメラは二人の目です。
// カメラ用パラメータ
var cameraParams = {
fov : 95,
aspect : 1.8,
near : 1,
far : 50
};
var camera = new THREE.PerspectiveCamera(
cameraParams.fov, cameraParams.aspect, cameraParams.near, cameraParams.far
);
camera.position.set( Player.x * blockSize, 0, Player.y * blockSize );
camera.rotation.y = rotationAngle[Player.direction];
3Dっぽく見えるように透視投影のカメラを作ります。カメラには、画角(fov)とアスペクト比(aspect)、見える範囲(near~far)を指定します。場所と向きをさっき記憶した情報から取得して、カメラの位置と向きを設定します。
描画
表示サイズを決めて、rendererを作成し、描画します。
// 表示サイズ
var mazeScreen = {
width : 480,
height : 270
};
var canvas = document.getElementById("game");
var renderer = new THREE.WebGLRenderer();
renderer.setSize( mazeScreen.width, mazeScreen.height );
canvas.appendChild( renderer.domElement );
var render = function(){
renderer.render(scene, camera);
};
var animate = function(){
render();
requestAnimationFrame( animate );
};
animate();
いや、真っ暗だし...
光源
はい、人もそうですけど、光ないと何も見えませんw
空間に光を追加します。太陽の光は入ってこない設定なので、懐中電灯とかが頼りです。今回はスポットライト的な懐中電灯ではなく、ろうそく的な感じで光を追加します。
var lightParams = {
point : 0xffffff
};
var light = new THREE.PointLight( lightParams.point, 3, 10, 1 );
light.position.copy( camera.position );
light.position.z -= 1;
scene.add( light );
ろうそく(点光源)を空間に追加します。位置はカメラのちょっと前ぐらいで。光の色、明るさ、光の届く範囲、減衰率を指定します。光の届く範囲を0にすると、無限に光が届きますw
これで見えますよね。ぅん。
しかし、ブラウザのレンダリング能力ってすごかったんですね...
いよいよ、次回最終回は、このダンジョンを徘徊してみますw
こんな感じ(デモ)で...