恭喜你成为UI中国推荐设计师 (详情)
//百度统计 20220402 uicn

您的意见是我们 UI 中国进步的动力!
点击立即反馈按钮,发表您的意见!
立即反馈
QQ群反馈
您也可以加入UI中国官方反馈群进行反馈!
群号:302892100
备注:反馈问题后@管理员能让我们及时了解您的意见

提交需求

赛事与广告咨询合作,请填写需求表单,我们会在第一时间与您联系!

0/20
0/200

设计大赛

  • 设计大赛
  • 发布广告
  • 发布招聘
  • 其它需求

提交完成
感谢您对UI中国的支持和信赖!
设计师怎么玩转Html5 3D游戏,一款让开发都折服的H5
0.0°
2017-06-15 原创文章 教程 举报 8559 7 25 0

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

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

Image title

Image title

Image title


 

首先说一说需求来源,同程旅游市场中心品牌组要搞一个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的第一次尝试,由于本人只是个业余开发,学代码也才一两年,未来还有很长的路要走,感谢峰哥和小伙伴的帮助和鼓励,希望以后会带给大家更多更好的作品,也希望大家多给些建议,大家互相学习。

更新:2017-06-15

收藏

7人已收藏

  • 0

    作品

  • 0

    粉丝

  • 0

    关注

  • 一款html5 3D飞行游戏-616邀请函
相关标签
网页设计html53d

    猜你喜欢

      2017-06-15 原创文章 教程 举报 8559 7 25 0

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

      0.0°

      你确定要举报设计师怎么玩转Html5 3D游戏,一款让开发都折服的H5

      如果查出恶意举报,十天内禁止提交任何举报申请。

      0/200

      上传证据: 超过10M的附件请使用网盘地址

      点击上传附件

      对谁可见:

      全部设计师
      • 全部设计师
      • 推荐设计师和认证设计师

      您确认要推荐?

      该作品发布时间:2017年06月15日

      评分

      完整度

      启发性

      勤奋性

      排版布局

      推荐心得

      建议20-200字以内

      0/200

      25
      7
      0

      账号或密码错误,请重新输入

      账号或密码错误,请重新输入

      登录

      手机号

      发送验证码 120s 验证码错误

      登录
      第三方账号登录