首页 > 其他分享 >Cannon-ES中RaycastVehicle的深入探索与实践

Cannon-ES中RaycastVehicle的深入探索与实践

时间:2024-10-09 11:50:23浏览次数:3  
标签:CANNON const Cannon THREE vehicle new RaycastVehicle ES

本文目录

前言

在三维物理引擎的世界里,Cannon-ES以其轻量级、高效和易于集成的特点,赢得了众多开发者的青睐。而RaycastVehicle,作为Cannon-ES中用于模拟复杂车辆运动的核心类,更是为开发者提供了强大的车辆动力学模拟功能。从简单的汽车驾驶到复杂的赛车游戏,RaycastVehicle都能轻松应对。
本文将带您深入探索RaycastVehicle的概念、核心特性和应用场景。我们将从理论到实践,逐步揭示RaycastVehicle的奥秘。通过详细的代码示例和效果展示,您将看到RaycastVehicle如何在Cannon-ES的物理世界中模拟出逼真的车辆运动。此外,我们还将探讨如何监听和施加力,以实现更加丰富的车辆交互效果。
无论您是Cannon-ES的新手,还是已经有一定经验的开发者,本文都将为您提供宝贵的参考和启示。让我们一起踏上这段探索之旅,共同领略RaycastVehicle的魅力吧!

1、RaycastVehicle

Cannon-es中,RaycastVehicle是一个重要的概念,它允许开发者在物理世界中模拟复杂的车辆运动。

1.1 概念

RaycastVehicleCannon-es中用于模拟车辆运动的类。它通过使用射线投射(Raycasting)技术来确定车辆与地面的接触点,并据此计算车辆的物理行为。这种技术使得RaycastVehicle能够处理各种复杂的地形和物理交互。

1.2 核心特性

  1. 射线投射技术:RaycastVehicle利用射线投射来确定车辆与地面的接触点。这些射线从车辆底部的多个点发出,并沿着车辆的运动方向投射到地面上。通过检测这些射线与地面的交点,RaycastVehicle能够计算出车辆的支撑点和摩擦力等物理参数。
  2. 多轮支持:RaycastVehicle支持多轮车辆模拟,这意味着它可以处理具有多个轮子的车辆。每个轮子都可以独立地进行射线投射和物理计算,从而更准确地模拟车辆的运动。
  3. 物理行为模拟:RaycastVehicle能够模拟车辆的多种物理行为,包括加速、减速、转向、碰撞等。这些行为都是基于物理原理进行计算的,因此能够呈现出逼真的车辆运动效果。

1.3 应用场景

RaycastVehicleCannon-es中具有广泛的应用场景,特别是在需要模拟车辆运动的3D物理模拟中。以下是一些典型的应用场景:

  • 游戏开发:在游戏开发中,RaycastVehicle可以用于模拟玩家的车辆控制。通过模拟车辆的物理行为,游戏可以提供更加逼真的驾驶体验。
  • 虚拟现实:在虚拟现实应用中,RaycastVehicle可以用于模拟用户在虚拟环境中的车辆驾驶。这可以增强用户的沉浸感和交互体验。
  • 物理模拟:在物理模拟领域,RaycastVehicle可以用于研究和分析车辆在不同条件下的运动特性。这有助于优化车辆设计、提高车辆性能以及确保车辆的安全性。

2、前置代码准备

<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, 40)
    // 创建渲染器
    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 planeShap = new CANNON.Plane()
    const planeBody = new CANNON.Body({
        shape: planeShap,
        mass: 0,
        type: CANNON.BODY_TYPES.STATIC,
        position: new CANNON.Vec3(0, 0, 0)
    })
    planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
    physicsWorld.addBody(planeBody)
    phyMeshes.push(planeBody)

    const planeGeometry = new THREE.PlaneGeometry(100, 100)
    const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xE0E0E0, side: THREE.DoubleSide })

    const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
    scene.add(planeMesh)
    meshes.push(planeMesh)

    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>

我们也是初始化了整个场景,并且放了平面。


3、RaycastVehicle的使用

3.1 代码

创建悬架车架及轮子,代码如下:

    // 创建车身
    let chassisShape = new CANNON.Box(new CANNON.Vec3(2,0.5,1))
    let chassisBody = new CANNON.Body({
        mass: 1,
        shape: chassisShape,
        position: new CANNON.Vec3(0, 5, 0)
    })
    physicsWorld.addBody(chassisBody)
    phyMeshes.push(chassisBody)

    // 创建视图车身
    let chassisMesh = new THREE.Mesh(
        new THREE.BoxGeometry(4,1,2),
        new THREE.MeshBasicMaterial({color: 0x0000ff})
    )
    scene.add(chassisMesh)
    meshes.push(chassisMesh)

    // 创建复杂车架
    const vehicle = new CANNON.RaycastVehicle({
        chassisBody: chassisBody
    })

    // 车轮配置
    const wheelOptions = {
        // 半径
        radius: 0.5,
        // 车轮垂直哪个轴
        directionLocal: new CANNON.Vec3(0, -1, 0),
        // 设置悬架刚度
        suspensionStiffness: 30,
        // 设置悬架的休息长度
        suspensionRestLength: 0.5,
        // 设置车轮的滑动摩擦力
        frictionSlip: 1.5,
        // 悬架拉伸阻尼
        dampingRelaxation: 2.3,
        // 悬架压缩阻尼
        dampingCompression: 4.3,
        // 最大悬架力
        maxSuspensionForce: 100000,
        // 设置最大的悬架变化
        maxSuspensionTravel: 0.3,
        // 设置车轮的转向轴
        axleLocal: new CANNON.Vec3(0,0,1)
    }

    // 添加
    vehicle.addWheel({
        ...wheelOptions,
        // 车轮位置
        chassisConnectionPointLocal: new CANNON.Vec3(-1,0,1)
    })
    vehicle.addWheel({
        ...wheelOptions,
        // 车轮位置
        chassisConnectionPointLocal: new CANNON.Vec3(-1,0,-1)
    })
    vehicle.addWheel({
        ...wheelOptions,
        // 车轮位置
        chassisConnectionPointLocal: new CANNON.Vec3(1,0,1)
    })
    vehicle.addWheel({
        ...wheelOptions,
        // 车轮位置
        chassisConnectionPointLocal: new CANNON.Vec3(1,0,-1)
    })

    vehicle.addToWorld(physicsWorld)

    // 车轮形状
    const wheelShape = new CANNON.Cylinder(0.5,0.5,0.2,20);
    const wheelGeometry = new THREE.CylinderGeometry(0.5,0.5,0.2,20)
    const wheelMaterial = new THREE.MeshBasicMaterial({color: 0x888888})

    let wheelBodies = []
    for(let i = 0; i < vehicle.wheelInfos.length; i++) {
        const wheel = vehicle.wheelInfos[i]
        const cylinderBody = new CANNON.Body({
            mass: 0,
            shape: wheelShape
        })
        cylinderBody.position.copy(wheel.chassisConnectionPointWorld)
        cylinderBody.quaternion.copy(chassisBody.quaternion)
        // physicsWorld.addBody(cylinderBody)
        phyMeshes.push(cylinderBody)
        wheelBodies.push(cylinderBody)

        const cylinderMesh = new THREE.Mesh(wheelGeometry, wheelMaterial)
        cylinderMesh.rotation.x = -Math.PI/2
        const wheelObj = new THREE.Object3D()
        wheelObj.add(cylinderMesh)
        scene.add(wheelObj)
        meshes.push(wheelObj)
    }

physicsWorld.addEventListener('postStep', () => {
    for(let i = 0; i < vehicle.wheelInfos.length; i++) {
        vehicle.updateWheelTransform(i)
        const t = vehicle.wheelInfos[i].worldTransform
        const wheelBody = wheelBodies[i]
        wheelBody.position.copy(t.position)
        wheelBody.quaternion.copy(t.quaternion)

    }
})

3.2 效果

请添加图片描述


4、监听施加力

4.1 代码

我们监听事件以便给车子施加力,代码如下:

window.addEventListener('keydown', (event) => {
        if (event.key == 'w') {
            vehicle.applyEngineForce(-100, 0)
            vehicle.applyEngineForce(-100, 1)

        }
        if (event.key == 's') {
            vehicle.applyEngineForce(100, 0)
            vehicle.applyEngineForce(100, 1)

        }
        if (event.key == 'a') {
            vehicle.setSteeringValue(Math.PI/4, 0)
            vehicle.setSteeringValue(Math.PI/4, 1)

        }
        if (event.key == 'd') {
            vehicle.setSteeringValue(-Math.PI/4, 0)
            vehicle.setSteeringValue(-Math.PI/4, 1)

        }
    })
    window.addEventListener('keyup', (event) => {
        if (event.key == 'w') {
            vehicle.applyEngineForce(0, 0)
            vehicle.applyEngineForce(0, 1)

        }
        if (event.key == 's') {
            vehicle.applyEngineForce(0, 0)
            vehicle.applyEngineForce(0, 1)

        }
        if (event.key == 'a') {
            vehicle.setSteeringValue(0, 0)
            vehicle.setSteeringValue(0, 1)

        }
        if (event.key == 'd') {
            vehicle.setSteeringValue(0, 0)
            vehicle.setSteeringValue(0, 1)

        }
    })

4.2 效果

请添加图片描述

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

标签:CANNON,const,Cannon,THREE,vehicle,new,RaycastVehicle,ES
From: https://blog.csdn.net/weixin_44103733/article/details/142761941

相关文章

  • 学习011-08-03 Numeric Properties(数字属性)
    NumericProperties(数字属性)XAFsupportsPropertyEditorsfornumericdatatypes(byte,int,decimal,long,correspondingnullabletypes,etc.)onallplatforms.However,WinForms,ASP.NETWebForms,andBlazorUIapplicationsusedifferentformattingru......
  • 学习011-08-03-01 Numeric Properties in XPO(XPO中的数字属性)
    NumericPropertiesinXPO(XPO中的数字属性)TheexamplebelowillustrateshowtoimplementNumericPropertiesinanXPOpersistentclass.下面的示例说明了如何在XPO持久类中实现数字属性。C#privatedoubledoubleProperty;publicdoubleDoubleProperty{g......
  • 学习011-08-03-02 Numeric Properties in EF Core(EF Core中的数字属性)
    NumericPropertiesinEFCore(EFCore中的数字属性)TheexamplebelowillustrateshowtoimplementNumericPropertiesinanEFCoreclass.下面的示例说明了如何在EFCore类中实现数字属性。C#publicvirtualdoubleDoubleProperty{get;set;}publicvirtual......
  • 解决ERROR ResizeObserver loop completed with undelivered notifications.
    https://www.cnblogs.com/luo9tian/p/18116299该报错虽然不影响项目运行,但是影响开发效率,总是弹出报错的黑框很烦人该报错原因:newResizeObserver包裹的方法,在布局发生变化时,不支持每帧都调用解决方法:用window.requestAnimationFrame包裹回调函数在App.vue/main.js中加......
  • springboot 添加@Test(org.junit.Test) 注解后,idea右键菜单,没有运行项
    网上试了很多办法,包括检查idea的junit插件是否已安装,我安装了也用不了清除idea缓存,也不行。 后来将org.junit.Test换成  org.junit.jupiter.api.Test就可以了。maven引入包(注意这里版本换成5.9.3后不行)<dependency><groupId>org.junit.jupiter</groupI......
  • DevExpress WinForms中文教程:Data Grid - 如何完成数据输入验证?
    本教程介绍DevExpressWinForm的DataGrid控件是如何利用网格组件完成数据输入验证的。P.S:DevExpressWinForms拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是......
  • wordpress建立数据库连接时出错怎么办
    WordPress在建立数据库连接时出现问题通常是因为配置文件 wp-config.php 中的信息不正确或数据库本身的问题。你可以按照以下步骤来排查和解决问题:检查配置文件:打开你的WordPress安装目录下的 wp-config.php 文件。确认数据库名称(DB_NAME)、用户名(DB_USER)、密码......
  • 【Spring Security OAuth2】- 【使用Spring MVC开发RESTful API】 异步处理rest服务
    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬学习必须往深处挖,挖的越深,基础越扎实!阶段1、深入多线程阶段2、深入多线程设计模式阶段3、深入juc源码解析阶段4、深入jdk其余源码解析......
  • 【Spring Security OAuth2】- 【使用Spring MVC开发RESTful API】 使用切片拦截rest服
    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬学习必须往深处挖,挖的越深,基础越扎实!阶段1、深入多线程阶段2、深入多线程设计模式阶段3、深入juc源码解析阶段4、深入jdk其余源码解析......
  • LeetCode 1371. Find the Longest Substring Containing Vowels in Even Counts
    原题链接在这里:https://leetcode.com/problems/find-the-longest-substring-containing-vowels-in-even-counts/description/题目:Giventhestring s,returnthesizeofthelongestsubstringcontainingeachvowelanevennumberoftimes.Thatis,'a','e&......