设计师怎么玩转Html5 3D游戏,一款让开发都折服的H5

栏目: Html5 · 发布时间: 6年前

内容简介:设计师怎么玩转Html5 3D游戏,一款让开发都折服的H5

基于webgl的html5 3D实战,两位设计师两周内完成设计和编程

第一次接触webgl还是去年的天猫活动,记得当时网上好多人疯狂转载,因为是国内第一次出现,所以大家觉得挺新鲜的,后来我了解到原来使用的是threejs的第三方库。去官网看了现有的示例,当时深深地被其3D效果所震惊,原来web还能做到主机和客户端的3D效果。作为一名交互设计师,除了日常产品对接体验优化工作外,自己业余时间也在学习threejs。不得不吐槽,threejs文档真心难用,学习成本还是比较高,但是多看看官网示例,多看看源码还是有不少进步的,顺便让我的Javascript的基础知识更牢固了。废话不多说了,进入今天的主题吧。先放几张截图和体验地址

设计师怎么玩转Html5 3D游戏,一款让开发都折服的H5

设计师怎么玩转Html5 3D游戏,一款让开发都折服的H5

设计师怎么玩转Html5 3D游戏,一款让开发都折服的H5

首先说一说需求来源,同程旅游市场中心品牌组要搞一个616运营活动,主要是以游乐场为场景,突出616活动气氛,以H5的形式出现。产品找到我们UED,本来是让我们这边出交互和设计稿,我们UED leader峰哥说可以尝试不一样的,可以试试我们最近研究的Webgl,产品看了两个demo后,欣然同意我们的想法(Demo更厉害,留着以后放大招)。期限为两周,包括搭场景、开发和测试,说真的,第一次做压力不是一般大,时间比较紧,难度有点高,而且要做那种漫游的感觉。

做的时候我和峰哥两人分工的,我负责coding,峰哥负责建模搭场景。这时不得不佩服我们leader的综合能力,场景是3dmax做的,包括建模,贴图,烘焙,AO等。这东西我只是了解,这部分交给峰哥了。Webgl基本环境和场景搭建我就不介绍了,这个太简单了,主要包括相机、灯光、场景等,大家可以去Github上看我源码

我首先要解决的问题是路径问题,飞机要沿着一定的路径飞行,因为场景不能太大,如果单纯绕圈或者沿着某一方向飞行一会就结束了,没有趣味性,飞机要飞得忽上忽下,有种漫游的感觉(PM原话)。要沿着某一固定曲线路径,首先要生成曲线即SplineCurve,查看document文档,发现只能生成二维的,找到几个示例才发现CatmullRomCurve3才是生成三维曲线(写在了对象里,为了“再玩一次”重新初始化)。

game={

status: "playing",

t:0,

score:0,

k:0,

spline: new THREE.CatmullRomCurve3([

new THREE.Vector3(-1095, 1285, 71),

new THREE.Vector3(134, 920, 230),

new THREE.Vector3(1170, 762, 615),

new THREE.Vector3(2234, 628, 537),

new THREE.Vector3(2618, 737, -701),

new THREE.Vector3(1802, 488, -1260),

new THREE.Vector3(823, 462, -1236),

new THREE.Vector3(203, 307, -357),

new THREE.Vector3(207, 275, 745),

new THREE.Vector3(1031, 468, 1247),

new THREE.Vector3(1280, 550, 256),

new THREE.Vector3(1280, 448, -550)

])

};

生成曲线后飞机怎么才能沿着曲线飞行呢?对了,飞行的时候相机是跟随的,这时候要用到Object3D,类似于父类DIV容器一样,代码如下:

var body=new THREE.Object3D();

body.add(camera); //添加相机

body.add(plane); //添加飞机机身

body.add(scoller); //添加螺旋桨

调好两者的位置,这样飞机和相机都绑定在一块了,只要控制body的运动,飞机和相机就会做相同动作。沿着曲线运动用到了getPoint()和getTangent(),但是遇到了问题,曲线的法线方向是一直变化的,而飞机的朝向一直是固定的,移动时一直朝向前方,这就尴尬了,应该是朝向沿着曲线切线方向,这样才有飞行的感觉。去Stack Overflow搜了一下,其实很简单,移动时一直计算切线方向,利用反余弦获取角度,然后使飞机朝向和切线方向一致就可以了。代码如下:

var pt = game.spline.getPoint( game.t );

body.position.set( pt.x, pt.y, pt.z );

var tangent = game.spline.getTangent( game.t ).normalize();

axis.crossVectors( up, tangent ).normalize();

var radians = Math.acos( up.dot( tangent ) );

body.quaternion.setFromAxisAngle( axis, radians );

第一个问题花了一天左右解决后,其他问题就简单了,主要就是模型的导出和导入,以及一些细节的处理。模型基本上都是导出json文件,然后调用ObjectLoader或JsonLoader导入。导出Json有好几种方法,例如网上用Blender导出Json,这种方法自己试过跑通的但是不方便,得要一个一个导出,还有就是Blender界面超难用,果断放弃。后来自己和峰哥探索出了Maya,3dmax导出Json的方法,但是稳定性有待提高,等我学好了 python 再优化。我们现在使用最稳妥的就是3dmax导出obj文件,导入到threejs官网的editor里编辑后导出Json,但是只能导入静态模型,动画模型还是要用到3dmax或者Maya导出,但对于这个项目是足够的。方法如下:

模型导入后就是大的背景搭建了,比如天空盒和海水,天空盒就是在建个Box,六张贴图贴在六个面(前后上下左右),代码如下:

function createSkybox(){

var path = "images/sky_";

var format = '.jpg';

var urls = [

path + 'px' + format, path + 'nx' + format,

path + 'py' + format, path + 'ny' + format,

path + 'pz' + format, path + 'nz' + format

];

var materials = [];

for (var i = 0; i<urls.length; ++i) {

var loader = new THREE.TextureLoader();

loader.setCrossOrigin( this.crossOrigin );

var texture = loader.load( urls[i], function(){}, undefined, function(){} );

materials.push(new THREE.MeshBasicMaterial({

map: texture

})

);

}  //导入六张贴图(前后左右上下)

var skyBox = new THREE.Mesh( new THREE.CubeGeometry( 100000, 100000, 100000 ), new THREE.MeshFaceMaterial( materials ) );//建个正方形盒子,尽量超大

skyBox.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, -1 ) );

scene.add( skyBox );

}

海水就比较麻烦了,用的是threejs示例里的Mirror.js和WaterShader.js,可以参考下官网的例子,调下参数就OK了,海水代码如下:

function createWater(){

waterNormals=new THREE.TextureLoader().load( 'models/texture_scene/waternormals.jpg' );

waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;

water = new THREE.Water( renderer, camera, scene, {

textureWidth: 512,

textureHeight: 512,

waterNormals: waterNormals,

alpha:   1.0,

sunDirection: light.position.clone().normalize(),

sunColor: 0xffffff,

waterColor: 0x0090c5,

distortionScale: 50.0,

});

mirrorMesh = new THREE.Mesh(

new THREE.PlaneBufferGeometry( 100000, 100000),

water.material

);

mirrorMesh.add( water );

mirrorMesh.rotation.x = - Math.PI * 0.5;

scene.add( mirrorMesh );

}

第三步就是加入金币和红包了,金币和红包要分布在固定曲线路线的两侧,而且朝向得跟曲线方向一致,不然就吃不到了,所以得要再次用到上续的代码,核心代码如下:

for (var i=1; i

Pn[i]=game.spline.getPoint(i/nBlocs);

Tn[i]=game.spline.getTangent(i/nBlocs);

var m=new THREE.Mesh(geometry, mat);

var s=0.2;

m.scale.set(s,s,s);

m.position.set(Pn[i].x+randomRange(-12,12),Pn[i].y,Pn[i].z);//分布在曲线两侧

var tt= game.spline.getTangent(i/nBlocs ).normalize();

Tn.crossVectors( up, tt).normalize();

var radians11 = Math.acos( up.dot( tt ) );

m.quaternion.setFromAxisAngle( Tn, radians11 );

coinsets.add(m);

if(i%11==0){i=i+2;}

}

第四步就是导入飞机和控制飞机,飞机拆成了螺旋桨和机身是为了螺旋桨的动画,通过var delta = clock.getDelta(); rotatescoller.rotation.z+=delta*50;两行代码让螺旋桨旋转起来。最难的就是控制飞行了,可能你会说不就是利用陀螺仪控制左右移动嘛,刚开始就是这样的,半小时搞定了;但后来发现存在一个问题,飞行时左右移动是漂移的,就是太tm假了,没有物理飞行的效果。好吧,在leader的重压之下搬出了高中的物理知识,利用JS的if…else写出了物理运动效果,当时也想过调用第三方库,但是提醒的大家是要想做出好的Webgl作品,尽量要克制使用第三方库,不然渲染时肯定会卡,就用原生的JS写,这样你才能有所提高。代码如下,有点傻瓜但是实用。

function controlUpdate(tt){

var actualV=0.5;

var angleLR=0.02;

group.position.x+=flymove;

group.rotation.z=flyangle;

if( moveLeft&&group.position.x>-10){

if(flyangle<0.2){

flyangle+=angleLR;

}

flymove-=actualV*tt;

}else if(moveRight&&group.position.x<10){

if(flyangle>-0.2){

flyangle-=angleLR;

}

flymove+=actualV*tt;

}

else {

flymove=0;

if(flyangle<-0.04){

flyangle+=0.02;

}

else if(flyangle>0.04)

{

flyangle-=0.02;

}

else{

flyangle=0;

}

}

}

飞机弄完后就是检测碰撞了,离成功只有一步之遥了,能否吃到金币和红包得要利用threejs里的Raycaster,Raycaster就是射出一道射线,类似于雷达波一样,检测到物体则返回值,从而判断是否碰撞到物体。检测代码如下:

function detectcollision(){

var collisions;

var rays = [

new THREE.Vector3(0, 0, 1),

new THREE.Vector3(1, 0, 1),

new THREE.Vector3(1, 0, 0),

new THREE.Vector3(1, 0, -1),

new THREE.Vector3(0, 0, -1),

new THREE.Vector3(-1, 0, -1),

new THREE.Vector3(-1, 0, 0),

new THREE.Vector3(-1, 0, 1),

];//射线方向

for (var i = 0; i<rays.length; i++) {

var caster = new THREE.Raycaster(body.position,rays[i],0,6);

// We reset the raycaster to this direction

collisions = caster.intersectObjects(coinsets.children);

//判断是否碰撞到

if (collisions.length > 0){

game.score+=2;

document.getElementById("track").innerHTML ='score: '+game.score;

var collectcoin=collisions[0].object;

coinsets.remove(collectcoin);

var Particleset=new ParticlesHolder();

Particleset.spawnParticles(collectcoin.position,6,1);

scene.add(Particleset.mesh);

}

}

}

大家看到代码里有一个对象ParticlesHolder,这个就是碰撞后的粒子,就是碰撞的一个反馈,也是纯JS手写,高中物理没白学…..

最后一步就是动画了,场景要实时渲染的,包括螺旋桨,飞机轨迹,场景、海水都是要一直渲染的,加上function animate(){requestAnimationFrame( animate );render();}就可以了。

除了上续的threejs核心代码外,还有一些流程上其他的工作,比如预加载,再玩一次,分享等。说一下预加载,因为是我第一次写这个预加载,所以去国内网站上看了一些例子,只能说很坑,new image()预加载图片还行,Json文件根本没反应,后来用了XMLHttpRequest()就可以了。

总的来说,这次收获还是蛮大的。期间遇到各种各样的问题,坑肯定不止以上这些问题,但是我们没有放弃,坚持在两周内解决了。但是整个游戏流程还是有点问题,比如吃完金币和红包没有任何奖励,这点体验很不好,但快上线了已经来不及了,这次也算是一个教训,遗憾总是有的。这算是我们市场中心UED的第一次尝试,由于本人只是个业余开发,学代码也才一两年,未来还有很长的路要走,感谢峰哥和小伙伴的帮助和鼓励,希望以后会带给大家更多更好的作品,也希望大家多给些建议,大家互相学习。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

x86汇编语言

x86汇编语言

李忠、王晓波、余洁 / 电子工业出版社 / 2013-1 / 56.00元

《x86汇编语言:从实模式到保护模式》采用开源的NASM汇编语言编译器和VirtualBox虚拟机软件,以个人计算机广泛采用的Intel处理器为基础,详细讲解了Intel处理器的指令系统和工作模式,以大量的代码演示了16/32/64位软件的开发方法,介绍了处理器的16位实模式和32位保护模式,以及基本的指令系统。 《x86汇编语言:从实模式到保护模式》是一本有趣的书,它没有把篇幅花在计算一......一起来看看 《x86汇编语言》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码