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

news2024/10/9 0:00:45

本文目录

  • 前言
  • 1、RaycastVehicle
    • 1.1 概念
    • 1.2 核心特性
    • 1.3 应用场景
  • 2、前置代码准备
  • 3、RaycastVehicle的使用
    • 3.1 代码
    • 3.2 效果
  • 4、监听施加力
    • 4.1 代码
    • 4.2 效果

前言

在三维物理引擎的世界里,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 效果

请添加图片描述

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

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

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

相关文章

小红书推广7种策略让你在竞争中脱颖而出-华媒舍

1. 小红书&#xff08;Redbook&#xff09; 小红书是中国一家以时尚生活为主题的社交电商平台&#xff0c;为用户提供购物、分享、评论和推广的综合服务。用户可以在平台上发布和浏览心得、评价和购买链接。随着用户数量的不断增加&#xff0c;如何在竞争中脱颖而出&#xff0…

远程控制软件推荐:亲测好用!

无论是在家办公、技术支持还是远程协助家人&#xff0c;一个好的远程控制工具都能让我们的工作更加高效。下面&#xff0c;我将分享我对几款流行的远程控制软件的个人体验&#xff0c;并给出我的推荐。 向日葵远程控制 直达链接&#xff1a;down.oray.com 向日葵远程控制是…

《重生到现代之从零开始的C语言生活》—— 联合体和枚举

联合体 像结构体一样&#xff0c;联合体也是由一个或多个成员构成 但是只会给最大的成员分配内存&#xff0c;联合体的特点就是所有成员共用一块内存空间&#xff0c;所以也叫共同体 由于所有的成员共用一块内存空间&#xff0c;所以如果给其中的一个成员赋值的话&#xff0…

外贸财务管理必备,6款热门软件优势对比

外贸企业的财务管理面临着多币种结算、汇率波动、跨境支付等复杂问题。本文将盘点Zoho Books、KashFlow、Sage Intacct等六款热门的外贸财务软件&#xff0c;并探讨它们各自的优势与特点&#xff0c;以帮助外贸企业做出明智的选择。 一、Zoho Books Zoho Books是一款面向中小企…

集合框架05:List接口使用、List实现类、ArrayList使用

视频链接&#xff1a;13.11 ArrayList使用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?p11&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.List接口使用代码举例 package com.yundait.Demo01;import java.util.ArrayList; import java.util.List;pu…

DAMA数据管理知识体系(第8章 数据集成和互操作)

课本内容 8.1 引言 概要 数据集成和互操作&#xff08;DII&#xff09;描述了数据在不同数据存储、应用程序 和组织这三者内部和之间进行移动和整合的相关过程数据集成和互操作是新兴大数据管理领域的核心业务驱动因素 数据集成和互操作的主要目的是为了对数据移动进行有效的管…

open3D release版配置及简单使用

open3D release版配置及简单使用 0 引言1 open3d库文件获取及配置到VS1.1 open3d库文件获取1.2 open3d库配置到VS 2 测试open3d库3 结语 0 引言 &#x1f4bb;&#x1f4bb;AI一下&#x1f4bb;&#x1f4bb; Open3D是一个用于处理3D数据的开源库。它提供了一系列算法和工具&a…

职场中的人情世故,你懂了多少?

职场如战场&#xff0c;稍有不慎&#xff0c;满盘皆输。 职场如江湖&#xff0c;不是打打杀杀&#xff0c;而是人情世故。 成年人的世界里没有“容易”二字&#xff0c;我们也需要懂得哪些人情世故和。 职场上的各种光怪陆离现象&#xff0c;有很多职场人吐槽&#xff1a;“…

云岚到家,使用Elasticsearch实现服务的搜索功能,使用Canal+MQ完成服务信息与ES索引同步。MQ

为什么使用elasticsearch?数据很多么&#xff1f; 项目使用Elasticsearch是实现了门户上对服务的搜索。 平台上的服务数据是并不是很多&#xff0c;全国所有区域下的服务信息加一起几千条&#xff0c;之所以使用Elasticsearch是因为&#xff1a; 1、公司架构师在系统架构时…

九、5 USART串口数据包

数据包作用&#xff1a;把一个个单独的数据给打包起来&#xff0c;将同一批的数据进行打包和分割&#xff0c;方便接收方进行识别&#xff0c;方便我们进行多字节的数据通信。 1、串口收发HEX数据包 &#xff08;1&#xff09;数据包的格式是个人规定的&#xff0c;如以FF为包…

dotnet7==windows ZIP方式安装和web demo和打包

下载ZIP Download .NET 7.0 (Linux, macOS, and Windows) 解压 创建项目 mkdir MyWebApp cd MyWebApp "C:\Users\90816\Downloads\dotnet-sdk-7.0.317-win-x64\dotnet.exe" new webapp -n MyWebApp 运行项目 "C:\Users\90816\Downloads\dotnet-sdk-7.0.317-…

MySQL9的3个新特性

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) 本文讲解MySQL9的3个新特性&…

出国读研,是否有必要选择与自己本科专业相关的研究方向?

出国读研时&#xff0c;选择什么专业&#xff0c;是否选择与自己本科专业相关的方向&#xff0c;是很多同学会考虑的问题。事实上这个问题没有标准答案&#xff0c;取决于个人的职业目标、兴趣及市场需求等多方面因素。今天我们就这一问题展开讨论&#xff0c;希望能给即将出国…

【渗透测试】-OAuth授权框架-burp

文章目录 1.Lab: Authentication bypass via OAuth implicit flow  2.Lab: Forced OAuth profile linking 1.Lab: Authentication bypass via OAuth implicit flow 通过 Burp 代理流量时&#xff0c;单击“我的帐户”并完成 OAuth 登录过程。之后&#xff0c;您将被重定向回博…

信息安全工程师(39)防火墙防御体系结构类型

前言 防火墙防御体系结构类型多样化&#xff0c;每种类型都针对不同的安全需求和应用场景&#xff0c;提供不同层次的保护。 一、传统防火墙系统 包过滤防火墙 原理&#xff1a;通过检查进出网络数据包的头信息&#xff08;如源IP地址、目的IP地址、源端口、目的端口和协议等&a…

数据结构-4.4.朴素模式匹配算法

一.专业术语&#xff1a; 注&#xff1a;子串和模式串有区别。 二.朴素模式匹配算法&#xff1a; 思路&#xff1a;在主串中找出所有与模式串长度相等的子串&#xff0c;与模式串进行比较&#xff0c;如果找到了&#xff0c;返回子串第一个字符在主串的位置 1.使用字符串的基本…

大龄焦虑?35岁码农逆袭之路:拥抱大模型时代,焕发职业生涯新活力!

前言 其实我很早就对大龄程序员这个话题感到焦虑&#xff0c;担心自己35岁之后会面临失业&#xff0c;有时和亲戚朋友聊天时&#xff0c;也会经常拿这个出来调侃。现在身边已经有很多35岁左右的同事&#xff0c;自己过两年也会步入35岁的行列&#xff0c;反倒多了一份淡定和从…

【C++ 11】for 基于范围的循环

文章目录 【 1. 基本用法 】【 2. for 新格式的应用 】2.1 for 遍历字符串2.2 for 遍历列表2.3 for 遍历的同时修改元素 问题背景 C 11标准之前&#xff08;C 98/03 标准&#xff09;&#xff0c;如果要用 for 循环语句遍历一个数组或者容器&#xff0c;只能套用如下结构&#…

AtCoder Beginner Contest 373

D - Hidden Weights 题目&#xff1a; 思路&#xff1a; 代码&#xff1a; #include <bits/stdc.h> #define fi first; #define se second;using namespace std;typedef long long LL; typedef pair<int,int> PII;const int N2e510; const LL lnf0x3f3f3f3f3f3f3…

【JavaEE】【多线程】Thread类讲解

目录 Thread构造方法Thread 的常见属性创建一个线程获取当前线程引用终止一个线程使用标志位使用自带的标志位 等待一个线程线程休眠线程状态线程安全线程不安全原因总结解决由先前线程不安全问题例子 Thread构造方法 方法说明Thread()创建线程对象Thread(Runnable target)使用…