@vladkrutenyuk/cannon-es-debugger-pro
Version:
This is improved pro debugger for 'cannon-es' with 'three' to visualize all bodies and its shapes of physics world.
321 lines (227 loc) β’ 10 kB
Markdown
# @vladkrutenyuk/cannon-es-debugger-pro
This is improved pro debugger for [cannon-es](https://github.com/pmndrs/cannon-es) with [three](https://github.com/pmndrs/cannon-es) to visualize all bodies and its shapes of physics world.
It was based on [cannon-es-debugger](https://www.npmjs.com/package/cannon-es-debugger). I wasn't completely satisfied with it so I decided to create a **new fixed and improved version** based on it.

- New pretty and clean appearance to make scene more readable.
- Reworked debug graphics objects and its geoemtries.
- New `offset` parameter to prevent overlapping instead of `scale`.
- True memory management
- Prevent memory leaking and disposing custom geometries created for complex shapes.
- Ability to destroy debugger instance if its no longer used.
- New methods - `clean` and `destroy`.
- Better types and implementation
- Class instead of function
- Events instead of callback args.
- Parameters changing after initialization - `setColor`, `setOffset`, `setVisible`.
> Read section **"Why use this instead of existed old one?"** below after 'Usage' section.
## Installation π€
```bash
pnpm add @vladkrutenyuk/cannon-es-debugger-pro
# or via 'npm i ...', 'yarn add ...' and so on
```
β οΈ Make sure you also have `three` and `cannon-es` as dependencies.
```bash
pnpm add three cannon-es
# or via 'npm i ...', 'yarn add ...' and so on
```
## Usage π§
#### Initialization
```ts
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
import { CannonEsDebuggerPro } from '@vladkrutenyuk/cannon-es-debugger-pro'
const world = new CANNON.World()
const scene = new THREE.Scene()
const root = new THREE.Group() // or any another THREE.Object3D like
scene.add(root)
// optional, default is 0x00ff00
const color = 0xe60c0c // or 'rgb(228, 14, 88)', '#e60c0c', 'red'
// optional, defailt is 0.005
const offset = 0.009
const cannonDebugger = new CannonEsDebuggerPro(root, world, color, offset)
```
#### Update
The `update()` method needs to be called after `cannon` physics world's step and before `three.js` render to update its state.
```ts
const animate = () => {
requestAnimationFrame(animate)
world.step(timeStep) // Update cannon-es physics
cannonDebugger.update() // Update the CannonEsDebuggerPro
renderer.render(scene, camera) // Render the three.js scene
}
animate()
```
# Why use this instead of existed old one? What improvements? π€©π
> _"...existed old one" means [cannon-es-debugger](https://www.npmjs.com/package/cannon-es-debugger) package._
## 1. Pretty and clean appearance
Make scene where a lot of objects and bodies readable and clean again.\
See comparison image in the top of the page.
### Reworked debug graphics objects and its geoemtreis
- **Sphere**\
Custom shematic visualization of sphere shape instead of mesh with default icosahedron geometry.\
Geometry is built for `THREE.LineSegments` way.\
Thus, now it's combination of 3 perpendicular to each other edge-circles for each dimension. Looks like sphere colliders in Unity.
- **Box**\
Only quad faces without diagonales of trianguilation (grid-like style).\
Geometry is built for `THREE.LineSegments` way.
- **ConvexPolyhedron**\
This shape debug graphic geometry is built for `THREE.LineSegments` way. Thus, `ConvexPolyhedron` is displayed as it is, even if its faces contain more then 3 verticies (polygonal), for example `Cylinder` shape.
- **Plane**\
Only quad faces without diagonales of trianguilation (grid-like style). \
Geometry is built for `THREE.LineSegments` way.\
Also it has bigger size (100x100 instead of 10x10) because physical plane is infinite.
- **Heightfield**\
Only quad faces without diagonales of trianguilation (grid-like style).\
Geometry is built for `THREE.LineSegments` way.
### Geometry offset
There is new parameter `offset` instead of old `scale`.\
It's float value in meters to push up geometries vertices along its normals of debug graphics objects to prevent overlapping with source scene objects.
Recommended range of hundredths `~0.01` or thousandths `~0.001`.
Default is `0.005`.
The method `setOffset()` is used to change it.
## 2. Solved memory issues
Original `cannon-es-debugger` doesn't clear scene from created meshes and doesn't dispose created geometry for complex shape after its removing.
This version does **disposing** for complex geometry when its body shape removed. Also it happens on new `clear()`, `destroy()` methods (read below in the next point).
## 3. Destroying, disposing and clearing
The method `destroy()` is called to remove all created debug 3d objects and dispose all created geometries for complex shapes and shared materials of this instance.
After calling `destroy()` the method `update()` won't work anymore and property (read-only) `isDestroyed` will become `true`.
```ts
cannonDebugger.update() // update happened
cannonDebugger.isDestroyed // > false
cannonDebugger.destroy() // remove all debug 3d objects and dispose its geometries, materials
cannonDebugger.isDestroyed // > true
cannonDebugger.update() // and now it does nothing
```
If you need just clear scene from debugs but you still need this cannon debugger instance then you are able to use `clear()` method. It does the same but shared materials won't be disposed and the `update()` method will be still working.
But oftenly, it's necessary to hide and show back debugs. In this case the better way is to use the `setVisible()` method. It doesn't remove and dispose anything.
```ts
// hide
cannonDebugger.setVisible(false)
// show back
cannonDebugger.setVisible(true)
```
## 4. Events
`CannonEsDebuggerPro` class extends `THREE.EventDispatcher` and uses it to dispatch `update`, `init` events. Old version's callbacks (`onInit`, `onUpdate`) are not longer used anymore.
**Comparing:**
π in `cannon-es-debugger`
```typescript
cannonDebugger = new CannonDebugger(scene, world, {
onInit: (body, obj3d, shape) => {
console.log('inited')
},
onUpdate: (body, obj3d, shape) => {
console.log('updated')
}
})
```
ππ now in `@vladkrutenyuk/cannon-es-debugger-pro`
```typescript
cannonDebugger = new CannonEsDebuggerPro(root, world)
cannonDebugger.addEventListener('init', (event) => {
const { body, obj3d, shape } = event
console.log(`inited obj-${obj3d.id} body-${body.id} shape-${shape.id}`)
})
cannonDebugger.addEventListener('update', (event) => {
const { body, obj3d, shape } = event
console.log(`updated obj-${obj3d.id} body-${body.id} shape-${shape.id}`)
})
```
## 5. Typing and implementation
**Changes:**
- This version is implemented via `class` and instance just has its type `CannonEsDebuggerPro` instead of old implemention via `function` where instance can be typed only as `ReturnType<...>`.
- `THREE.Object3D` root instead of `THREE.Scene` scene argument.
**Comparing:**
π in `cannon-es-debugger`
```typescript
// implementation
function CannonDebugger(scene: THREE.Scene, ...) { ... }
// thus, we need this to create typed variable:
cannonDebugger: ReturnType<typeof CannonDebugger>
this.cannonDebugger = new CannonDebugger(...)
// also, it provides only THREE.Scene as root:
const root = new THREE.Group()
new CannonDebugger(root, ...) //! TypeError
```
ππ now in `@vladkrutenyuk/cannon-es-debugger-pro`
```typescript
// implementation
class CannonEsDebuggerPro {
constructor(root: THREE.Object3D, ...) { ... }
...
}
// thus
class AnyYourClass {
cannonDebuggerPro: CannonEsDebuggerPro
constructor() {
// able to use any THREE.Object3D
const root = new THREE.Group()
this.cannonDebuggerPro = new CannonEsDebuggerPro(root, ...)
}
}
```
## Types π§βπ«
**Kind of API.**
```ts
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
// Helper types for THREE.EventDispatcher
export type CannonEsDebuggerProEventMap = {
init: {
body: CANNON.Body
obj3d: THREE.Object3D
shape: CANNON.Shape
}
update: {
body: CANNON.Body
obj3d: THREE.Object3D
shape: CANNON.Shape
}
}
type CannonEsDebuggerProEventType = Extract<keyof CannonEsDebuggerProEventMap, string>
export class CannonEsDebuggerPro extends THREE.EventDispatcher<CannonEsDebuggerProEventMap> {
constructor(
root: THREE.Object3D,
world: CANNON.World,
color: THREE.ColorRepresentation = 0x00ff00,
offset: number = 0.005
): void
// Accessors
get color(): THREE.ColorRepresentation
get offset(): number
get isVisible(): boolean
get isDestroyed(): boolean
// Main methods
update(): void
clear(): void
destroy(): void
// Customization methods
setVisible(isVisible: boolean): this
setColor(color: THREE.ColorRepresentation): this
setOffset(offset: number): this
// THREE.EventDispatcher methods
addEventListener<T extends CannonEsDebuggerProEventType>(
type: T,
listener: THREE.EventListener<CannonEsDebuggerProEventMap[T], T, this>,
): void
hasEventListener<T extends CannonEsDebuggerProEventType>(
type: T,
listener: THREE.EventListener<CannonEsDebuggerProEventMap[T], T, this>,
): boolean
removeEventListener<T extends CannonEsDebuggerProEventType>(
type: T,
listener: THREE.EventListener<CannonEsDebuggerProEventMap[T], T, this>,
): void
}
```
## Donate π₯Ίπ
π ERC-20 wallet (USDC / USDT / ETH):\
`0xF348AB28dB048CbFF18095b428ac9Da4f1A7a90e`
## Social media / links ππ
- [Telegram (@vladkrutenyuk)](https://t.me/vladkrutenyuk)
- [Instagram (@vlad_krutenyuk)](https://instagram.com/vlad_krutenyuk)
- [Twitter/X (@vladkrutenyuk)](https://x.com/vladkrutenyuk)
- [Twitch (vladdyusha)](https://twitch.tv/vladdyusha)
- [kvy-4Β° (My funny telegram channel)](https://t.me/kvvy4)
- [https://kvy.world](https://kvy.world)
---
_meow :3_