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

news2024/10/1 18:46:01

本文目录

  • 前言
  • 最终效果
  • 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.offsetWidth
    const 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 previousBody
    for(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.05
    control.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.offsetWidth
    const 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 previousBody
    for(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 = 0
    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)
        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.05
    control.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.coloradmin.cn/o/2182933.html

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

相关文章

基于R语言机器学习遥感数据处理与模型空间预测

随机森林作为一种集成学习方法&#xff0c;在处理复杂数据分析任务中特别是遥感数据分析中表现出色。通过构建大量的决策树并引入随机性&#xff0c;随机森林在降低模型方差和过拟合风险方面具有显著优势。在训练过程中&#xff0c;使用Bootstrap抽样生成不同的训练集&#xff…

在海量文档中找资料?5 步打造属于自己的搜索引擎

在海量的文件中寻找那个关键的会议记录&#xff0c;是否让你感到头疼&#xff1f; 想要快速回顾上个月的项目报告&#xff0c;却迷失了一小时都找不到&#xff1f; 找报价、找库存、找供应商&#xff0c;整天忙于各种找找找&#xff1f; 现在就来教大家如何做属于自己的搜索引…

分治法c++

分治法c 快速幂题目算法代码 大整数乘法题目思路代码 快速幂 题目 三个整数 a、b 和 m&#xff0c;计算 a 的 b 次幂对 m 取模的结果ab ac abc(ab)|m(a|mb|m)|m。|为数学中的求模运算符&#xff0c;相当于c语言中的%。(ab)|m(a|mb|m)|m 算法 二分法求幂。先求ab的一半&…

【算法竞赛】尺取法

尺取法(又称为双指针、Two Pointers)是算法竞赛中一个常)用的优化技巧,用来解决序列的区间问题,操作简单,容易编程。如果区间是单调的,也常常用二分法求解,所以很多问题用尺取法和二分法都行。另外,尺取法的操作过程和分治算法的步骤很相似,有时也用在分治中。 概念 什么是尺…

位运算(4)_丢失的数字

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 位运算(4)_丢失的数字 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 温馨提示:…

学习VTK的目的和方法

1 VTK&#xff08;Visualization Toolkit&#xff09;是一个开源的跨平台软件系统&#xff0c;用于三维计算机图形学、图像处理和可视化。学习VTK的主要目的有&#xff1a; 3D可视化&#xff1a; VTK提供了丰富的工具和算法&#xff0c;可以用来可视化各种科学数据&#xff0c…

VBA数据库解决方案第十五讲:Recordset集合中单个数据的精确处理

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

windows下安装nginx和基本配置

1. 下载 Nginx 从 Nginx 官方网站下载 Windows 版本的 Nginx。访问 Nginx 官网 并选择适合 Windows 的版本。通常应选择稳定版&#xff08;Stable version&#xff09; 2. 安装 Nginx 安装 Nginx 实际上是解压下载的文件。可以选择一个适合的位置来存放 Nginx 的文件夹 例如…

【数据结构初阶】排序算法(下)冒泡排序与归并排序

文章目录 4. 交换排序4. 1 冒泡排序 5. 归并排序6. 非比较排序6. 1 计数排序 5. 排序性能分析6. 排序算法复杂度及稳定度分析 4. 交换排序 交换排序基本思想: 所谓交换**&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置**。 交换排序的特点是…

下一代性能怪兽RTX 5090最新规格更新与Blackwell架构解析

据悉&#xff0c;目前各家AIC厂商已经陆续收到NVIDIA的相关资料&#xff0c;RTX 5090、RTX 5080已经正式进入开案阶段&#xff0c;也就是厂商们开始设计各自的产品方案了。不出意外&#xff0c;年初的CES 2025上会看到RTX 5090/5080的发布。 作为NVIDIA的新一代GPU&#xff0c…

2024年健康经济与大数据研讨会(HEBD 2024)2024 Seminar on Health Economics and Big Data

在线投稿&#xff1a;学术会议-学术交流征稿-学术会议在线-艾思科蓝 2024年经济决策与人工智能国际学术会议 &#xff08;EDAI 2024&#xff09;将在2024年11月08-10日在广东省广州市隆重举行。大会邀请来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师…

理解互联网链路:从本地ISP到Tier 1 ISP运营商

1. 互联网服务提供商&#xff08;ISP&#xff09; 互联网服务提供商&#xff08;ISP&#xff09;是指提供互联网接入服务的公司或组织。它们负责将用户连接到互联网&#xff0c;并提供相关的服务&#xff0c;如电子邮件、网站托管和其他在线服务。ISP可以分为不同的层级&#…

告别转换顾虑,来试试这四款pdf转换器~

各位小伙伴们&#xff0c;大家好&#xff01;今天我来给大家分享几款超级好用的PDF转换工具&#xff0c;无论是工作还是学习&#xff0c;相信这些工具都会给你带来极大的便利&#xff1b;别看PDF文件看似难搞&#xff0c;其实有了这些神器&#xff0c;一切都变得轻松又愉快&…

在线css像素Px到百分比(%)换算器

具体请前往&#xff1a;在线Px转百分比(%)工具--将绝对像素(px)长度单位转换为相对父级元素内尺寸的相对长度单位百分比(%)

PCL GridMinimum获取栅格最低点

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 GridMinimum 栅格最低点提取 2.1.2 可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长…

新农人的求索:既要种菜,也要种钱

澎湃新闻记者 何惠子 灯下立着一个玻璃瓶&#xff0c;内里空无一物&#xff0c;清晰透亮。 一只手握住瓶身。“就像这个瓶子。前途一片光明&#xff0c;但其实都在瓶子里。” 解晓巍说的是音乐——他曾梦想以此维生。事实上&#xff0c;这也适合描述农业。 在没有任何收入的202…

计算机网络:计算机网络概述 —— 初识计算机网络

文章目录 计算机网络组成部分网络架构协议与标准网络设备网络类型作用实际应用案例 计算机网络 计算机网络是指将多台计算机通过通信设备和通信链路连接起来&#xff0c;以实现数据和信息的交换和共享的技术和系统。它是现代信息社会的基础设施之一&#xff0c;也是互联网的基…

工业现场干扰问题及处理方法

目前&#xff0c;各种干扰在各类工业现场中均存在&#xff0c;所以仪表及控制系统的可靠性直接影响到现代化工业生产装置安全、稳定运行&#xff0c;系统的抗干扰能力是关系到整个系统可靠运行的关键。随着DCS、现场总线技术的应用&#xff0c;被控对象和被测信号往往分布在各个…

Ubuntu启动后第一次需要很久才能启动GTK应用问题

Ubuntu启动后第一次需要很久才能启动GTK应用问题 自从升级了 Ubuntu 之后&#xff0c;设备重启&#xff0c;发现打开 Terminal 、Nautilus 以及其他的GTK 应用都很慢&#xff0c;需要至少一分钟的时间启动。 刚开始也是拿着 journalctl 的异常日志去寻找答案&#xff0c;但是没…

cheese安卓版纯本地离线文字识别插件

目的 cheese自动化平台是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。可以采用Vscode、IDEA编写&#xff0c;支持Java、Python、nodejs、GO、Rust、Lua。cheese也包含图色功能&#xff0c;识别…