0 前言

本文是“初见物理引擎库Cannon.js”系列的第二篇文章,在本文中主要讲解dat.gui的使用。

1 dat.gui简介

熟悉Three.js的读者肯定对dat.gui不陌生,通过该工具能够创建一个小型的菜单,如下图,在菜单中能够实时的修改一些变量或执行一些操作:

在Three.js官方案例中,也大量的使用了此类工具,如webgl_clipping_stencil,如下图:

只不过官方使用的是lil-gui,而不是dat.gui,两者实现的功能和使用方式类似,本文仅讲解dat.gui

2 在线地址

[ Live Demo ]:Cannon.js - datgui (syzdev.cn)

本文沿用“初见物理引擎库Cannon.js:基本使用”中的示例,并在此基础上实现如下功能:

  1. 重置小球下落;
  2. 修改摩擦力;
  3. 修改弹性系数;
  4. 修改小球颜色。

3 dat.gui的使用方法

3.1引入dat.gui.min.js

3.1.1 CDN引入

<script src="https://cdn.jsdelivr.net/npm/dat.gui@0.7.9/build/dat.gui.min.js"></script>

3.1.2 NPM引入

npm install dat.gui

3.2 初始化

首先要初始化一个gui对象,该变量表示菜单对象,对菜单的操作都是基于该对象完成的。

let gui = new dat.GUI()

接下来要初始化菜单的数据对象controls,即菜单中要修改哪些变量或执行哪些操作,除此之外还要为这些变量提供默认值或给这些操作提供按钮点击事件。

不难发现,要实现上述的4个功能,需要修改3个变量:

  1. 关联材质cannonDefaultCantactMaterial:用于修改摩擦力和弹性系数;
  2. 小球cannonSphereBody:用于重置小球下落;
  3. 小球材质threeSphereMaterial:用于修改小球的颜色。

注:上述3个变量,在初始化菜单的数据对象controls和创建实体时都要使用,因此要保证三者在同一个作用域下,最简单的办法是将上述三个变量定义为全局变量。

而重置小球下落是一个按钮,点击后将触发一个事件resetBall,因此在数据对象中controls,该变量为一个函数,函数中要重置小球的位置、重置小球的速度,修改摩擦力和弹性系数。

综上所述,完整的数据对象controls如下:

let controls = {resetBall: () => {cannonSphereBody.position.set(0, 10, 0) // 重置小球位置cannonSphereBody.velocity.set(0, 0, 0) // 重置小球速度cannonDefaultCantactMaterial.friction = friction // 修改摩擦力cannonDefaultCantactMaterial.restitution = restitution // 修改弹性系数},friction: 0.5, // 摩擦力,默认值为0.5restitution: 0.7, // 弹性系数,默认值为0.7color: threeSphereMaterial.color.getStyle() // 小球的颜色,默认值为初始化时的材质颜色
}

3.3 添加菜单项

完成了1.3节中的初始化工作,确定了要修改的变量和要执行的操作,接下来的工作就是将其添加到菜单对象gui上,上述4个功能在菜单项上可以分为3类:

  1. 按钮类型:重置小球下落;
  2. 数字输入类型:修改摩擦力和弹性系数;
  3. 颜色选取类型:修改小球的颜色。

之所以要对菜单项进行分类,是因为在添加菜单项时,对不同类型的菜单项要使用不同的添加方法或参数。

3.3.1 添加按钮类型菜单项

当菜单项类型为按钮时,使用的是add方法,在方法中提供数据对象controls和要执行的操作方法resetBall。以重置小球下落的按钮为例,添加方法如下:

gui.add(controls, 'resetBall')

3.3.2 添加数字输入类型菜单项

当菜单项类型为数字输入类型时,使用的也是add方法,但参数有所不同,以修改摩擦力为例,除了提供数据对象controls和操作变量friction之外,还可以提供可选的参数最小值、最大值和步长,最后在回调函数onChange中执行对应的修改即可,代码如下(参数中省略了步长):

// 摩擦力
gui.add(controls, 'friction', 0, 2).onChange((e) => {friction = e
})
// 同理:弹性系数
gui.add(controls, 'restitution', 0, 2).onChange((e) => {restitution = e
})

按钮类型的菜单不需要提供回调函数,这是因为按钮类型本身就触发了一个方法,在方法中执行了所需的操作。

3.3.3 添加颜色选取类型菜单项

当菜单项类型为颜色选取类型时,使用的是addColor方法,在方法中提供数据对象controls和要修改的颜色值color,最后在回调函数onChange中执行对应的修改即可,代码如下:

gui.addColor(controls, 'color').onChange((e) => {threeSphereMaterial.color.setStyle(e)
})

3.4 在菜单项中使用中文

完成上述操作后,如下图所示:

不难发现,菜单项中的名称默认使用的是数据对象controls中的变量名,在实践中往往需要将菜单项的文字修改为中文,虽然JavaScript已经支持了ASCII扩展字符和Unicode字符的标识符,但依然不建议这么做,在dat.gui中,可以使用name方法,给菜单项的文字取个别名,代码如下:

gui.add(controls, 'resetBall').name('重置小球下落')
// 类似的
gui.add(controls, 'friction', 0, 2).name('摩擦力').onChange((e) => {friction = e
})
gui.add(controls, 'restitution', 0, 2).name('弹性系数').onChange((e) => {restitution = e
})
gui.addColor(controls, 'color').name('小球颜色').onChange((e) => {threeSphereMaterial.color.setStyle(e)

这样,菜单项中的文字就修改为了中文,如下图所示:

4 实现代码

/*** MeshBodyToUpdate为一个对象数组* 数组中的每一个对象为Three中的Mesh和Cannon中的Body* 添加的形式如下* MeshBodyToUpdate.push({*   mesh: mesh,*   body: body,* })* 在render函数中遍历该数组,将Three中的Mesh的位置和旋转更新为Cannon中的Body的位置和旋转*/
const MeshBodyToUpdate = []/*** 声明默认材质* 用于初始化Cannon时创建关联材质*/
const cannonDefaultMaterial = new CANNON.Material()/*** 初始化Three的参数,为了将Three和Cannon分离* 用three对象来保存Three中的场景scene、相机camera和渲染器renderer*/
let three = {scene: null,camera: null,renderer: null,
}/*** 初始化Cannon参数*/
let cannon = {world: null,
}/*** dat.gui控制的变量*/
let cannonDefaultCantactMaterial = null // 关联材质
let cannonSphereBody = null // 小球
let threeSphereMaterial = null // 小球材质/*** @description: 初始化dat.gui*/
const initDatGui = () => {let gui = new dat.GUI()let controls = {resetBall: () => {cannonSphereBody.position.set(0, 10, 0) // 重置小球位置cannonSphereBody.velocity.set(0, 0, 0) // 重置小球速度cannonDefaultCantactMaterial.friction = friction // 修改摩擦力cannonDefaultCantactMaterial.restitution = restitution // 修改弹性系数},friction: 0.5,restitution: 0.7,color: threeSphereMaterial.color.getStyle()}gui.add(controls, 'resetBall').name('重置小球下落')gui.add(controls, 'friction', 0, 2).name('摩擦力').onChange((e) => {friction = e})gui.add(controls, 'restitution', 0, 2).name('弹性系数').onChange((e) => {restitution = e})gui.addColor(controls, 'color').name('小球颜色').onChange((e) => {threeSphereMaterial.color.setStyle(e)})
}/*** @description: 初始化Three场景*/
const initThree = () => {// 1 初始化Three场景three.scene = new THREE.Scene()// 2 初始化Three相机/*** THREE.PerspectiveCamera 初始化一个Three透视相机* fov:摄像机视锥体垂直视野角度* aspect:相机场景长宽比* near:摄像机视锥体近端面* far:摄像机视锥体远端面*/three.camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,0.1,1000)three.camera.position.set(20, 20, 20) // 设置相机位置three.camera.lookAt(three.scene.position) // 设置相机视角朝向Three场景scene// three.scene.add(new THREE.AxesHelper(20)) // 添加场景坐标轴// 3 初始化Three渲染器three.renderer = new THREE.WebGLRenderer({ antialias: true }) // 初始化Three渲染器// three.renderer = new THREE.WebGLRenderer() // 初始化Three渲染器three.renderer.setClearColor(0xffffff, 1.0) // 设置渲染背景色three.renderer.shadowMap.enabled = true // 开启场景光照阴影效果three.renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染范围大小// 4 初始化光源three.scene.add(new THREE.AmbientLight(0x404040)) // 初始化坏境光// 初始化聚光源let spotLight = new THREE.SpotLight(0x999999)spotLight.position.set(-10, 30, 20) // 设置聚光源的位置spotLight.castShadow = true // 开启聚光源投射阴影// spotLight.distance = 1000000000three.scene.add(spotLight)// 5 添加Three到DOM下document.getElementById('threeContainer').appendChild(three.renderer.domElement)// OrbitControls轨道控制器let controls = new THREE.OrbitControls(three.camera, three.renderer.domElement)
}/*** @description: 初始化Cannon物理场景*/
const initCannon = () => {// 1 初始化Cannon中的物理世界Worldcannon.world = new CANNON.World()// 2 设置物理世界中的重力,设置为y轴负方向的-9.8 m/s²,模拟真实世界cannon.world.gravity.set(0, -9.8, 0)// 3 设置物理世界中的碰撞检测模式cannon.world.broadphase = new CANNON.NaiveBroadphase()// 4 设置物理世界中的联系材质,用于判断物体之间的接触关系// 4.1 声明混泥土材质// const cannonConcreteMaterial = new CANNON.Material('concrete')// 4.2 声明塑料材质// const cannonPlasticMaterial = new CANNON.Material('plastic')// 4.3 声明默认材质// const cannonDefaultMaterial = new CANNON.Material()// 4.4 创建两个默认材质的关联材质cannonDefaultCantactMaterial = new CANNON.ContactMaterial(cannonDefaultMaterial,cannonDefaultMaterial,{friction: 0.5,restitution: 0.7,})// 4.5 将两个默认材质添加到物理世界world中cannon.world.addContactMaterial(cannonDefaultCantactMaterial)
}/*** @description: 创建地板* 由于Plane不需要改变位置,所以不需要添加至MeshBodyToUpdate进行位置旋转同步*/
const createPlane = () => {// 1 创建Cannon中的地板刚体// 1.0 创建地板刚体形状let cannonPlaneShape = new CANNON.Plane()// 1.1 创建地板刚体的材质,默认材质// let cannonPlaneMaterial = new CANNON.Material()let cannonPlaneMaterial = cannonDefaultMaterial// 1.2 创建地板刚体的质量mass,质量为0的物体为静止的物体let cannonPlaneMass = 0// 1.3 创建地板刚体的位置position,坐标原点let cannonPlanePosition = new CANNON.Vec3(0, 0, 0)// 1.4 创建地板刚体的Bodylet cannonPlaneBody = new CANNON.Body({mass: cannonPlaneMass,position: cannonPlanePosition,shape: cannonPlaneShape,material: cannonPlaneMaterial,})// 1.5 旋转地板刚体Body,使其垂直与y轴// setFromAxisAngle方法第一个参数是旋转轴,第二个参数是角度cannonPlaneBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0),-Math.PI / 2)// 1.6 将cannonPlaneBody添加到物理场景world中cannon.world.addBody(cannonPlaneBody)// 2 创建Three中的地板网格// 2.0 创建Three中的地板网格形状let threePlaneGeometry = new THREE.PlaneGeometry(20, 20, 20)// 2.1 创建地板网格的材质let threePlaneMaterial = new THREE.MeshLambertMaterial({color: 0xa5a5a5,side: THREE.DoubleSide,})// 2.2 创建地板网格的meshlet threePlaneMesh = new THREE.Mesh(threePlaneGeometry,threePlaneMaterial)// 2.3 设置地板网格的旋转threePlaneMesh.rotation.x = -Math.PI / 2// 2.4 开启地表网格接收光照阴影threePlaneMesh.receiveShadow = true// 2.5 设置地板网格的位置,坐标原点threePlaneMesh.position.set(0, 0, 0)// 2.6 设置地板网格的大小缩放threePlaneMesh.scale.set(2, 2, 2)// 2.7 将threePlaneMesh添加到Three场景scene中three.scene.add(threePlaneMesh)
}/*** @description: 创建球体* 球体是含有质量mass的,所以需要添加至MeshBodyToUpdate进行位置旋转同步*/
const createSphere = () => {// 1 创建Cannon中的球体刚体// 1.1 创建球体刚体形状,参数为球体的半径let cannonSphereShape = new CANNON.Sphere(1)// 1.2 创建球体刚体的材质,默认材质// let cannonSphereMaterial = new CANNON.Material()let cannonSphereMaterial = cannonDefaultMaterial// 1.3 创建球体刚体的质量mass,单位为kglet cannonSphereMass = 5// 1.4 创建球体刚体的位置positionlet cannonSpherePosition = new CANNON.Vec3(0, 10, 0)// 1.5 创建球体刚体的BodycannonSphereBody = new CANNON.Body({mass: cannonSphereMass,shape: cannonSphereShape,position: cannonSpherePosition,material: cannonSphereMaterial,})// 1.6 将cannonSphereBody添加到物理场景world中cannon.world.addBody(cannonSphereBody)// 2 创建Three中的球体网格// 2.1 创建球体网格的形状let threeSphereGeometry = new THREE.SphereGeometry(1, 32, 32)// 2.2 创建球体网格的材质threeSphereMaterial = new THREE.MeshStandardMaterial({color: 0xFFB6C1,})// 2.3 创建球体网格的Meshlet threeSphereMesh = new THREE.Mesh(threeSphereGeometry,threeSphereMaterial)// 2.4 设置球体网格投射光照阴影threeSphereMesh.castShadow = true// 2.5 将threeSphereMesh添加到Three场景的scene中three.scene.add(threeSphereMesh)// 3 将cannonSphereBody和threeSphereMesh添加到MeshBodyToUpdate中MeshBodyToUpdate.push({body: cannonSphereBody,mesh: threeSphereMesh,})
}/*** @description: 循环渲染场景*/
const render = () => {/*** 设置更新物理世界world的步长timestep* 这里选用60Hz的速度,即1.0 / 60.0*/cannon.world.step(1.0 / 60.0)// 更新MeshBodyToUpdate中的Mesh和Body,使其位置和旋转同步for (const object of MeshBodyToUpdate) {object.mesh.position.copy(object.body.position)object.mesh.quaternion.copy(object.body.quaternion)}requestAnimationFrame(render)three.renderer.render(three.scene, three.camera)
}initThree()
initCannon()
createPlane()
createSphere()
initDatGui()
render()// 改变窗口大小重新渲染场景
window.addEventListener('resize', () => {three.camera.aspect = window.innerWidth / window.innerHeightthree.camera.updateProjectionMatrix()three.renderer.setSize(window.innerWidth, window.innerHeight)
})

初见物理引擎库Cannon.js:使用dat.gui修改物体属性相关推荐

  1. 16 Three.js使用dat.GUI简化试验流程

    简介 使用这个插件的最省事的地方在于,调试很方便的调节相关的值,从而影响最后绘制的结果.而dat.GUI实现的东西也很简单,理解起来也很好理解. 案例查看地址:http://www.wjceo.com ...

  2. C++ 3D物理引擎库BulletPhysics基本使用

    前言:最近在接触OpenGl和DX11的时候,顺便学习了Bullet这个3D物理引擎的基本使用,记录一下. |BulletPhysics介绍 BulletPhysics是一个跨平台的开源物理引擎,也是 ...

  3. 一篇上手LayaAir的3D物理引擎

    昨天,我们分享了一篇2D物理文档<LayaAirIDE的可视化2D物理使用文档>. 今天,我们针对LayaAir引擎的初学者,以及对物理引擎使用不熟悉的开发者,再来分享一篇3D物理文档,本 ...

  4. Cannon.js -- 3d物理引擎

    文章目录 前言 一.关于Cannon.js 二.Cannon.js的使用 最后 注意点: 优化 事件 其他 本文完整代码下载: 相关链接: 前言 本篇将介绍Cannon.js -- 3d物理引擎的基础 ...

  5. web物理引擎p2.js入门手册

    最近在学egret游戏引擎,他们推荐的第三方物理引擎库是p2.js,瑞士的一个杀马特开发的,中文资料很少,于是我就把他github项目的wiki给翻译出来了,这里是项目地址:https://githu ...

  6. JavaScript 的物理引擎对比

    在本文中,我们将对比看一下当前三个非常流行的和一个目前还在开发中的JavaScript 物理引擎库,分别是: box2dweb,Ammo.js,JigLibJS 以及 Connon.js.我们会简短的 ...

  7. 基于HT for Web 3D呈现Box2DJS物理引擎

    为什么80%的码农都做不了架构师?>>>    上篇我们基于HT for Web呈现了A* Search Algorithm的3D寻路效果,这篇我们将采用HT for Web 3D来 ...

  8. 基于HTML5的WebGL结合Box2DJS物理引擎应用

    上篇我们基于HT for Web呈现了A* Search Algorithm的3D寻路效果,这篇我们将采用HT for Web 3D来呈现Box2DJS物理引擎的碰撞效果,同上篇其实Box2DJS只是 ...

  9. html5游戏开发box2djs,基于HT for Web 3D呈现Box2DJS物理引擎

    上篇我们基于HT for Web呈现了A* Search Algorithm的3D寻路效果,这篇我们将采用HT for Web 3D来呈现Box2DJS物理引擎的碰撞效果,同上篇其实Box2DJS只是 ...

最新文章

  1. SSD数据可靠性问题分析
  2. telnet与ssh的配置
  3. Spring5源码 - 01 BeanDefination源码分析
  4. Class 17 - 1 动态渲染页面爬取 — Selenium使用
  5. 开关造成的毛刺_玻璃面板开关钻孔加工
  6. linux ls 中文乱码_每天一个linux命令:Linux文件类型与扩展名
  7. 一个Linux中用于监控的简易shell脚本
  8. mybatis对mysql进行分页
  9. 关于java中assert(断言)的使用讲解
  10. 国家级计算机实验教学师范中心,国家级实验教学示范中心
  11. 桌面许多快捷方式图标/文件左下角出现绿色对号小图标
  12. android底部滑动出现虚拟按键,Android适配底部虚拟按键的方法详解
  13. android推送设置功能
  14. HLS 流媒体服务与加解密
  15. 为什么互联网巨头热衷“年终盘点”?
  16. 我用Python逆向登录世界上最大的游戏平台,steam加密手段有多高明【内附源码】
  17. 读取db服务器信息出错,尝试读取foxpro dbf并获取错误:无法初始化链接服务器的OLE DB提供程序“MSDASQL”的数据源对象“(空)”...
  18. WR703N烧写openwrt全过程
  19. 【程序源代码】小程序-前后开源
  20. 使用有道api抓取读音

热门文章

  1. c++获取mac操作系统的版本号
  2. C语言单片机压力传感器报警器,单片机压力传感器的实时监测和报警程序
  3. 一加6可以刷的rom_一加6MIUI刷机包(系统刷机完整固件升级包MIUI10)
  4. 生僻字怎么用计算机打出来,电脑搜狗输入法生僻字怎么打?电脑搜狗输入法怎么打不认识的字?...
  5. MMD新人学习记录博客1
  6. 【原创】【学习笔记5】关于console相关修改
  7. 大白话5分钟带你走进人工智能-第30节集成学习之Boosting方式和Adaboost
  8. unittest+tomorrow+BeautifulReport实现自动化测试的多线程
  9. 微信小程序生成小程序码图片-【附坑点】
  10. 逻辑推理题:谁是凶手