使用THREE.js制作一款3D游戏

栏目: JavaScript · 发布时间: 7年前

内容简介:这个学期选修了一门计算机图形学的课程,课程选用的教材是基于WebGL。在此之前我对计算机图形学是没有任何概念的,只知道如果想要设计一款游戏具有图形学的知识是很重要的。我从来就有一个念想就是制作一款游戏,因此我对这方面是很有兴趣的。 老师推荐了一本入门教材《WebGL编程指南》,这本教材讲的很详细很适合入门使用,我花了大约一周的时间将这本书看完,对webgl的编程有了个大致的了解,当然真的只是粗粗入门。学习过程中又了解到three.js这个基于webgl的第三方3d图形库,看到了很多酷炫的3d游戏都使用了这

本文是基于某位大神使用three.js设计游戏的学习心得与知识分享 The Making of “The Aviator”: Animating a Basic 3D Scene with Three.js

前言

这个学期选修了一门计算机图形学的课程,课程选用的教材是基于WebGL。在此之前我对计算机图形学是没有任何概念的,只知道如果想要设计一款游戏具有图形学的知识是很重要的。我从来就有一个念想就是制作一款游戏,因此我对这方面是很有兴趣的。 老师推荐了一本入门教材《WebGL编程指南》,这本教材讲的很详细很适合入门使用,我花了大约一周的时间将这本书看完,对webgl的编程有了个大致的了解,当然真的只是粗粗入门。学习过程中又了解到three.js这个基于webgl的第三方3d图形库,看到了很多酷炫的3d游戏都使用了这个库,我决定下一步就是学习这个库的使用。 我大概看了半本的《THREE.js开发指南》,这本书很系统的讲了这个第三方库,但是难免很枯燥,于是就找到了现在这个使用three.js设计游戏的这么一个小项目。

代码结构

主函数由各种构件场景的函数组成,十分简洁

function init(event){
    createScene();
    
    createLights();

    createPlane();
    
    createSea();
    
    createSky();
    
    document.addEventListener('mousemove', handleMouseMove, false);
    
    loop();//循环函数,用于最后每一个帧的重绘,实现动画效果
}
复制代码

使用dat.gui图形界面动态的调整数据

dat.gui是一个第三方的图形库,通过这个图形界面来调整数据真的很方便

//通过dat.gui来调整环境光
var controls = new function () {//声明一个控制对象
    this.ambientLightColor = "#dc8874";
}
//环境光的值可以是16进制的数值,如"#ffffff",每次通过gui调整了color值都会触发下面的匿名函数从而调整环境光的颜色,环境光加入到场景中后每次渲染场景时都会使用最新的环境光颜色值,从而实现了使用gui调整环境光颜色的功能
var gui = new dat.GUI;//创建gui对象
gui.addColor(controls,'ambientLightColor').onChange(function (e) {
	ambientLight.color = new THREE.Color(e);// 
});
复制代码

当然可以添加更多的数据来进行动态调整,比如照相机的位置,各种颜色数值,等等。这是一个超级使用的功能。

一、搭建一个场景

使用thre.js绘制3d图形最基本的就是需要一个场景,场景像一个容器,至少需要包括灯光,照相机和渲染器

function createScene(){

    HEIGHT = window.innerHeight;
    WIDTH = window.innerWidth;

    scene = new THREE.Scene();//创建场景
    scene.fog = new THREE.Fog(0xf7d9aa, 100,950);//使用雾化的效果
	var axes = new THREE.AxisHelper(200);//场景中添加一个三维坐标系,便于观察图形的位置
    scene.add(axes);
    aspectRatio = WIDTH / HEIGHT;//宽高比设置为窗口大小,避免图案的变形
    fieldOfView = 50;
    nearPlane = 0.1;
    farPlane = 10000;
    camera = new THREE.PerspectiveCamera(fieldOfView,aspectRatio,nearPlane,farPlane);//使用一个透视相机使物体具有3d的效果
    camera.position.x = 0;//相机的位置和视点将影响观察到的物体
    camera.position.z = 200;
    camera.position.y = 100;//待优化

    renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });//声明一个webgl的渲染器,这个渲染器就如同html中的canvas
    renderer.setSize(WIDTH,HEIGHT);
    renderer.shadowMap.enabled = true;
    container = document.getElementById('world');
    container.appendChild(renderer.domElement);//将这个渲染器加到html当中

    
复制代码

二、添加灯光

在three.js中添加灯光十分的简单,不同的灯光有不同的作用,比如环境光,点光源,聚光灯等等。这里用到了半球光,半球光其中设置的两个参数天空和地面的颜色可以使场景更加的真实。

function createLights(){
    hemisphereLight = new THREE.HemisphereLight(0xbbbbbb,0x000000, .9);
    ambientLight = new THREE.AmbientLight(controls.ambientLightColor);
    shadowLight = new THREE.DirectionalLight(0xffffff, .9);
    shadowLight.castShadow = true;
    shadowLight.shadow.camera.left = -400;
    shadowLight.shadow.camera.right = 400;
    shadowLight.shadow.camera.top = 400;
    shadowLight.shadow.camera.bottom = -400;
    shadowLight.shadow.camera.near = 1;
    shadowLight.shadow.camera.far = 1000;
    shadowLight.shadow.mapSize.width = 2048;
    shadowLight.shadow.mapSize.height = 2048;
//每次设置完灯光都需要把他添加到场景中
    scene.add(hemisphereLight);
    scene.add(shadowLight);
    scene.add(ambientLight);
}
复制代码

三、创造一片大海

这里的大海是通过一个倒置的圆柱体来实现的,通过调整照相机的位置,并且配合旋转的动画,在半球光的照射下就如同一片汪洋。

Sea = function(){
    var geom = new THREE.CylinderGeometry(600,600,800,40,10);
    geom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI/2));
    var mat = new THREE.MeshPhongMaterial({
        color:Colors.blue,
        transparent:true,
        opacity:.6,
        shading:THREE.FlatShading,
    });
    this.mesh = new THREE.Mesh(geom, mat);
    this.mesh.receiveShadow = true;

}

var sea;
function createSea(){
    sea = new Sea();
    sea.mesh.position.y = -600;
    scene.add(sea.mesh);
}
复制代码

四、简单又精致的天空

我们使用几块大小不一的方块随机的堆叠在一起,它们就像云朵一样,很抽象派是吧!如果再把他们的位置随机的摆放,配合转动的动画,是不是更像了呢!

//构造一个云朵对象
Cloud = function(){
    this.mesh = new THREE.Object3D();
    var geom = new THREE.BoxGeometry(20,20,20);

    var mat = new THREE.MeshPhongMaterial({
        color:Colors.white,
    });
    var nBlocs = 3+Math.floor(Math.random()*3);

    for(i=0;i<nBlocs;i++){
    //实现位置随机,大小随机
        var m = new THREE.Mesh(geom, mat);
        m.position.x = i*15;
        m.position.y =Math.random()*10;
        m.position.z = Math.random()*10;
        m.rotation.z = Math.random()*Math.PI*2;
        m.rotation.y = Math.random()*Math.PI*2;
        var s = .1 + Math.random()*.9;
        m.scale.set(s,s,s);

        m.castShadow = true;
        m.receiveShadow = true;

        this.mesh.add(m);
    }
}

Sky = function(){
    this.mesh = new THREE.Object3D();
    this.nClouds = 20;
    var stepAngle = Math.PI*2 / this.nClouds;
    for (var i=0;i<this.nClouds;i++){
        var c = new Cloud();
        var a = stepAngle*i;
        var h = 750 + Math.random()*200;
        c.mesh.position.y = Math.sin(a)*h;
        c.mesh.position.x = Math.cos(a)*h;
        c.mesh.rotation.z = - Math.PI/2+a;
        c.mesh.position.z = -50-Math.random()*400;
        var s = 1+Math.random()*2;
        c.mesh.scale.set(s,s,s);
        this.mesh.add(c.mesh);
    }
}
var sky;

function createSky(){
    sky = new Sky();
    sky.mesh.position.y = -600;
    scene.add(sky.mesh);
}
复制代码

五、设计一架超酷的飞机

使用五个矩形打造一款飞机!这似乎听起来有点困难,但是这真的很有意思,配合不同的颜色,在螺旋桨的转动下,这款飞机真的很逼真!

var AirPlane = function() {

    this.mesh = new THREE.Object3D();

    // 这里要做的是一个驾驶舱
    var geomCockpit = new THREE.BoxGeometry(80,50,50,1,1,1);
    var matCockpit = new THREE.MeshPhongMaterial({color:Colors.red, shading:THREE.FlatShading});
    geomCockpit.vertices[4].y-=10;
    geomCockpit.vertices[4].z+=20;
    geomCockpit.vertices[5].y-=10;
    geomCockpit.vertices[5].z-=20;
    geomCockpit.vertices[6].y+=20;
    geomCockpit.vertices[6].z+=20;
    geomCockpit.vertices[7].y+=20;
    geomCockpit.vertices[7].z-=20;

    var cockpit = new THREE.Mesh(geomCockpit, matCockpit);
    cockpit.castShadow = true;
    cockpit.receiveShadow = true;
    this.mesh.add(cockpit);

    // 还要有引擎盖
    var geomEngine = new THREE.BoxGeometry(20,50,50,1,1,1);
    var matEngine = new THREE.MeshPhongMaterial({color:Colors.white, shading:THREE.FlatShading});
    var engine = new THREE.Mesh(geomEngine, matEngine);
    engine.position.x = 40;
    engine.castShadow = true;
    engine.receiveShadow = true;
    this.mesh.add(engine);

    // 做个尾巴吧
    var geomTailPlane = new THREE.BoxGeometry(15,20,5,1,1,1);
    var matTailPlane = new THREE.MeshPhongMaterial({color:Colors.red, shading:THREE.FlatShading});
    var tailPlane = new THREE.Mesh(geomTailPlane, matTailPlane);
    tailPlane.position.set(-35,25,0);
    tailPlane.castShadow = true;
    tailPlane.receiveShadow = true;
    this.mesh.add(tailPlane);

    // 机翼当然少不了,用长长的矩形穿过机身,多么美妙!
    var geomSideWing = new THREE.BoxGeometry(40,8,150,1,1,1);
    var matSideWing = new THREE.MeshPhongMaterial({color:Colors.red, shading:THREE.FlatShading});
    var sideWing = new THREE.Mesh(geomSideWing, matSideWing);
    sideWing.castShadow = true;
    sideWing.receiveShadow = true;
    this.mesh.add(sideWing);

    // 飞机前端旋转的螺旋桨
    var geomPropeller = new THREE.BoxGeometry(20,10,10,1,1,1);
    var matPropeller = new THREE.MeshPhongMaterial({color:Colors.brown, shading:THREE.FlatShading});
    this.propeller = new THREE.Mesh(geomPropeller, matPropeller);
    this.propeller.castShadow = true;
    this.propeller.receiveShadow = true;

    // 螺旋桨
    var geomBlade = new THREE.BoxGeometry(1,100,20,1,1,1);
    var matBlade = new THREE.MeshPhongMaterial({color:Colors.brownDark, shading:THREE.FlatShading});

    var blade = new THREE.Mesh(geomBlade, matBlade);
    blade.position.set(8,0,0);
    blade.castShadow = true;
    blade.receiveShadow = true;
    this.propeller.add(blade);
    this.propeller.position.set(50,0,0);
    this.mesh.add(this.propeller);

};

var airplane;

function createPlane(){
    airplane = new AirPlane();
    airplane.mesh.scale.set(.25,.25,.25);
    airplane.mesh.position.y = 100;
    scene.add(airplane.mesh);
}
复制代码

好了,伙计!现在我们的场景中有了灯光,大海,天空还有飞机,但是,似乎还少了什么。对的,我们要操控这个飞机!

六、控制我们的飞机

飞机能跟随鼠标移动的轨迹,为了做到更完美,当飞机上升和下降时,应该要有旋转的感觉!

function handleMouseMove(event) {

    // 我们要把鼠标的坐标值转换成webgl系统中规格化的数值,从-1到1
    // 这种转换很简单的伙计!tx = (x-width/2)/(width/2)

    var tx = -1 + (event.clientX / WIDTH)*2;

    // y轴在窗口坐标系和webg坐标系的方向是相反的,因此我们把他逆一下就可以
    

    var ty = 1 - (event.clientY / HEIGHT)*2;
    mousePos = {x:tx, y:ty};

}


function updatePlane(){
    var targetY = 100+mousePos.y*75;//控制飞机在y轴25到175的位置
    var targetX = mousePos.x*195;//控制飞机在x轴-195到195的位置

    // 每一帧移动飞机移动的距离,使飞机最终到达鼠标的位置,这样制造出飞机缓缓飞向指定位置的效果,而不会显得很突兀。
    airplane.mesh.position.y += (targetY-airplane.mesh.position.y)*0.1;
    airplane.mesh.position.x += (targetX-airplane.mesh.position.x)*0.5;
    // 通过剩余距离的长度来计算旋转地幅度,这样飞机如果一次性移动的距离很多相应的旋转幅度就越大,与真实的情况也符合,使动画更加真实。
    airplane.mesh.rotation.z = (targetY-airplane.mesh.position.y)*0.0256;
    airplane.mesh.rotation.x = (airplane.mesh.position.y-targetY)*0.0256;

    airplane.propeller.rotation.x += 0.3;

}
复制代码

七、让画面动起来、

实现动画的本质就是每一帧改变相应的参数,不断的渲染,使人眼感觉画面在运动

function loop(){
    airplane.propeller.rotation.x += 0.3;
    sea.mesh.rotation.z += .005;
    sky.mesh.rotation.z += .01;
    updatePlane();
    airplane.pilot.updateHairs();


    // 渲染
    renderer.render(scene, camera);

    // 再次调用
    requestAnimationFrame(loop);
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Handbook of Data Structures and Applications

Handbook of Data Structures and Applications

Dinesh P. Mehta / Chapman and Hall/CRC / 2004-10-28 / USD 135.95

In the late sixties, Donald Knuth, winner of the 1974Turing Award, published his landmark book The Art of Computer Programming: Fundamental Algorithms. This book brought to- gether a body of kno......一起来看看 《Handbook of Data Structures and Applications》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具