Cannon-es.js之removeConstraint破坏约束案例

本文目录

  • 前言
  • 最终效果
  • 1、postStep
  • 2、前置准备
    • 2.1 代码
    • 2.2 效果
  • 3、removeConstraint
    • 3.1 解除约束代码
    • 效果
  • 4、完整代码

前言

在3D物理引擎的广阔天地中,cannon-es以其轻量级、高性能和易于集成的特点,成为了WebGL环境中物理模拟的首选工具。它不仅能够精准地模拟刚体碰撞、力学行为和约束等物理现象,还提供了丰富的API供开发者自由调用。
本文将深入探讨cannon-es中的两个关键特性:postStep事件(尽管cannon-es官方文档中可能并未直接提及,但我们可以基于物理引擎的通用概念进行阐述)和removeConstraint方法。postStep通常被理解为在物理模拟步骤之后执行的操作,它允许开发者在物理世界更新后插入自定义逻辑。而removeConstraint方法则是用来从物理世界中移除不再需要的约束,从而优化模拟性能和准确性。
接下来,我们将通过前置代码准备、解除约束的代码示例及其效果展示,以及完整的代码汇总,来详细解析这两个特性的实际应用。无论你是物理引擎的新手,还是经验丰富的开发者,本文都将为你提供一份详尽的指南,帮助你更好地掌握cannon-es的精髓。

最终效果

请添加图片描述

1、postStep

Cannon-es.js作为Cannon.js的现代分支,是一款专为WebGL设计的轻量级、高性能3D物理引擎,能够轻松模拟真实世界中的物理现象,如刚体碰撞、力学模拟和约束等。在物理引擎中,模拟步骤之后执行一些额外的逻辑,比如更新渲染场景、处理用户输入、计算新的物理状态等。即在物理模拟步骤之后执行的操作。postStep事件是在每个物理时间步后被调用的事件。这个事件可以用于执行一些与物理更新后的状态更改相关的操作。所以我们可以监听在物体约束中被力冲击后断开约束。接下来将用代码演示。


2、前置准备

2.1 代码

<template><canvas ref="cannonDemo" class="cannonDemo"></canvas>
</template><script setup>
import { onMounted, ref } from "vue"
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
const cannonDemo = ref('null')onMounted(() => {const cannonDemoDomWidth = cannonDemo.value.offsetWidthconst cannonDemoDomHeight = cannonDemo.value.offsetHeight// 创建场景const scene = new THREE.Scene// 创建相机const camera = new THREE.PerspectiveCamera( // 透视相机45, // 视角 角度数cannonDemoDomWidth / cannonDemoDomHeight, // 宽高比 占据屏幕0.1, // 近平面(相机最近能看到物体)1000, // 远平面(相机最远能看到物体))camera.position.set(0, 2, 70)// 创建渲染器const renderer = new THREE.WebGLRenderer({antialias: true, // 抗锯齿canvas: cannonDemo.value})// 设置设备像素比renderer.setPixelRatio(window.devicePixelRatio)// 设置画布尺寸renderer.setSize(cannonDemoDomWidth, cannonDemoDomHeight)const light = new THREE.AmbientLight(0x404040, 200); // 柔和的白光scene.add(light);let meshes = []let phyMeshes = []const physicsWorld = new CANNON.World()// 设置y轴重力physicsWorld.gravity.set(0, -9.82, 0)const planeShape = new CANNON.Box(new CANNON.Vec3(10, 0.05, 10))const planeBody = new CANNON.Body({shape: planeShape,position: new CANNON.Vec3(0,0,0)})physicsWorld.addBody(planeBody)phyMeshes.push(planeBody)const planeGeometry = new THREE.BoxGeometry(20, 0.1, 20)const planeMaterial = new THREE.MeshBasicMaterial({wireframe: true})const planeMesh = new THREE.Mesh(planeGeometry,planeMaterial)scene.add(planeMesh)meshes.push(planeMesh)const sphereShape = new CANNON.Sphere(1)const sphereGeometry = new THREE.SphereGeometry(1, 16, 16)const sphereMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true})let previousBodyfor(let i = 0; i < 10; i++) {const sphereBody = new CANNON.Body({shape: sphereShape,position: new CANNON.Vec3(0, 25 - i * 2.2 , 0),mass: i == 0 ? 0 : 1})physicsWorld.addBody(sphereBody)phyMeshes.push(sphereBody)const sphereMesh = new THREE.Mesh(sphereGeometry,sphereMaterial)scene.add(sphereMesh)meshes.push(sphereMesh)if (i > 0) {const constraint = new CANNON.DistanceConstraint(previousBody,sphereBody,2.2)physicsWorld.addConstraint(constraint)}previousBody = sphereBody}const sphereClickShape = new CANNON.Sphere(0.8)const sphereClickGeometry = new THREE.SphereGeometry(0.8, 16, 16)const sphereClickMaterial = new THREE.MeshBasicMaterial({color: 0x0000ff})window.addEventListener('click', () => {const sphereClickBody = new CANNON.Body({mass: 1,shape: sphereClickShape,position: new CANNON.Vec3(5, 15, 0)})physicsWorld.addBody(sphereClickBody)phyMeshes.push(sphereClickBody)sphereClickBody.velocity.set(-10, 0, 0)const sphereClickMesh = new THREE.Mesh(sphereClickGeometry, sphereClickMaterial)scene.add(sphereClickMesh)meshes.push(sphereClickMesh)})const axesHelper = new THREE.AxesHelper(30);scene.add(axesHelper);const updatePhysic = () => { // 因为这是实时更新的,所以需要放到渲染循环动画animate函数中physicsWorld.step(1 / 60)for (let i = 0; i < phyMeshes.length; i++) {meshes[i].position.copy(phyMeshes[i].position)meshes[i].quaternion.copy(phyMeshes[i].quaternion)}}// 控制器const control = new OrbitControls(camera, renderer.domElement)// 开启阻尼惯性,默认值为0.05control.enableDamping = true// 渲染循环动画function animate() {// 在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒)requestAnimationFrame(animate)updatePhysic()// 更新控制器。如果没在动画里加上,那必须在摄像机的变换发生任何手动改变后调用control.update()renderer.render(scene, camera)}// 执行动画animate()
})</script>
<style scoped>
.cannonDemo {width: 100vw;height: 100vh;
}
</style>

2.2 效果

请添加图片描述
可以看到我们就有点击生成蓝色小球冲击的效果。


3、removeConstraint

cannon-es中,约束(Constraints)是用来限制或控制物体运动的一种机制。通过约束,可以将物体连接在一起,或者限制物体在某些方向上的运动。而removeConstraint方法,则是用来从物理世界中移除已经添加的约束的。

3.1 解除约束代码

    physicsWorld.addEventListener('postStep', () => {for(let i = 0; i < physicsWorld.constraints.length; i++) {const constraint = physicsWorld.constraints[i]// 获取约束力度的绝对值大小let multiplier = Math.abs(constraint.equations[0].multiplier)console.log(multiplier)if (multiplier > 1000) {// 约束破坏physicsWorld.removeConstraint(constraint)}}})

效果

请添加图片描述
可以看到我前面3次由于力度不够所以约束不能被破坏,第4下的力直接把约束破坏了。


4、完整代码

最后给出完整代码:

<template><canvas ref="cannonDemo" class="cannonDemo"></canvas>
</template><script setup>
import { onMounted, ref } from "vue"
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
const cannonDemo = ref('null')onMounted(() => {const cannonDemoDomWidth = cannonDemo.value.offsetWidthconst cannonDemoDomHeight = cannonDemo.value.offsetHeight// 创建场景const scene = new THREE.Scene// 创建相机const camera = new THREE.PerspectiveCamera( // 透视相机45, // 视角 角度数cannonDemoDomWidth / cannonDemoDomHeight, // 宽高比 占据屏幕0.1, // 近平面(相机最近能看到物体)1000, // 远平面(相机最远能看到物体))camera.position.set(0, 2, 70)// 创建渲染器const renderer = new THREE.WebGLRenderer({antialias: true, // 抗锯齿canvas: cannonDemo.value})// 设置设备像素比renderer.setPixelRatio(window.devicePixelRatio)// 设置画布尺寸renderer.setSize(cannonDemoDomWidth, cannonDemoDomHeight)const light = new THREE.AmbientLight(0x404040, 200); // 柔和的白光scene.add(light);let meshes = []let phyMeshes = []const physicsWorld = new CANNON.World()// 设置y轴重力physicsWorld.gravity.set(0, -9.82, 0)const planeShape = new CANNON.Box(new CANNON.Vec3(10, 0.05, 10))const planeBody = new CANNON.Body({shape: planeShape,position: new CANNON.Vec3(0,0,0)})physicsWorld.addBody(planeBody)phyMeshes.push(planeBody)const planeGeometry = new THREE.BoxGeometry(20, 0.1, 20)const planeMaterial = new THREE.MeshBasicMaterial({wireframe: true})const planeMesh = new THREE.Mesh(planeGeometry,planeMaterial)scene.add(planeMesh)meshes.push(planeMesh)const sphereShape = new CANNON.Sphere(1)const sphereGeometry = new THREE.SphereGeometry(1, 16, 16)const sphereMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true})let previousBodyfor(let i = 0; i < 10; i++) {const sphereBody = new CANNON.Body({shape: sphereShape,position: new CANNON.Vec3(0, 25 - i * 2.2 , 0),mass: i == 0 ? 0 : 1})physicsWorld.addBody(sphereBody)phyMeshes.push(sphereBody)const sphereMesh = new THREE.Mesh(sphereGeometry,sphereMaterial)scene.add(sphereMesh)meshes.push(sphereMesh)if (i > 0) {const constraint = new CANNON.DistanceConstraint(previousBody,sphereBody,2.2)physicsWorld.addConstraint(constraint)}previousBody = sphereBody}const sphereClickShape = new CANNON.Sphere(0.8)const sphereClickGeometry = new THREE.SphereGeometry(0.8, 16, 16)const sphereClickMaterial = new THREE.MeshBasicMaterial({color: 0x0000ff})let muscle = 0window.addEventListener('click', () => {const sphereClickBody = new CANNON.Body({mass: 1,shape: sphereClickShape,position: new CANNON.Vec3(5, 15, 0)})physicsWorld.addBody(sphereClickBody)phyMeshes.push(sphereClickBody)muscle++sphereClickBody.velocity.set(-10, 0, 0)if (muscle > 3) {sphereClickBody.velocity.set(-50, 0, 0)}const sphereClickMesh = new THREE.Mesh(sphereClickGeometry, sphereClickMaterial)scene.add(sphereClickMesh)meshes.push(sphereClickMesh)})physicsWorld.addEventListener('postStep', () => {for(let i = 0; i < physicsWorld.constraints.length; i++) {const constraint = physicsWorld.constraints[i]// 获取约束力度的绝对值大小let multiplier = Math.abs(constraint.equations[0].multiplier)console.log(multiplier)if (multiplier > 1000) {// 约束破坏physicsWorld.removeConstraint(constraint)}}})const axesHelper = new THREE.AxesHelper(30);scene.add(axesHelper);const updatePhysic = () => { // 因为这是实时更新的,所以需要放到渲染循环动画animate函数中physicsWorld.step(1 / 60)for (let i = 0; i < phyMeshes.length; i++) {meshes[i].position.copy(phyMeshes[i].position)meshes[i].quaternion.copy(phyMeshes[i].quaternion)}}// 控制器const control = new OrbitControls(camera, renderer.domElement)// 开启阻尼惯性,默认值为0.05control.enableDamping = true// 渲染循环动画function animate() {// 在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒)requestAnimationFrame(animate)updatePhysic()// 更新控制器。如果没在动画里加上,那必须在摄像机的变换发生任何手动改变后调用control.update()renderer.render(scene, camera)}// 执行动画animate()
})</script>
<style scoped>
.cannonDemo {width: 100vw;height: 100vh;
}
</style>

在学习的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1552266.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】指针是啥东西?看这篇博客就够了!

指针到底是啥东西&#xff1f;很多人都有这样的问题&#xff0c;今天我就为大家来解答 首先看一行代码&#xff1a; int a; 很显然&#xff0c;这行代码的用途是定义变量&#xff0c;那么再看一行代码 int *a; 这下懵了吧&#xff0c;你们以为这是一行错误的代码&#xff…

【规控+slam】探索建图方案及代码分享

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言背景建图描述SLAM定位+感知数据标记构建地图自动探索建图规划方法一:手动遥控探索建图算法步骤方法二:手动给定目标点探索建图算法原理方法三:f…

动态规划最低票价

前言&#xff1a;之前看到过这个题目归结到动态规划&#xff0c;当初还没什么思路&#xff0c;其实就是定义好dp [ i ] 为到第 i 个的最小费用就行&#xff0c;我们可以用upper_bound来优化我们的查找下标 题目地址 class Solution { public:int mincostTickets(vector<int&…

Minstrel自动生成结构化提示,让AI为AI写提示词的多代理提示生成框架

在人工智能快速发展的今天&#xff0c;如何有效利用大型语言模型&#xff08;LLMs&#xff09;成为了一个普遍关注的话题。这是9月份的一篇论文&#xff0c;提出了LangGPT结构化提示框架和Minstrel多代理提示生成系统&#xff0c;为非AI专家使用LLMs提供了强大支持。 对于非人…

SpringBoot框架下的社区医院信息系统开发

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理社区医院信息平台的相关信息成为必然。开发…

关于pip install -e .的一点理解

笔者在安装库时对教程里面的pip install -e .产生了一些疑惑&#xff0c;查资料解决如下 参考资料&#xff1a;【python pip特殊用法】pip install -v -e . 命令详解-CSDN博客 首先Sources Root就是根目录 笔者最开始将ultralytics以pip install -e .方式安装在了D盘ultraly…

家用高清投影仪怎么选?目前口碑最好的投影仪推荐

双十一马上要到了&#xff0c;而且今年还有投影仪的家电国补&#xff0c;所以大家入手投影仪的需求也越来越多&#xff0c;但是家用高清投影仪怎么选&#xff1f;什么投影仪最适合家用&#xff1f;家庭投影仪哪个牌子质量最好&#xff1f;今天就给大家做一个2024性价比高的家用…

国庆节快乐

葡萄城在这里祝大家国庆快快乐&#xff1a; 10月葡萄城活动&#xff1a; 公开课 【从软件应用走向数据应用——葡萄城技术赋能数据挖掘】 新版本发布&#xff1a; 活字格 V10.0 Update1新版本发布

等保测评:企业数字安全的坚实盾牌

1.1 企业数字化转型的浪潮 在当今时代&#xff0c;企业数字化转型的浪潮正以前所未有的速度席卷全球&#xff0c;据IDC预测&#xff0c;到2023年&#xff0c;全球数字化转型支出将达到惊人的2.3万亿美元。这一趋势不仅重塑了企业的运营模式&#xff0c;更对企业的信息安全提出…

昇思MindSpore进阶教程--使能图算融合

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 正文开始 图算融合是MindSpore特有的网络…

氨基酸在PDB文件中的原子命名规则

氨基酸在PDB文件中的原子命名规则 氨基和羧基上的原子都采用本名&#xff0c;C, N, O, H, etc. 其它原子除 H 外&#xff0c;所有原子命名均采用“原子名后缀[编号]”形式。整体命名方法类似于图论中求解最大流问题时所采用的标号法。首先α-C被命名为CA。其后按照成键关系逐级…

Markdown笔记管理工具Haptic

什么是 Haptic &#xff1f; Haptic 是一个新的本地优先、注重隐私的开源 Markdown 笔记管理工具。它简约、轻量、高效&#xff0c;旨在提供您所需的一切&#xff0c;而不包含多余的功能。 目前官方提供了 docker 和 Mac 客户端。 Haptic 仍在积极开发中。以下是未来计划的一些…

尝鲜使用 YOLO V11 Fine-Tuning 训练自定义的目标检测模型

一、YOLO V11 2024年9月30日&#xff0c;Ultralytics官方团队宣布YOLOv11正式发布&#xff0c;标志着YOLO系列实时目标检测器的又一次重大升级。这一新版本不仅在准确性和检测速度上再创新高&#xff0c;还通过架构和训练方法的革新&#xff0c;极大地提升了目标检测的综合性能…

构建现代化社区医疗服务:SpringBoot平台

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理社区医院信息平台的相关信息成为必然。开发…

银行CRM系统的核心功能解析与应用价值

在当今竞争激烈的金融市场中&#xff0c;银行业务的成功与否&#xff0c;越来越依赖于高效而精准的客户关系管理系统&#xff08;CRM&#xff09;。Zoho CRM系统不仅帮助银行提升服务质量、增强客户满意度&#xff0c;还能有效地促进业务发展和风险控制。为了帮助读者更好地理解…

社区医疗健康管理:SpringBoot技术应用

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理社区医院信息平台的相关信息成为必然。开发…

如何从损坏的 USB 闪存驱动器中恢复文件

与您的内部硬盘驱动器一样&#xff0c;USB 闪存驱动器也将数据存储在其内存中。与笨重的硬盘不同&#xff0c;这些便携式拇指驱动器易于携带&#xff0c;并且很容易从中获取数据。除了有一天&#xff0c;当我们将其连接到 PC 只是为了发现数据无法访问时。您知道您保存了它&…

【算法竞赛】堆

堆是一种树形结构,树的根是堆顶,堆顶始终保持为所有元素的最优值。 有最大堆和最小堆,最大堆的根节点是最大值,最小堆的根节点是最小值。 本节都以最小堆为例进行讲解。 堆一般用二叉树实现,称为二叉堆。 二叉堆的典型应用有堆排序和优先队列。 二叉堆的概念 二叉堆是一棵…

vue2圆形标记(Marker)添加点击事件不弹出信息窗体(InfoWindow)的BUG解决

目录 一、问题详情 二、问题排查 三、解决方案 一、问题详情 地图上面的轨迹点希望能通过点击看到详细的经纬度信息&#xff0c;但是点击的时候就是显示不出来。 二、问题排查 代码都是参考高德的官方文档&#xff0c;初步看没有问题啊&#xff0c;但是点击事件就感觉失效…

Linux中的软硬链接和动静态库

硬链接 ln myfile.txt hard_file.link 264962 -rw-rw-r-- 2 zhangsan zhangsan 0 Sep 30 03:16 hard_file.link 264962 -rw-rw-r-- 2 zhangsan zhangsan 0 Sep 30 03:16 myfile.txt 273922 lrwxrwxrwx 1 zhangsan zhangsan 10 Sep 30 03:17 soft_file.link -> …