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基于八叉树的碰撞检测相关推荐

  1. 基于弹性云服务器的昇腾AI应用开发随笔【与云原生的故事】

    什么是弹性云服务器? 先来说说云服务器(Elastic Cloud Server),云服务器是具有完整硬件.操作系统.网络功能,并且运行在一个完全隔离环境中的计算机系统.云服务器具有弹性.按需获取的特 ...

  2. 软件开发随笔系列一——分布式架构实现

    软件开发随笔系列一--分布式架构实现 文章目录 软件开发随笔系列一--分布式架构实现 理论基础 分布式架构的实现 内核框架 应用开发 基础设施 服务接入 监控 日志监控 调用链监控 度量指标监控 健康 ...

  3. 开发随笔——NOT IN vs NOT EXISTS

    原文: 开发随笔--NOT IN vs NOT EXISTS 原文出处: http://blog.csdn.net/dba_huangzj/article/details/31374037  转载请引 ...

  4. Vue.js – 基于 MVVM 实现交互式的 Web 界面

    Vue.js 是用于构建交互式的 Web  界面的库.它提供了 MVVM 数据绑定和一个可组合的组件系统,具有简单.灵活的 API.从技术上讲, Vue.js 集中在 MVVM 模式上的视图模型层,并 ...

  5. SAP UI5 应用开发教程之六十七 - 基于 OData V4 的 SAP UI5 List-Detail(列表-明细)布局的实现方式试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  6. SAP UI5 应用开发教程之六十三 - 基于 OData V4 的本地 Mock Server 实现的深入介绍试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  7. Arctext.js - 基于 CSS3 jQuery 的文本弯曲效果

    Arctext.js 是基于 Lettering.js 的文本旋转插件,根据设置的旋转半径准确计算每个字母的旋转弧度并均匀分布.虽然 CSS3 也能够实现字符旋转效果,但是要让安排每个字母都沿着弯曲路 ...

  8. 向别人网页注入js_区块链研究实验室 | Web3 .js基于以太坊的Javascript API

    web3.js是一个库集合,你可以使用HTTP或IPC连接本地或远程以太它节点进行交互. web3的JavaScript库能够与以太坊区块链交互. 它可以检索用户帐户,发送交易,与智能合约交互等. V ...

  9. OMS开发随笔之开发和部署要点

      OMS开发随笔之概述 上一篇中,介绍了OMS的特点和基本的开发概述,这篇详细谈谈具体在实践中遇到的问题及解决方案.本文之涉及到普通短信的开发,不包括彩信. OMS的开发要点 总的来说开发oms没有 ...

最新文章

  1. PNAS:张航课题组揭示人类为何“扭曲”概率信息
  2. 第十六届全国大学生智能汽车竞赛 讯飞智慧餐厅 全国总决赛竞赛规则
  3. Python程序设计题解【蓝桥杯官网题库】 DAY7-基础练习
  4. python3.6.4安装教程-centos7中安装python3.6.4的教程
  5. ssential Diagram for Windows FormsC#/winForm类似visio的拓扑图节点连线控件免费下载
  6. OpenStack vlan教程 (操作篇)
  7. 3部世界顶级宇宙纪录片,献给对宇宙万物充满好奇的大人孩子~
  8. 多个服务器数据互通_5月23日部分服务器数据互通公告!
  9. 苹果付费app共享公众号_娄底共享云店铺公众号
  10. 能让你「情商暴涨」的6个聊天小技巧
  11. 惠普服务器查询ilo信息,HP服务器在线配置ilo地址
  12. 真懂?Retrofit完整剖析
  13. Sketch 插件开发指南
  14. 【无标题】基于javaweb、java的管理系统毕设计与实现怎样选题思路分享
  15. scrapy html页面加载未完成,Scrapy与scrapy-splash框架快速加载js页面
  16. SetupWizard调试技巧
  17. Netty channelRegistered\ChannelActive---源码分析
  18. 融云开发者沙龙(济南站)活动精彩回顾
  19. Error: Entrypoint isnt within the current project
  20. 欧盟ROHS有害物质测试标准

热门文章

  1. EXCEL列乱序后内容重新对应
  2. html 绘制篮球,7篮球运动.html
  3. 深度学习 图像识别 四
  4. 每天可以一看的哲理句子
  5. python面向对象练习——飞机大战
  6. 大学计算机实验报告虚拟机,1虚拟机安装和使用实验报告书
  7. linux 中的rime 输入法 自定义 新世纪五笔输入法
  8. 优雅地从浏览器打开本地应用
  9. eclipes 快捷键操作:
  10. 【HTML5】Web前端——网页实用技巧1:将一个方形图片,变成圆形(利用CSS属性)