THREE.JS を利用してFPSゲームを作ってみよう1

Author: Heo

おはようございます。
最近Three.jsの勉強をしています。
それでThree.jsを使ってFPSゲームを作ってみようかと思いまして基本的なSourceを作ってみました。

現在FPS_base.html と FPS_Control.htmlがあります。
FPS_baseは名前とおりThree.jsのrendering構成Baseで
FPS_Controlはmouseやkeyboardに反応できるeventを追加してcameraを一人称視点で変えてみました。

FPS_base

FPS_Control

Three_example.zip

sourceはこんな感じです。FPS_BASEの構成の基本にして書きました。

[code lang="coffee"]

(function()
{
	var sw,sh,
		scene, 
		camera,fov,aspect,near,far, 
		renderer, 
		light,
		stats;
		
	var chScene, chCamera;			
	function init()
	{
		/* WINDOW SIZE */ 
		sw = WindowSize.width();
		sh = WindowSize.height();
		
		/* SCENE SETTING*/ 
		scene 	= new THREE.Scene();
		
		/* RENDERER SETTING */ 
		renderer = new THREE.WebGLRenderer({antialias : true});
		renderer.setSize(sw,sh);
		document.body.appendChild(renderer.domElement);
		
		/* CAMERA SETTING */ 
		fov 	= 45;
		aspect 	= sw/sh;
		near	= 0.1;
		far		= 10000;
		camera 	= new THREE.PerspectiveCamera(fov,aspect,near,far);
		camera.position.set(0,500,1000);
		scene.add(camera);
		
		/* LIGHT SETTING */ 
		light = new THREE.DirectionalLight( 0xffffff, 1.5 );
		light.position.set( 200, 450, 500 );
		light.shadowMapWidth = 1024;
		light.shadowMapHeight = 1024;
		light.shadowMapDarkness = 0.95;
		scene.add(light);
		
		/* STATS */
		stats = new Stats();
		stats.domElement.style.position = 'absolute';
		stats.domElement.style.top = '0px';
		stats.domElement.style.zIndex = 100;
		document.body.appendChild( stats.domElement );
		
		
		/* CROSSHAIR SECEN */
		renderer.autoClear = false;
		chScene 	= new THREE.Scene();
		chCamera 	= new THREE.PerspectiveCamera(fov,aspect,near,far);
		chCamera.position.set(0,0,0);
		chScene.add(chCamera);
		
		
		/* SETUP & RENDERING LOOP */
		setup();
		addEvent();
		requestAnimationFrame(rendering);
	}
	
	// ------------------------------------------------------------------
	// RENDERING
	// ------------------------------------------------------------------
	
	function rendering()
	{
		delta = clock.getDelta();
		
		animation();
		
		renderer.setDepthTest(true);
		renderer.clear();
		renderer.render(scene, camera);
		renderer.setDepthTest(false);
		renderer.render(chScene, chCamera);
		
		
		stats.update();
		requestAnimationFrame(rendering);
	}
	
	var lookTarget  = new THREE.Vector3(0,0,0);
	function animation()
	{
		camera.position = chr.camera.position;
		camera.rotation = chr.camera.rotation;
		//camera.lookAt(lookTarget);
		controler.update(delta);
		chr.update();
	}
	
	// ------------------------------------------------------------------
	// SETUP & ADD EVENT
	// ------------------------------------------------------------------
	var clock,delta,
		ground,chr,tower,
		controler,
		crossHair;
	function setup()
	{
		clock = new THREE.Clock();
		
		// object
		ground 		= new Ground("asset/img/grasslight-big.jpg")
		chr 		= new Cube(new THREE.Vector3(0,50,200),100,100,100,0xff0000);
		tower		= new Cube(new THREE.Vector3(0,500,0),100,1000,100,0x0000ff);
		chr.cameraOn();
		
		// controler
		controler						= new FPC_Controler(chr.camera, renderer.domElement);
		controler.movementSpeed 		= 400;
		controler.lookSpeed 			= .2;
		controler.lookVertical			= true;
		controler.constrainVertical 	= true;
		controler.verticalMin			= (30 * Math.PI) / 180;	
		controler.verticalMax 			= (100 * Math.PI) / 180;
		
		// CrossHair
		crossHair	= new CrossHair();
				
		scene.add(ground.root);
		scene.add(chr.root);
		scene.add(tower.root);
	}
	
	function addEvent()
	{
		document.addEventListener("keydown", onKeyDown, false);
		renderer.domElement.addEventListener("mousedown", onMouseDown, false);	
	}
	
	// ------------------------------------------------------------------
	// EVENT HANDLER
	// ------------------------------------------------------------------
	
	function onKeyDown(e){
		switch(e.keyCode){
			case 32: /*space*/ break;
			case 49: /*1*/ crossHair.change(0); break;
			case 50: /*2*/ crossHair.change(1); break;
			case 51: /*3*/ break;
			case 52: /*4*/ break;
			case 53: /*5*/ break;
		};
	}
	
	function onMouseDown(e){
		switch ( e.button ) {
			case 0: console.log("attack"); break;
		}
	}
	
	// ------------------------------------------------------------------
	// OBJECT CLASS
	// ------------------------------------------------------------------
	
	//GROUND CLASS
	function Ground(imgURL)
	{
		this.root = new THREE.Object3D();
		this.gt = THREE.ImageUtils.loadTexture( imgURL );
		this.gg = new THREE.PlaneGeometry( 3000, 3000 );
		this.gm = new THREE.MeshPhongMaterial( { color: 0xffffff, map: this.gt, perPixel: true } );
		
		this.mesh						= new THREE.Mesh( this.gg, this.gm );
		this.mesh.material.map.wrapS 	= this.mesh.material.map.wrapT = THREE.RepeatWrapping;
		this.mesh.rotationAutoUpdate 	= true;
		this.mesh.castShadow 			= false;
		this.mesh.material.map.repeat.set( 100, 100 );
		
		this.root.add(this.mesh);
	}
	Ground.prototype.constructor 	= Ground;
	
	//CUBE CLASS			
	function Cube(position,width,height,depth,color,imgURL)
	{
		this.geo 	= new THREE.CubeGeometry(width,height,depth);
		this.mat 	= new THREE.MeshLambertMaterial({color:color});//new THREE.MeshBasicMaterial();//
		this.mesh 	= new THREE.Mesh(this.geo, this.mat);
		this.mesh.position.set(0,0,0);
		
		this.root	= new THREE.Object3D();
		this.root.add(this.mesh);
		this.root.position.set(position.x, position.y, position.z);
	}
	Cube.prototype.update = function(){
		if(this.camera){
			if(this.camera.position.y < 50 || this.camera.position.y > 50) this.camera.position.y = 50;
			
			this.root.position = this.camera.position;
			this.root.rotation = this.camera.rotation;
		}
	}
	Cube.prototype.cameraOn = function(){
		if(!this.camera){
			this.camera = new THREE.PerspectiveCamera(45,sw/sh,.1,10000);
			this.camera.position = this.root.position;
			this.root.add(this.camera);
			console.log(this.camera)
		}	
	}
	Cube.prototype.cameraOff = function(){
		if(this.camera){
			this.root.remove(this.camera);
			this.camera = null;
		}
	}
	Cube.prototype.constructor 	= Cube;
	
	//CROSSHAIR CLASS
	function CrossHair()
	{
		this.CrossHairID 		= 0;
		this.CrossHairMaps 		= ["asset/img/crosshair1.png","asset/img/crosshair2.png"];
		this.CrossHairMap 		= THREE.ImageUtils.loadTexture( this.CrossHairMaps[this.CrossHairID]);
		
		this.CrossHair 						= new THREE.Sprite({ useScreenCoordinates: false});
		this.CrossHair.alignment 			= THREE.SpriteAlignment.center;
		this.CrossHair.useScreenCoordinates = false;
		this.CrossHair.position.z = -1000;
		
		// console.log(chScene);
		this.CrossHair.map = this.CrossHairMap;
		chScene.add(this.CrossHair);
	}
	
	CrossHair.prototype.change = function(id){
		if(this.CrossHairID == id || this.CrossHairID > this.CrossHairMaps.length - 1) return;
		this.CrossHairID 	= id;
		this.CrossHairMap 	= THREE.ImageUtils.loadTexture( this.CrossHairMaps[this.CrossHairID]);
		this.CrossHair.map = this.CrossHairMap;
		
	}
	CrossHair.prototype.constructor = CrossHair;
	
	
	
	window.addEventListener("load",init)
}).call(this);

[/code]

ここで問題点がいくつかありましたけどその中で
下のsourceを見ると FPC_Controler(…)のClassは元々Three.jsのFirstPersonControlsというclassでした。
FirstPersonControlsClassはkeyboardやmouseのeventが定義されて上書きができなかったところで別のClassにして修正をしました。

[code lang="coffee"]

controler = new FPC_Controler(chr.camera, renderer.domElement);

[/code]

次の公開は来週になる予定です。
次は鳥が飛んでいて鳥を落とすまでと思います。

よろしくお願いします。