我們用three.js可以繪制出各種酷炫的畫面,但是當我們想要一個更加真實的物理效果的話,這個時候我們就需要一個物理的庫,接下來我們就講解一下今天要學習的canon,它可以給我們提供一個更加真實的物理效果,像物體的張力、摩擦力、拉伸、反彈等等各種真實的物理效果。該庫都能夠有一個非常好的模擬。
PS:目前博主在一家互聯網公司工作,該公司的編碼風格是vue+tsx,所以接下來的項目以該編碼風格進行舉例,詳細了解參考我之前的文章:地址?。
目錄
canon基本使用
基礎碰撞使用
材質與摩擦系數設置
彈性與接觸材質設置
碰撞與碰撞組
canon基本使用
Cannon 是一種輕量級的 JavaScript 3D 物理引擎,用于實現虛擬世界中的物理模擬和交互。它提供了一套強大的功能,能夠處理剛體碰撞、力學模擬、約束系統等物理效果,使開發者能夠在 Web 應用程序和游戲中實現逼真的物理行為。
Cannon的官網:地址?,提供了一系列關于物理運動在three世界的實現,實現案例?的效果非常直觀,展示了物理運動的魅力,如下:
接下來我們在three.js的vue項目中使用Cannon,終端執行如下命令安裝,具體參考:官網
npm i cannon-es
接下來我們通過tsx風格語法撰寫three基礎項目實現:
import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'
import { div } from "three/examples/jsm/nodes/Nodes.js";export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 3// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 渲染let clock = new THREE.Clock()const animate = () => {let delta = clock.getDelta()controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})
接下來我們需要給場景添加一些物體,如下:
// 創建一個物理球體,半徑為0.5
const sphereShape = new CANNON.Sphere(0.5)
// 創建一個剛體
const sphereBody = new CANNON.Body({mass: 1,shape: sphereShape,position: new CANNON.Vec3(0, 5, 0)
})
// 將剛體添加到物理世界中
world.addBody(sphereBody)
// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個球
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32) // 創建一個幾何體
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }) // 創建一個球體材質
const shpereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial) // 創建一個球體網格
// 將網格添加到3D場景中
scene.add(shpereMesh)
打開控制臺,可以看到我們的球體已經顯示出來了:
接下來我們要把物理世界的球體給到我們three渲染出來的球體,讓兩者開始關聯起來。在每一幀中,根據物理引擎模擬的結果來更新 Three.js 場景中球體網格的位置和旋轉狀態,從而實現了基于物理引擎的球體模擬效果,如下:
得到的結果如下:
基礎碰撞使用
上面我們實現了一個物體的自由下落,接下來我們實現物體與平面的碰撞效果。如下添加平面:
// 創建一個物理世界的平面
const planeShape = new CANNON.Plane()
// 創建一個剛體
const planeBody = new CANNON.Body({mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0)
})
// 設置剛體旋轉(設置旋轉X軸)
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
// 將剛體添加到物理世界當中
world.addBody(planeBody)
// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面
const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10
// 創建一個平面材質
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 創建一個平面網格
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
// 旋轉平面90度讓其平鋪
planeMesh.rotation.x = -Math.PI / 2
// 將網格添加到3D場景當中
scene.add(planeMesh)
當然除了我們設置平面質量為0之外,我們也可以設置平面為靜態效果,也不受碰撞影響:
最終得到的效果如下:
那我們讓物理世界和渲染世界的平面傾斜度加上0.1,小球是否會滑落而掉下去呢?測試一下:
得到的效果如下,可見小球是不會掉落下去的,因為物理世界的平面是無限衍生的,即使渲染世界的平面有限,小球仍然會走物理世界的規律,如下:
如果我們想在物理世界有一個有限大的平面的話, 我們可以通過構建一個立方體,然后把立方體壓扁形成一個平面來使用,因為立方體已經有高度了,所以我們也不需要在旋轉90度了,稍微給點傾斜度0.1即可,代碼如下:
得到的效果如下,可見到我們的小球已經實現了掉落的效果:
上面兩標題的案例代碼如下:
import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 3// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 創建一個物理球體,半徑為0.5const sphereShape = new CANNON.Sphere(0.5)// 創建一個剛體const sphereBody = new CANNON.Body({mass: 1,shape: sphereShape,position: new CANNON.Vec3(0, 5, 0)})// 將剛體添加到物理世界中world.addBody(sphereBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個球const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32) // 創建一個幾何體const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }) // 創建一個球體材質const shpereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial) // 創建一個球體網格// 將網格添加到3D場景中scene.add(shpereMesh)// 創建一個物理世界的平面// const planeShape = new CANNON.Plane()const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))// 創建一個剛體const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0),type: CANNON.Body.STATIC // 設置物體為靜態,不受碰撞的影響})// 設置剛體旋轉(設置旋轉X軸)planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)// 將剛體添加到物理世界當中world.addBody(planeBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)// 創建一個平面材質const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })// 創建一個平面網格const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)// 旋轉平面90度讓其平鋪planeMesh.rotation.x = 0.1// 將網格添加到3D場景當中scene.add(planeMesh)// 渲染let clock = new THREE.Clock()const animate = () => {// 獲取了兩次渲染之間的時間差,通常用于控制動畫和物理模擬。let delta = clock.getDelta()// 使用時間差來推進物理世界的模擬world.step(delta)// 更新球體網格的位置和旋轉// 將物理引擎中球體的位置賦值給 Three.js 中球體網格(shpereMesh)的位置,從而將物理模擬的結果更新到可視化場景中。shpereMesh.position.copy(sphereBody.position)// 將物理引擎中球體的旋轉狀態賦值給 Three.js 中球體網格(shpereMesh)的旋轉狀態,確保網格的旋轉與物理模擬一致。shpereMesh.quaternion.copy(sphereBody.quaternion)controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})
材質與摩擦系數設置
cannon的材質可以模擬我們現實生活當中的物理的效果,比如說我們可以設置它的摩擦系數,彈性系數來實現我們這個物體的滑動的有摩擦的效果。借助上面的案例,我們將球體換成立方體,因為要創建多個立方體,這里我們設置一個變量用于存儲
// 創建網格數組
let phyMeshes: any[] = [] // 物理世界
let meshes: any[] = [] // 渲染世界// 創建物理立方體
const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))
// 設置立方體的材質
const boxMaterialCon = new CANNON.Material("boxMaterial")
// 創建一個剛體
const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(0, 15, 0),mass: 1,material: boxMaterialCon
})
// 將剛體添加到物理世界當中
world.addBody(boxBody)
phyMeshes.push(boxBody)
// 創建立方體幾何體
const boxGeometry = new THREE.BoxGeometry(1, 1, 1)
// 創建立方體材質
const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
// 創建立方體網格
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)
// 將網格添加到3D場景當中
scene.add(boxMesh)
meshes.push(boxMesh)
在渲染函數處,我們變量數組來推進物理世界模擬:
最終得到是效果如下:
接下來我們添加第二個物體,將第二個物體的摩擦系數設置為0,第一個物體和平面的摩擦系數設置為0.7,代碼如下:
// 創建網格數組
let phyMeshes: any[] = [] // 物理世界
let meshes: any[] = [] // 渲染世界// 創建物理立方體
const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))
// 設置立方體的材質
const boxMaterialCon = new CANNON.Material("boxMaterial")
boxMaterialCon.friction = 0.7
// 創建一個剛體
const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(0, 15, 0),mass: 1,material: boxMaterialCon
})
// 將剛體添加到物理世界當中
world.addBody(boxBody)
phyMeshes.push(boxBody)
// 創建立方體幾何體
const boxGeometry = new THREE.BoxGeometry(1, 1, 1)
// 創建立方體材質
const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
// 創建立方體網格
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)
// 將網格添加到3D場景當中
scene.add(boxMesh)
meshes.push(boxMesh)// 創建第二個物理立方體(使用第一個物理立方體的內容,材質不同)
const boxSlipperyMaterial = new CANNON.Material("boxSlipperyMaterial")
boxSlipperyMaterial.friction = 0 // 摩擦系數為0
// 創建剛體
const boxBody2 = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(1, 5, 0), // 區別于第一個物體,位置改變一下mass: 1,material: boxSlipperyMaterial
})
// 將剛體添加到物理世界當中
world.addBody(boxBody2)
phyMeshes.push(boxBody2)
// 創建立方體幾何體(使用第一個物體的內容)
const boxMesh2 = new THREE.Mesh(boxGeometry, boxMaterial)
// 將網格添加到3D場景當中
scene.add(boxMesh2)
meshes.push(boxMesh2)// 創建一個物理世界的平面
// const planeShape = new CANNON.Plane()
const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))
// 創建一個剛體
const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0),type: CANNON.Body.STATIC, // 設置物體為靜態,不受碰撞的影響material: boxMaterialCon
})
// 設置剛體旋轉(設置旋轉X軸)
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)
// 將剛體添加到物理世界當中
world.addBody(planeBody)
// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面
// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10
const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)
// 創建一個平面材質
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 創建一個平面網格
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
// 旋轉平面90度讓其平鋪
planeMesh.rotation.x = 0.1
// 將網格添加到3D場景當中
scene.add(planeMesh)
最終得到的效果如下,可以看到我們設置的第二個物體因為很光滑,所以很容易就滑落下去:
彈性與接觸材質設置
上文我們介紹了摩擦效果的操作,接下來我們繼續開始講解物體的彈性操作,我們根據上文的代碼,再創建第三個立方體,然后給該立方體添加彈性系數
// 創建第三個物理立方體(使用第一個物理立方體的內容,材質不同)
const boxBouncyMaterial = new CANNON.Material("boxBouncyMaterial")
boxBouncyMaterial.friction = 0.1
boxBouncyMaterial.restitution = 1 // 設置彈性系數為1
// 創建剛體
const boxBody3 = new CANNON.Body({shape: boxShape,mass: 1,position: new CANNON.Vec3(2, 5, 3),material: boxBouncyMaterial
})
// 將剛體添加到物理世界當中
world.addBody(boxBody3)
phyMeshes.push(boxBody3)
// 創建幾何體(使用第一個立方體的內容以及材質)
const boxMesh3 = new THREE.Mesh(boxGeometry, boxMaterial) // 添加網格
// 將網格添加到3D場景當中
scene.add(boxMesh3)
meshes.push(boxMesh3)
給立方體設置彈性系數之后,如果我們想讓彈性效果奏效的話,我們也需要給平面網格設置相同的彈性系數,因為平面網格使用的材質是第一個立方體的材質,所以我們只要給第一個立方體設置彈性系數即可,如下:
最終得到的效果如下,可以看到設置高度高的物體,從高處下落反彈的效果是很直觀的:
當然我們也沒有必要單獨設置一下立方體和平面的彈性和摩擦系數,我們也可以通過接觸材質的系數設置兩個材質之間的一個彈性和摩擦系數,來實現相應的效果,如下:
// 創建第三個物理立方體(使用第一個物理立方體的內容,材質不同)
const boxBouncyMaterial = new CANNON.Material("boxBouncyMaterial")
// boxBouncyMaterial.friction = 0.1
// boxBouncyMaterial.restitution = 1 // 設置彈性系數為1
// 創建剛體
const boxBody3 = new CANNON.Body({shape: boxShape,mass: 1,position: new CANNON.Vec3(2, 5, 3),material: boxBouncyMaterial
})
// 將剛體添加到物理世界當中
world.addBody(boxBody3)
phyMeshes.push(boxBody3)
// 創建幾何體(使用第一個立方體的內容以及材質)
const boxMesh3 = new THREE.Mesh(boxGeometry, boxMaterial) // 添加網格
// 將網格添加到3D場景當中
scene.add(boxMesh3)
meshes.push(boxMesh3)
// 定義接觸材質
const material3toplane = new CANNON.ContactMaterial(boxMaterialCon,boxBouncyMaterial,{friction: 0,restitution: 1}
)
// 將接觸材質添加到物理世界當中
world.addContactMaterial(material3toplane)
最終呈現的效果依然很明顯:
碰撞與碰撞組
Cannon中的碰撞指的是游戲開發中物體之間的相互作用,通常包括物體之間的碰撞檢測和碰撞響應兩個部分。碰撞檢測用于判斷物體是否發生了碰撞,而碰撞響應則是在發生碰撞時對物體進行相應的處理,比如改變物體的速度、方向等。如下我們設置代碼來實現:
依次創建立方體、球體、圓柱體到場景當中,舉例代碼如下:
接下來我們給創建到場景的立方體添加一個初速度使其運動來碰撞另外兩個物體,如下:
這里給出完整的代碼來給大家進行學習:
import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 8camera.position.y = 5camera.position.x = 2// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 創建網格數組let phyMeshes: any[] = [] // 物理世界let meshes: any[] = [] // 渲染世界// 創建物理立方體const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))// 設置立方體的材質const boxMaterialCon = new CANNON.Material("boxMaterial")boxMaterialCon.friction = 0// 創建一個剛體const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(2, 0.8, 0),mass: 1,material: boxMaterialCon})// 將剛體添加到物理世界當中world.addBody(boxBody)phyMeshes.push(boxBody)// 創建立方體幾何體const boxGeometry = new THREE.BoxGeometry(1, 1, 1)// 創建立方體材質const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })// 創建立方體網格const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)// 將網格添加到3D場景當中scene.add(boxMesh)meshes.push(boxMesh)// 創建物理球const spereShape = new CANNON.Sphere(0.5)// 創建一個剛體const sphereBody = new CANNON.Body({shape: spereShape,position: new CANNON.Vec3(0, 0.8, 0),mass: 1,material: boxMaterialCon})// 將剛體添加到物理世界當中world.addBody(sphereBody)phyMeshes.push(sphereBody)// 創建球的幾何體const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32)// 創建球的材質const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff })// 創建球網格const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial)// 將網格添加到3D場景當中scene.add(sphereMesh)meshes.push(sphereMesh)// 創建物理圓柱體const cylinderShape = new CANNON.Cylinder(0.5, 0.5, 1, 32)// 創建一個剛體const cylinderBody = new CANNON.Body({shape: cylinderShape,position: new CANNON.Vec3(-2, 0.8, 0),mass: 1,material: boxMaterialCon})// 將剛體添加到物理世界當中world.addBody(cylinderBody)phyMeshes.push(cylinderBody)// 創建圓柱體幾何體const cylinderGeometry = new THREE.CylinderGeometry(0.5 ,0.5, 1, 32)// 創建圓柱體材質const cylinderMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 })// 創建圓柱體網格const cylinderMesh = new THREE.Mesh(cylinderGeometry, cylinderMaterial)// 將網格添加到3D場景當中scene.add(cylinderMesh)meshes.push(cylinderMesh)// 創建一個物理世界的平面// const planeShape = new CANNON.Plane()const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))// 創建一個剛體const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0),type: CANNON.Body.STATIC, // 設置物體為靜態,不受碰撞的影響material: boxMaterialCon})// 設置剛體旋轉(設置旋轉X軸)// planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)// 將剛體添加到物理世界當中world.addBody(planeBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)// 創建一個平面材質const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })// 創建一個平面網格const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)// 旋轉平面90度讓其平鋪// planeMesh.rotation.x = 0.1// 將網格添加到3D場景當中scene.add(planeMesh)// 設置立方體的初始速度boxBody.velocity.set(-2, 0, 0)// 渲染let clock = new THREE.Clock()const animate = () => {// 獲取了兩次渲染之間的時間差,通常用于控制動畫和物理模擬。let delta = clock.getDelta()world.step(delta)// 使用時間差來推進物理世界的模擬for(let i = 0; i < phyMeshes.length; i++) {meshes[i].position.copy(phyMeshes[i].position)meshes[i].quaternion.copy(phyMeshes[i].quaternion)}controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})
接下來實現碰撞組,碰撞組是為了更高效地管理和處理碰撞而引入的概念。通過將具有相似碰撞特性的物體分組,可以在碰撞檢測和碰撞響應時只考慮同一組內的物體之間的碰撞,從而減少不必要的計算量,提高游戲的性能和效率。代碼如下:
我們設置立方體為組1,然后碰撞掩碼就是能夠和誰發生碰撞,我們設置立方體可以和所有物體碰撞:
在球體的分組當中,我們設置碰撞掩碼如下,可以看到我們的球不能碰撞圓柱體:
最終呈現的效果如下:
給出案例的完整代碼供大家學習:
import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 8camera.position.y = 5camera.position.x = 2// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 創建網格數組let phyMeshes: any[] = [] // 物理世界let meshes: any[] = [] // 渲染世界// 設置碰撞組,數值要用2的冪const GROUP1 = 1 // 分組立方體const GROUP2 = 2 // 分組球體const GROUP3 = 4 // 分組圓柱體const GROUP4 = 8 // 分組平面// 創建物理立方體const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))// 設置立方體的材質const boxMaterialCon = new CANNON.Material("boxMaterial")boxMaterialCon.friction = 0// 創建一個剛體const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(2, 0.8, 0),mass: 1,material: boxMaterialCon,collisionFilterGroup: GROUP1, // 設置碰撞組collisionFilterMask: GROUP2 | GROUP3 | GROUP4, // 碰撞掩碼,可以和二組和三、四組碰撞})// 將剛體添加到物理世界當中world.addBody(boxBody)phyMeshes.push(boxBody)// 創建立方體幾何體const boxGeometry = new THREE.BoxGeometry(1, 1, 1)// 創建立方體材質const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })// 創建立方體網格const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)// 將網格添加到3D場景當中scene.add(boxMesh)meshes.push(boxMesh)// 創建物理球const spereShape = new CANNON.Sphere(0.5)// 創建一個剛體const sphereBody = new CANNON.Body({shape: spereShape,position: new CANNON.Vec3(0, 0.8, 0),mass: 1,material: boxMaterialCon,collisionFilterGroup: GROUP2, // 設置碰撞組collisionFilterMask: GROUP1 | GROUP4, // 碰撞掩碼,可以和一、四組碰撞})// 將剛體添加到物理世界當中world.addBody(sphereBody)phyMeshes.push(sphereBody)// 創建球的幾何體const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32)// 創建球的材質const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff })// 創建球網格const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial)// 將網格添加到3D場景當中scene.add(sphereMesh)meshes.push(sphereMesh)// 創建物理圓柱體const cylinderShape = new CANNON.Cylinder(0.5, 0.5, 1, 32)// 創建一個剛體const cylinderBody = new CANNON.Body({shape: cylinderShape,position: new CANNON.Vec3(-2, 0.8, 0),mass: 1,material: boxMaterialCon,collisionFilterGroup: GROUP3, // 設置碰撞組collisionFilterMask: GROUP1 | GROUP4, // 碰撞掩碼,可以和一、四組碰撞})// 將剛體添加到物理世界當中world.addBody(cylinderBody)phyMeshes.push(cylinderBody)// 創建圓柱體幾何體const cylinderGeometry = new THREE.CylinderGeometry(0.5 ,0.5, 1, 32)// 創建圓柱體材質const cylinderMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 })// 創建圓柱體網格const cylinderMesh = new THREE.Mesh(cylinderGeometry, cylinderMaterial)// 將網格添加到3D場景當中scene.add(cylinderMesh)meshes.push(cylinderMesh)// 創建一個物理世界的平面// const planeShape = new CANNON.Plane()const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))// 創建一個剛體const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0.1, 0),type: CANNON.Body.STATIC, // 設置物體為靜態,不受碰撞的影響material: boxMaterialCon,collisionFilterGroup: GROUP4, // 設置碰撞組collisionFilterMask: GROUP1 | GROUP2 | GROUP3, // 碰撞掩碼,可以和一組、二組和三組碰撞})// 設置剛體旋轉(設置旋轉X軸)// planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)// 將剛體添加到物理世界當中world.addBody(planeBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)// 創建一個平面材質const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })// 創建一個平面網格const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)// 旋轉平面90度讓其平鋪// planeMesh.rotation.x = 0.1// 將網格添加到3D場景當中scene.add(planeMesh)// 設置立方體的初始速度boxBody.velocity.set(-2, 0, 0)// 渲染let clock = new THREE.Clock()const animate = () => {// 獲取了兩次渲染之間的時間差,通常用于控制動畫和物理模擬。let delta = clock.getDelta()world.step(delta)// 使用時間差來推進物理世界的模擬for(let i = 0; i < phyMeshes.length; i++) {meshes[i].position.copy(phyMeshes[i].position)meshes[i].quaternion.copy(phyMeshes[i].quaternion)}controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})
本篇文章對canon的學習暫時結束,下篇文章將對canon更加深入講解!!!