three.js物理学,物体自然下落

lxf2023-05-12 00:53:25

在之前的文章中写到堆叠游戏, 基本上每一步都有写

这是之前的实现效果

three.js物理学,物体自然下落

可以在这里体验游戏

其中没有做完的无效区域自由下落,将在本文中介绍

第一步,引入文件

物体自然下落需要物理学的插件,ammo.wasm.jsAmmoPhysics.js AmmoPhysics.js是基于ammo.js封装的,

所以需要将两个插件都引入到项目

<script src="./static/js/ammo.wasm.js"></script>
import { AmmoPhysics } from '../utils/AmmoPhysics.js';

three.js物理学,物体自然下落

源码中AmmoPhysics返回的是一个Promise对象

init()

async function init() {
  physics = await AmmoPhysics();
  console.log(physics)
}

打印出physics 提供两个方法,其中addmesh将需要实现物体下落的模型添加进去

three.js物理学,物体自然下落

第二步:添加底板

const floor = new THREE.Mesh(
  new THREE.BoxBufferGeometry(100, 5, 100),
  new THREE.MeshNormalMaterial({ color: 0x111111 })
);
_this.scene.add(floor);

three.js物理学,物体自然下落

接下来将底板添加到physics

physics.addMesh(floor);

addMesh 接受两个参数,第一个参数是需要施加力的物体,第二个参数是数字,可选,默认为0,如果不填,物体将作为底板存在

addMesh 部分代码

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);

three.js物理学,物体自然下落

目前算是实现了某个物体的自由降落,接下来将floor恢复,并另创建两个自由落体

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 的数量,从而提升你应用程序的整体渲染性能。

three.js物理学,物体自然下落

物体自然下落的小例子大概就是这样,接下来需要将自由下落的物体添加到游戏中

在游戏中,floorGroup中的每一个物体都将设置为不加力的物体,将所有的无效区域设置为自然下落的物体

嵌入到游戏

底板

在游戏第一次创建底板的时候定义物理学

定义一个initAmmo方法用来赋值AmmoPhysics方法

然后将底板加入进去

async initAmmo() {
  this.physics = await AmmoPhysics();
  this.initFloor()
}

创建底板的方法,跟改造之前有点区别

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)
}

创建实例化网格的方法

// 创建实体网格
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的运动效果

three.js物理学,物体自然下落

box作为自然降落物体, floor作为底板物体不动,当自然降落物体碰撞到底板物体,将进行物理碰撞效果,

就像现实中,从比桌子高的位置扔下一个方块,遇到桌子角,将进行碰撞、反转、降落等一系列物理运动

接下来就是要对主角分出的有效区域和无效区域进行操作

有效区域

有效区域的效果和底板的效果是相同的,都不进行运动,所以第一步创建一个与有效区域等同信息的实例化网格

// 克隆一个有效区域
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信息

export function getCenter(mesh:THREE.Object3D, v3: THREE.Vector3) {
  if (v3 instanceof THREE.Vector3) {
    getBox(mesh).getCenter(v3);
  }
}

接下来再创建一个可移动方块对新创建的实例化网格进行测试

// 获取主角的中心点作为有效区域底板的位置
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)

测试方块遇到有效区域边缘,碰撞,翻滚,降落,再遇到底板边缘,碰撞,翻滚,降落

three.js物理学,物体自然下落

有效区域的处理大致就是这样,接下来将处理一下无效区域,作为自由降落的物体

无效区域处理

堆叠层外面的视为无效区域

three.js物理学,物体自然下落

// 判断无效区域
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)

}

一层的实现效果,可能看不出来效果,如果是多层可以体验到更好的效果

three.js物理学,物体自然下落

多层的效果,可能动图比较大,加载比较慢

three.js物理学,物体自然下落

结束

好啦,堆叠游戏写到这里就可以宣告结束了,可能会有一些bug或者其他问题,有时间慢慢优化吧~

在之前的文章写过其他的步骤

第一步 基础 初始化游戏

第二步 控制 控制主角移动以及停止

第三步 切割等功能

第四步 结束判定,记分器等

代码

感兴趣的童鞋可以体验一下,欢迎童鞋们在评论区打出你们的分数哦

没有bug的程序 不是好程序!!!

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!