在微信小游戏中使用three.js显示3D图形

年前,微信发布了一个重磅消息:微信小程序的小游戏功能,千呼万唤始出来!

笔者之前从未接触过微信小程序和WebGL的开发,但是却一直有留意相关技术的发展,大概听说原来微信小程序是不支持WebGL 3D技术的。这次借着微信大力推广小游戏,看了一下API文档,发现小游戏是可以使用的WebGL进行开发的。而最近正好又有点时间,就随便搞搞,试试小游戏的效果。因为小游戏“跳一跳”是用three.js所制作的,所以我就选择了three.js所。那么开始吧。

微信小游戏教程地址:教程小游戏

开发环境搭建

下载了最新的微信开发工具,并按照教程建立了示例项目。示例游戏是2D游戏,和我期望的有点距离,找遍网络没有一个三维的微信小游戏示例,看来只能自己试试了。

新建了一个小程序项目,并且按照教程添加了game.js和game.json,但是程序一直报错:

后来发现是调试基础库没有默认为“游戏”,按照截图操作之后就正常了:

 

引入three.js所

到github上下载three.js所最新版本,笔者当时下载的是R89,用最新的应该也没有问题。前文介绍过,笔者并没有开发小程序的经验,所以一上来就在game.js里直接引用三人。 JS:

 

 
  1. import './js/libs/weapp-adapter.js'

  2. import './js/libs/symbol.js'

  3. import './js/three/three.js'

  4.  
  5.  
  6. var scene = new THREE.Scene();


程序直接报错:

 

耐心看了小程序开发的说明,再看了three.js所的写法,重新修改引用方式,game.js:

 
  1. import './js/libs/weapp-adapter.js'
  2. import './js/libs/symbol.js'

  3.  
  4. var THREE = require('./js/three/three.js');

  5. var scene = new THREE.Scene();

  6.  
  7. var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

  8. var context = canvas.getContext('webgl');

  9. var renderer = new THREE.WebGLRenderer(context);

  10.  
  11. renderer.setSize(window.innerWidth, window.innerHeight);

  12.  
  13. canvas.appendChild(renderer.domElement);

  14. var geometry = new THREE.CubeGeometry(1, 1, 1);

  15. var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });

  16. var cube = new THREE.Mesh(geometry, material);

  17. scene.add(cube);

  18. camera.position.z = 5;

  19. function render() {

  20. requestAnimationFrame(render);

  21. cube.rotation.x += 0.1;

  22. cube.rotation.y += 0.1;

  23. renderer.render(scene, camera);

  24. }

  25. render();

  26.  

 

一个旋转的立方体就在开发环境下显示出来了!

到目前为止,一切算是比较顺利,于是马上使用预览功能上传到手机微信进行测试:

这个时候发生问题了:手机微信看不到我的立方体!

还好微信环境下有提供控制台,笔者通过记录日志,最终定位了错误:

原来是three.js所里面有一段代码:

是判断当前环境WebGL版本的,而微信环境下是opengl es3.2,使用这句正则表达式明显不能匹配到。我们稍微改一下:

 

	var version = parseFloat( /^(WebGL|OpenGL ES)\ ([0-9])/.exec( gl.getParameter( gl.VERSION ) )[ 1 ] );

 

再次发布预览试试看!


成功!

网友eastecho  写了一篇文章,更加详细的阐述了这方面的内容:https://indienova.com/indie-game-development/run-threejs-on-wechat-game-platform/

 

在微信小游戏中载入模型

接下来再建立我们的微信小游戏项目,如果您不是很熟悉要做哪些准备工作,可以参考前文:《 利用 three.js 开发微信小游戏的尝试》。不过我们这次使用的 weapp-adapter.js 会有所不同,是基于 @大城小胖 修改过的,可以在 这里找到 

接下来我们就尝试着用 three.js 自己的 JSONLoader 来载入。其实有了模型的 json 文件以后,载入方式就可以很多样了,比如可以 require 进去包了壳的 js 文件,或者直接使用 wx.request 加载远程文件等等,但是我们认为原生方式还是比较好的,至少有以下几个优点:

  1. 保持原始格式,便于后续修改模型;
  2. 最大限度保证代码兼容性,便于移植;
  3. 由于微信小程序/小游戏包体限制,将素材放到服务器上再载入进来比较合理。

于是,我们的载入代码如下(非完整代码,仅为代码示例):

// 模型载入处理
let modelLoader = new THREE.JSONLoader()
modelLoader.load(modelURL,
  function(geometry, materials){
    mesh = new THREE.Mesh(geometry, materials[0])
    scene.add(mesh)
    console.log('模型载入完成')
  },
  // onProgress 回调
  function (xhr) {
    console.log( (xhr.loaded / xhr.total * 100) + '% 已载入' )
  },
  // onError 回调
  function(err) {
    console.log('载入出错', err.target.status)
  }
);

可能会遇到如下的错误:

TypeError: n.addEventListener is not a function
    at Ia.load (three.min.js:638)
    at ke.load (three.min.js:723)
    at new Main (main.js? [sm]:36)
    at game.js? [sm]:6
    at require (WAGame.js:11)
    at gamePage.html:84

不过对历经过实战的我们来说,应该马上会了解到,这是因为微信给出的 XMLHttpRequest 缺少 addEventListener 造成的。我们可以自行在 weapp-adapter.js 中添加它。

在 weapp-adapter.js 中找到 XMLHttpRequest 的定义部分,为其增加一个新的 key 

{
  key: 'addEventListener',
  value: function addEventListener(type, listener) {
    if (typeof listener === 'function') {
      let event = { target: this }
      let that = this
      this['on' + type] = function () {
        listener.call(that, event)
      }
    }
  }
}

再跑一次,应该就可以正常载入了,开发环境和真机均无问题。

开发环境和真机截屏

至此,模型载入就实现了。

实现交互(临时方案)

本来是准备就此先罢手了,不过看到群中有人在尝试使用 OrbitControls 来实现简单交互,就顺便也试验了一下。OrbitControls 是 three.js 提供的一个非常便于使用的让摄像机围绕目标对象旋转的交互功能,最简化的时候一行代码就可以搞定了,于是就将其加入到项目文件中。

我们直接将其引入:

require('libs/OrbitControls')

但是运行发现错误:

ReferenceError: THREE is not defined
    at OrbitControls.js? [sm]:18
    at require (WAGame.js:11)
    at WAGame.js:11
    at main.js? [sm]:2
    at require (WAGame.js:11)
    at WAGame.js:11
    at game.js? [sm]:4
    at require (WAGame.js:11)
    at gamePage.html:85

临时处理方法只要在 OrbitControls.js 第一行粗暴的添加这行代码引入即可:

var THREE = require('three.min');

注: 因为我没有对 three.js 做任何修改,所以直接引入了 minified 版本,如果您没有使用该版本,去掉 .min 即可。

然后代码中加入这一行就可以用了:

controls = new THREE.OrbitControls(camera);

至此没有出现什么问题,但是当想要交互的时候,一有动作就会发现屏幕被清空了。直觉告诉我是摄像机的座标或者旋转角度计算错了,经过跟踪,果然如此,在触摸屏幕并移动的时候,以下代码会出现问题:

var element = scope.domElement === document ? scope.domElement.body : scope.domElement;

// rotating across whole screen goes 360 degrees around
rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );

// rotating up and down along whole screen attempts to go 360, but limited to 180
rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );

element.clientWidth 不存在,因此得到的值会是 NaN ,造成摄像机无法定位。我临时进行了如下修改:

rotateLeft( 2 * Math.PI * rotateDelta.x / window.innerWidth * scope.rotateSpeed );
rotateUp( 2 * Math.PI * rotateDelta.y / window.innerHeight * scope.rotateSpeed );

这只是临时的修改 ,后面有时间会尝试合理一些的解决方案。

不过呢,经过这样的修改以后,已经可以正常的通过手指对摄像机进行旋转,也可以用双指进行缩放了。

结束语

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值