微信小游戏开发笔记:three.js 载入模型并交互

作者:eastecho
2018-01-27
17 10 16

之前的笔记

如果您是第一次阅读我们关于微信小游戏开发的笔记,那么您可能需要先通过之前的笔记快速了解一下:


继续 three.js 的探险

今天我们会继续基于 three.js 来做尝试。因为在各个群中,大家都在试图载入模型,不少人遇到了这样那样的问题,所以今天给大家介绍一种成功载入模型的方法。(方法很多,这里只介绍我们采用的方法)

我们今天使用的模型是通过 Blender 导出的。之所以使用 Blender,是因为它是免费且功能完善的 3D 软件,我们可以大大方方拿来使用。而正由于它是免费的,所以关于它的使用和开发技巧网络上随手可得,社区也非常活跃,很适合自学。

准备工作

  1. 最新版本的 Blender(文中采用了 v2.79 Mac);
  2. 最新版本的 three.js(文中采用了 90dev);
  3. 具有一定的 three.js 开发基础;
  4. 最新版本的“微信开发者工具”。

输出模型

要从 Blender 输出符合 three.js 标准的模型,我们需要为 Blender 添加一个专用的输出插件。如果您在学习和使用 three.js,那么这个插件您应该已经有了,它就在 three.js 的源码包中,位置如下:

three.js / utils / exporters / blender

github 上在这里

假定您已经安装好了 Blender,我们需要将插件 addons / io_three 目录整个儿复制到 Blender 的插件目录。

在 Windows 下一般在这里:

{盘符}:\Program Files\Blender Foundation\Blender\2.7X\scripts\addons

而 Mac 下一般在这里:

~/Library/Application Support/Blender/2.7X/scripts/addons

如果没有 scripts/addons 目录也不要方,可以自行创建。

然后该目录看起来大概是这样:

... ── Blender
       └── 2.7X
           ├── config
           └── scripts
               └── addons
                   └── io_three

准备完毕以后,就可以打开 Blender 了。我们需要进入 Blender 的用户设置面板,在“插件”中搜索“three”,找到后选定它,允许使用它作为输出选项:

允许使用输出选项

然后记得保存用户设置,这样以后就可以一直使用。好,现在我们就可以打开自己的模型文件并输出了。

选择输出我们所希望的格式

输出的选项还是蛮多,我们选用了如下的设置供参考,您也可以自己尝试调整。

我们采用的输出设置

保存输出的 json 文件,然后将其上传到自己的服务器,接下来就可以在微信小游戏中使用。

在微信小游戏中载入模型

接下来建立我们的微信小游戏项目,如果您不是很熟悉要做哪些准备工作,可以参考前文:《利用 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 来实现简单交互,就顺便也试验了一下。OrbitControlsthree.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 );

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

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

结束语

好了,今天的内容就到这里了。这并不是一篇教程,只是在目前信息和资料不完善情况下的一种尝试,也希望大家一起参与到开发和研究中来,互相交流。

欢迎加入我们的小组:

微信小游戏小组

indienova 小组 参与

也欢迎加入我们的微信讨论群,目前已经超过 100 人,无法提供二维码了,可以找 indienova 的工作人员或者其它开发者邀请加入,也可以到小组去找人邀请。

源代码

为了方便大家参考,特提供完整源代码。

Github

源代码下载 代码

其它相关链接

请先阅读:微信小游戏官方文档

目前,Cocos、Egret、Laya 已经完成了自身引擎及其工具对小游戏的适配和支持,访问对应的官方文档可以更快地接入小游戏的开发

近期点赞的会员

 分享这篇文章

eastecho 

从前的边城浪子,现在的路人乙 

您可能还会对这些文章感兴趣

参与此文章的讨论

  1. 哈哈哈 2018-01-31 微信会员

    真机加载不出来,请问是什么原因?JSON的问题吗?

  2. 大城小胖 2018-01-31

    使用
    import * as THREE from 'libs/three.min'
    window.THREE = THREE;
    require('libs/OrbitControls')
    或者
    window.THREE = require('libs/three.min.js')
    require('libs/OrbitControls')

    的方式来定义全局的THREE, 这样就不需要修改 OrbitControls 文件了.

    • michaeljavy 2018-05-13

      @Shxuai: @hippogaga: @陈文强15248590676: @大城小胖:求问那个载入模型一直报错:fail invalid url这个问题怎么解决?如何才能成功的用threejs在小游戏编辑器里载入模型?谢谢

  3. Shxuai 2018-02-20 微信会员

    博主试过加入阴影吗?在ide中没问题,但是开发环境中出现了shadow acne,推测可能是实机的webgl版本导致

  4. Shxuai 2018-02-21 微信会员

    为什么加载本地的模型总是报错呢?
    代码:const model = 'models/indienova-logo.json
    报错:request:fail invalid url "models/indienova-logo.json"

    • hippogaga 2018-05-03

      @Shxuai:同问,解决了吗。我加载本地文件报request:fail invalid url "xx.json"

    • 黄璞 2018-06-16 微信会员

      @hippogaga:同问,这个问题解决了吗?

  5. json解析错误 是什以原因

  6. zqcolor 2018-06-09

    有两个问题请教一下:谢谢!

    1. 真机加载不出来,请问是什么原因?

    gameThirdScriptError
    Uncaught TypeError: Cannot set property 'width' of undefined
    TypeError: Cannot set property 'width' of undefined
    at WebGLRenderer.setSize (https://servicewechat.qq.com/game.js:22016:25)
    at new d (https://servicewechat.qq.com/game.js:46924:955)
    at https://servicewechat.qq.com/game.js:46927:77
    at require (:3:8056)
    at https://servicewechat.qq.com/game.js:46928:10

    2. 为什么加载本地的模型总是报错呢?
    代码:const model = 'models/indienova-logo.json
    报错:request:fail invalid url "models/indienova-logo.json"

    最近由 zqcolor 修改于:2018-06-09 06:22:17
    • cb 2018-09-10 微信会员

      @zqcolor:我也碰到你的问题,请问您解决了吗

  7. 朗朗 2018-07-18 微信会员

    为什么加载本地的模型总是报错呢?
    代码:const model = 'models/indienova-logo.json
    报错:request:fail invalid url "models/indienova-logo.json"

  8. diedwalker 2019-02-01

    你好,我按照您的教程已经成功加载出了模型,也可以真机运行,但是
    three.js / utils / exporters / blender 这个文件没有了,麻烦您再发一份

  9. 则栋 2020-04-01

    先mark

您需要登录或者注册后才能发表评论

登录/注册