主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green, qklhk-chocolate
贡献主题:https://github.com/xitu/juejin-markdown-themes
theme: github
highlight:
在之前的文章中写到堆叠游戏, 基本上每一步都有写
这是之前的实现效果
其中没有做完的无效区域自由下落,将在本文中介绍
第一步,引入文件
物体自然下落需要物理学的插件,ammo.wasm.js
和 AmmoPhysics.js
AmmoPhysics.js是基于ammo.js封装的,
所以需要将两个插件都引入到项目中
``` html
```
typescript import { AmmoPhysics } from '../utils/AmmoPhysics.js';
源码中AmmoPhysics
返回的是一个Promise对象
``` typescript init()
async function init() { physics = await AmmoPhysics(); console.log(physics) } ```
打印出physics
提供两个方法,其中addmesh
将需要实现物体下落的模型添加进去
第二步:添加底板
typescript const floor = new THREE.Mesh( new THREE.BoxBufferGeometry(100, 5, 100), new THREE.MeshNormalMaterial({ color: 0x111111 }) ); _this.scene.add(floor);
接下来将底板添加到physics
中
physics.addMesh(floor);
addMesh
接受两个参数,第一个参数是需要施加力的物体,第二个参数是数字,可选,默认为0
,如果不填,物体将作为底板存在
addMesh
部分代码
```javascript function addMesh( mesh, mass = 0 ) {
const shape = getShape( mesh.geometry );
if ( shape !== null ) {
if ( mesh.isInstancedMesh ) {
handleInstancedMesh( mesh, mass, shape );
} else if ( mesh.isMesh ) {
handleMesh( mesh, mass, shape );
}
}
} ```
现在将底板的position.y
设置为100,让底板从高度100的位置降落 floor.position.y = 100 physics.addMesh(floor, 1);
目前算是实现了某个物体的自由降落,接下来将floor
恢复,并另创建两个自由落体
```typescript const material = new THREE.MeshNormalMaterial();
const matrix = new THREE.Matrix4();
const geometryBox = new THREE.BoxBufferGeometry(5, 5, 5); var boxes = new THREE.InstancedMesh(geometryBox, material, 2); _this.scene.add(boxes);
for (let i = 0; i < boxes.count; i++) {
matrix.setPosition(Math.random() - 10, (i + 1) * 10, Math.random() - (i * 4)); boxes.setMatrixAt(i, matrix); }
physics.addMesh(boxes, 1); ``` 创建两个实例化网格,官网上介绍实例化网格可以提升渲染性能
你可以使用 InstancedMesh 来渲染大量具有相同几何体与材质、但具有不同世界变换的物体。 使用 InstancedMesh 将帮助你减少 draw call 的数量,从而提升你应用程序的整体渲染性能。
物体自然下落的小例子大概就是这样,接下来需要将自由下落的物体添加到游戏中
在游戏中,floorGroup
中的每一个物体都将设置为不加力的物体,将所有的无效区域设置为自然下落的物体
嵌入到游戏
底板
在游戏第一次创建底板的时候定义物理学
定义一个initAmmo
方法用来赋值AmmoPhysics
方法
然后将底板加入进去
typescript async initAmmo() { this.physics = await AmmoPhysics(); this.initFloor() }
创建底板的方法,跟改造之前有点区别
``` typescript initFloor() { // 定义物理学插件 // this.physics = await new AmmoPhysics(); const w: number = this.size const h: number = 50 const l: number = this.size const floorParams = { w: w, h: h, l: l, x: w / 2, y: h / 2, z: l / 2 } this.floorCube = createCube(floorParams); // 临时将底板透明度降低0.5 方便观察物体降落 (this.floorCube.material as THREE.Material).transparent = true; (this.floorCube.material as THREE.Material).opacity = 0.5 // 将第一个底板添加到物理学插件中 this.floorGroup.add(this.floorCube) this.floorGroup.updateMatrix()
// 创建一个跟底板相同位置信息的实例化网格 const floor = instancedMesh(this.floorCube) this.physics.addMesh(floor) console.log(this.physics) // 创建一个可降落物体 const geometry = new THREE.BoxGeometry(5, 5, 5); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); cube.position.y = 80 const box = instancedMesh(cube) this.floorGroup.add(box) // 参数传1 物体将以下落属性进行运动 this.physics.addMesh(box, 1) } ```
创建实例化网格的方法
typescript // 创建实体网格 export function instancedMesh(box: any, position?:THREE.Vector3) { const size = new THREE.Vector3() getSize(box, size) const material = new THREE.MeshNormalMaterial(); // 创建实例化网格 const geometryBox = new THREE.BoxBufferGeometry(size.x, size.y, size.z); var boxes = new THREE.InstancedMesh(geometryBox, material, 1); // 定义一个四维矩阵,用来存放物体的位置信息 const matrix = new THREE.Matrix4(); // 将位置信息传入到四维矩阵中 if(position && position instanceof THREE.Vector3) { matrix.setPosition(position.clone()) } else { matrix.setPosition(box.position.clone()) } // 将四维矩阵设置到新的实体网格模型中 boxes.setMatrixAt(0, matrix); return boxes }
可以先看一下刚创建的box
的运动效果
box
作为自然降落物体, floor
作为底板物体不动,当自然降落物体碰撞到底板物体,将进行物理碰撞
效果,
就像现实中,从比桌子高的位置扔下一个方块,遇到桌子角,将进行碰撞、反转、降落等一系列物理运动
接下来就是要对主角分出的有效区域和无效区域进行操作
有效区域
有效区域的效果和底板的效果是相同的,都不进行运动,所以第一步创建一个与有效区域等同信息的实例化网格
```typescript // 克隆一个有效区域 const newMesh:any = meshArr[0].clone();
// 临时处理材质为透明 (newMesh.material as THREE.Material).transparent = true; (newMesh.material as THREE.Material).opacity = 0.5
// 获取主角的中心点作为有效区域底板的位置 const center = new THREE.Vector3() getCenter(newMesh, center)
const phyMesh = instancedMesh(this.floorCube, center) // 有效区域不动,第二参数默认不传 this.physics.addMesh(phyMesh) ```
getCenter
是封装好的获取元素中心点的方法,新创建的实例化网格将以这个中心点作为pisition
信息
typescript export function getCenter(mesh:THREE.Object3D, v3: THREE.Vector3) { if (v3 instanceof THREE.Vector3) { getBox(mesh).getCenter(v3); } }
接下来再创建一个可移动方块对新创建的实例化网格进行测试
```typescript // 获取主角的中心点作为有效区域底板的位置 const center = new THREE.Vector3() getCenter(newMesh, center) // console.log(center)
const phyMesh = instancedMesh(newMesh, center.clone()) // this.floorGroup.add(phyMesh) // 有效区域不动,第二参数默认不传 this.physics.addMesh(phyMesh,1) // 创建一个测试方块 const geometry = new THREE.BoxGeometry(5, 5, 5); const material = new THREE.MeshNormalMaterial(); const cube = new THREE.Mesh(geometry, material); const newCenter = center.clone() // 获取有效区域尺寸,将测试方块放置在有效区域边缘 const size = new THREE.Vector3() getSize(newMesh, size) newCenter.y = newCenter.y + 15 newCenter.x = newCenter.x + size.x/2 cube.position.copy(newCenter) cube.updateMatrix ()
// 创建一个实例化网格 const phyCube = instancedMesh(cube) this.floorGroup.add(phyCube) // 将测试方块创建的实例化网格作为可移动对象进行测试 this.physics.addMesh(phyCube, 1) ``` 测试方块遇到有效区域边缘,碰撞,翻滚,降落,再遇到底板边缘,碰撞,翻滚,降落
有效区域的处理大致就是这样,接下来将处理一下无效区域,作为自由降落的物体
无效区域处理
堆叠层外面的视为无效区域
```typescript // 判断无效区域 if (meshArr[1]) { // 创建无效区域实体网格 const newMesh = meshArr[1]
// 获取无效区域中心点 const center = new THREE.Vector3() getCenter(newMesh, center)
// 创建无效区域实体化网格 const phyMesh = instancedMesh(newMesh, center)
this.invalidGroup.add(phyMesh) // 自由落体 this.physics.addMesh(phyMesh, 1)
} ```
一层的实现效果,可能看不出来效果,如果是多层可以体验到更好的效果
多层的效果,可能动图比较大,加载比较慢
结束
好啦,堆叠游戏写到这里就可以宣告结束了,可能会有一些bug或者其他问题,有时间慢慢优化吧~
在之前的文章写过其他的步骤
感兴趣的童鞋可以体验一下,欢迎童鞋们在评论区打出你们的分数哦
没有bug的程序 不是好程序!!!