【threejs开发随笔】three.js基于八叉树的碰撞检测
0.前言
相信大家在看threejs官网案例的时候会觉得里面那个fps的小例子碰撞检测感觉很丝滑,还有一定的类似物理的效果,不像之前简单的利用射线进行碰撞检测顶到墙之后劈里啪啦卡顿。
之前一个大哥吧这个案例简单解析了一下,也介绍了一下八叉树的理论知识
three.js案例解析之游戏帧碰撞检测https://blog.csdn.net/web22050702/article/details/125301514这里就不赘述八叉树的理论和这个案例的解析了,那这儿就跟据这个案例搞一个碰撞检测的控制器CollisionController,方便大家直接用。下面的连接是本文的完整代码,需要js版本请复制代码到ts官网playground中根据自己需要的js版本自行编译。
octreeCollision.ts https://gitee.com/Susiia/threejs-standard-framework/blob/master/src/utils/octreeCollision.ts
1.先说用法
先引入
import { CollisionController } from './octreeCollision'
然后使用,格式如下
new CollisionController(capsuleParam: iCapsule, camera: PerspectiveCamera, collisionGroup: Object3D<Event>, canvas: HTMLCanvasElement): CollisionController
说人话就是new 一个CollisionController实例,传入相机体积参数(同Capsule)、需要控制的相机(PerspectiveCamera)、需要进行碰撞检测的模型(Object3D)、渲染画布(canvas)
// 控制器private controls () {this.controls = new CollisionController({start: new Vector3(0, 0.35, 0),end: new Vector3(0, 1, 0),radius: 0.35},this.camera,this.scene.children[this.scene.children.length - 1],this.renderer.domElement)this.controls.update()}
然后在渲染循环中对这个控制器进行更新
// 渲染循环private renderLoop () {this.renderer.render(this.scene, this.camera)this.controls?.update()}
2.再说内容
如何创建一个threejs项目这里就不赘述,我这儿有个vue3+ts+threejs的项目模板,有需要的话可以fork一下,里面后续也会更新一些实用的小工具
threejs-standard-framework: vue框架threejs标准模板 (gitee.com)https://gitee.com/Susiia/threejs-standard-framework新建octreeCollision.ts,首先我们需要引入的东西有
import { Clock, Object3D, PerspectiveCamera, Vector3 } from 'three'
import { Octree } from 'three/examples/jsm/math/Octree.js'
import { Capsule } from 'three/examples/jsm/math/Capsule.js'
创建构造函数需要的接口(碰撞体积参数类型)
interface iCapsule{start:Vector3,end:Vector3,radius:number
}
创建CollisionController类
class CollisionController {constructor (capsuleParam:iCapsule, camera:PerspectiveCamera, collisionGroup:Object3D, canvas:HTMLCanvasElement) {}
}
声明私有变量
private clock:Clock // 时钟private camera:PerspectiveCamera // 相机private collisionGroup:Object3D// 需要计算碰撞检测的组private canvas:HTMLCanvasElementprivate GRAVITY:number // 重力private STEPS_PER_FRAME:number // 每秒步数private worldOctree:Octree// 环境八叉树private _playerCollider!: Capsule // 玩家碰撞体积(公开方法,setter和getter在下面)private playerOnFloor:boolean;// 玩家是不是在地面上private playerVelocity:Vector3;// 玩家速度private playerDirection:Vector3;// 玩家方向private eventStates = { // 事件状态KeyW: false,KeyA: false,KeyS: false,KeyD: false,Space: false,mouseDown: false}
在构造函数中对声明的变量进行初始化
constructor (capsuleParam:iCapsule, camera:PerspectiveCamera, collisionGroup:Object3D, canvas:HTMLCanvasElement) {this.clock = new Clock()this.GRAVITY = 30this.STEPS_PER_FRAME = 5this.worldOctree = new Octree()this.playerOnFloor = falsethis.playerCollider = new Capsule(capsuleParam.start, capsuleParam.end, capsuleParam.radius)this.playerVelocity = new Vector3()this.playerDirection = new Vector3()this.camera = camerathis.camera.rotation.order = 'YXZ' // 相机旋转方式需要调整一下this.collisionGroup = collisionGroupthis.canvas = canvas// 将需要进行碰撞检测的Object3D加入worldOctreethis.worldOctree.fromGraphNode(this.collisionGroup) this.initEventListener() // 开启事件侦听}
事件侦听方法:
// 初始化按键侦听private initEventListener () {// 键盘按下document.addEventListener('keydown', (event) => {switch (event.code) {case 'KeyW':this.eventStates[event.code] = truebreakcase 'KeyA':this.eventStates[event.code] = truebreakcase 'KeyS':this.eventStates[event.code] = truebreakcase 'KeyD':this.eventStates[event.code] = truebreakcase 'Space':this.eventStates[event.code] = truebreakdefault:break}})// 键盘抬起document.addEventListener('keyup', (event) => {switch (event.code) {case 'KeyW':this.eventStates[event.code] = falsebreakcase 'KeyA':this.eventStates[event.code] = falsebreakcase 'KeyS':this.eventStates[event.code] = falsebreakcase 'KeyD':this.eventStates[event.code] = falsebreakcase 'Space':this.eventStates[event.code] = falsebreakdefault:break}})// 鼠标按下this.canvas.addEventListener('mousedown', () => {// this.canvas.requestPointerLock()this.eventStates.mouseDown = true})// 鼠标抬起this.canvas.addEventListener('mouseup', () => {// NOTE:exitPointerLock在ts中会报错, ts3.1一些浏览器厂商特定的类型从lib.d.ts中被移除,其中包括退出PointerLock状态的方法exitPointerLock(),https://www.lanqiao.cn/library/TypeScript/breaking-changes/typescript-3.1// this.canvas.exitPointerLock()this.eventStates.mouseDown = false})// 鼠标移动this.canvas.addEventListener('mousemove', (event) => {if (this.eventStates.mouseDown) {this.camera.rotation.y -= event.movementX / 500this.camera.rotation.x -= event.movementY / 500}})}
创建一个控制器方法
// 控制器private controls (deltaTime:number) {// gives a bit of air controlconst speedDelta = deltaTime * (this.playerOnFloor ? 25 : 8)if (this.eventStates.KeyW) {this.playerVelocity.add(this.getForwardVector().multiplyScalar(speedDelta))}if (this.eventStates.KeyS) {this.playerVelocity.add(this.getForwardVector().multiplyScalar(-speedDelta))}if (this.eventStates.KeyA) {this.playerVelocity.add(this.getSideVector().multiplyScalar(-speedDelta))}if (this.eventStates.KeyD) {this.playerVelocity.add(this.getSideVector().multiplyScalar(speedDelta))}if (this.playerOnFloor) {if (this.eventStates.Space) {this.playerVelocity.y = 15}}}
创建一个public方法出去,用来更新控制器
// 更新控制器public update () {const deltaTime = Math.min(0.05, this.clock.getDelta()) / this.STEPS_PER_FRAMEfor (let i = 0; i < this.STEPS_PER_FRAME; i++) {this.controls(deltaTime)this.updatePlayer(deltaTime)}}
创建一个用来更新玩家的方法
// 更新玩家private updatePlayer (deltaTime:number) {let damping = Math.exp(-4 * deltaTime) - 1if (!this.playerOnFloor) {this.playerVelocity.y -= this.GRAVITY * deltaTimedamping *= 0.1}this.playerVelocity.addScaledVector(this.playerVelocity, damping)const deltaPosition = this.playerVelocity.clone().multiplyScalar(deltaTime)this.playerCollider.translate(deltaPosition)this.playerCollisions()this.camera.position.copy(this.playerCollider.end)}
创建获得当前相机前后和左右方向的方法
// 获得前后方向private getForwardVector () {this.camera.getWorldDirection(this.playerDirection)this.playerDirection.y = 0this.playerDirection.normalize()return this.playerDirection}// 获得左右方向private getSideVector () {this.camera.getWorldDirection(this.playerDirection)this.playerDirection.y = 0this.playerDirection.normalize()this.playerDirection.cross(this.camera.up)return this.playerDirection}
创建碰撞检测方法
// 玩家碰撞private playerCollisions () {const result = this.worldOctree.capsuleIntersect(this.playerCollider)this.playerOnFloor = falseif (result) {this.playerOnFloor = result.normal.y > 0if (!this.playerOnFloor) {this.playerVelocity.addScaledVector(result.normal, -result.normal.dot(this.playerVelocity))}this.playerCollider.translate(result.normal.multiplyScalar(result.depth))}}
到此我们这个控制器就写好了。
【threejs开发随笔】three.js基于八叉树的碰撞检测相关推荐
- 基于弹性云服务器的昇腾AI应用开发随笔【与云原生的故事】
什么是弹性云服务器? 先来说说云服务器(Elastic Cloud Server),云服务器是具有完整硬件.操作系统.网络功能,并且运行在一个完全隔离环境中的计算机系统.云服务器具有弹性.按需获取的特 ...
- 软件开发随笔系列一——分布式架构实现
软件开发随笔系列一--分布式架构实现 文章目录 软件开发随笔系列一--分布式架构实现 理论基础 分布式架构的实现 内核框架 应用开发 基础设施 服务接入 监控 日志监控 调用链监控 度量指标监控 健康 ...
- 开发随笔——NOT IN vs NOT EXISTS
原文: 开发随笔--NOT IN vs NOT EXISTS 原文出处: http://blog.csdn.net/dba_huangzj/article/details/31374037 转载请引 ...
- Vue.js – 基于 MVVM 实现交互式的 Web 界面
Vue.js 是用于构建交互式的 Web 界面的库.它提供了 MVVM 数据绑定和一个可组合的组件系统,具有简单.灵活的 API.从技术上讲, Vue.js 集中在 MVVM 模式上的视图模型层,并 ...
- SAP UI5 应用开发教程之六十七 - 基于 OData V4 的 SAP UI5 List-Detail(列表-明细)布局的实现方式试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...
- SAP UI5 应用开发教程之六十三 - 基于 OData V4 的本地 Mock Server 实现的深入介绍试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...
- Arctext.js - 基于 CSS3 jQuery 的文本弯曲效果
Arctext.js 是基于 Lettering.js 的文本旋转插件,根据设置的旋转半径准确计算每个字母的旋转弧度并均匀分布.虽然 CSS3 也能够实现字符旋转效果,但是要让安排每个字母都沿着弯曲路 ...
- 向别人网页注入js_区块链研究实验室 | Web3 .js基于以太坊的Javascript API
web3.js是一个库集合,你可以使用HTTP或IPC连接本地或远程以太它节点进行交互. web3的JavaScript库能够与以太坊区块链交互. 它可以检索用户帐户,发送交易,与智能合约交互等. V ...
- OMS开发随笔之开发和部署要点
OMS开发随笔之概述 上一篇中,介绍了OMS的特点和基本的开发概述,这篇详细谈谈具体在实践中遇到的问题及解决方案.本文之涉及到普通短信的开发,不包括彩信. OMS的开发要点 总的来说开发oms没有 ...
最新文章
- PNAS:张航课题组揭示人类为何“扭曲”概率信息
- 第十六届全国大学生智能汽车竞赛 讯飞智慧餐厅 全国总决赛竞赛规则
- Python程序设计题解【蓝桥杯官网题库】 DAY7-基础练习
- python3.6.4安装教程-centos7中安装python3.6.4的教程
- ssential Diagram for Windows FormsC#/winForm类似visio的拓扑图节点连线控件免费下载
- OpenStack vlan教程 (操作篇)
- 3部世界顶级宇宙纪录片,献给对宇宙万物充满好奇的大人孩子~
- 多个服务器数据互通_5月23日部分服务器数据互通公告!
- 苹果付费app共享公众号_娄底共享云店铺公众号
- 能让你「情商暴涨」的6个聊天小技巧
- 惠普服务器查询ilo信息,HP服务器在线配置ilo地址
- 真懂?Retrofit完整剖析
- Sketch 插件开发指南
- 【无标题】基于javaweb、java的管理系统毕设计与实现怎样选题思路分享
- scrapy html页面加载未完成,Scrapy与scrapy-splash框架快速加载js页面
- SetupWizard调试技巧
- Netty channelRegistered\ChannelActive---源码分析
- 融云开发者沙龙(济南站)活动精彩回顾
- Error: Entrypoint isnt within the current project
- 欧盟ROHS有害物质测试标准