Qt Quick 3D物理 - カスタム形状の例
さまざまな形状の使用例を示します。

この例では、複数の剛体メッシュの読み込みと生成、およびそれらのアニメーション処理について解説します。シーンは、サイコロタワー、テーブルクロス、カップ、そして数個のサイコロで構成されています。カップは、生成されたサイコロを拾い上げ、サイコロタワーに入れるようにアニメーション化されています。その後、サイコロはテーブルクロスの上を転がり落ちていきます。
環境
いつものように、PhysicsWorld とView3D があります。View3D には、ライトプローブを設定した環境が用意されています:
environment: SceneEnvironment {
clearColor: "white"
backgroundMode: SceneEnvironment.SkyBox
antialiasingMode: SceneEnvironment.MSAA
antialiasingQuality: SceneEnvironment.High
lightProbe: proceduralSky
}テクスチャ
スカイボックス、テーブルクロス、およびサイコロの数字に使用される4つのテクスチャを定義します:
Texture {
id: proceduralSky
textureData: ProceduralSkyTextureData {
sunLongitude: -115
}
}
Texture {
id: weaveNormal
source: "maps/weave.png"
scaleU: 200
scaleV: 200
generateMipmaps: true
mipFilter: Texture.Linear
}
Texture {
id: numberNormal
source: "maps/numbers-normal.png"
}
Texture {
id: numberFill
source: "maps/numbers.png"
generateMipmaps: true
mipFilter: Texture.Linear
}シーン
カメラと指向性ライトを含むシーンを格納するNodeがあります:
id: scene
scale: Qt.vector3d(2, 2, 2)
PerspectiveCamera {
id: camera
position: Qt.vector3d(-45, 25, 60)
eulerRotation: Qt.vector3d(-6, -33, 0)
clipFar: 1000
clipNear: 0.1
}
DirectionalLight {
eulerRotation: Qt.vector3d(-45, 25, 0)
castsShadow: true
brightness: 1
shadowMapQuality: Light.ShadowMapQualityHigh
pcfFactor: 0.1
shadowBias: 1
}テーブルクロス
テーブルクロスを追加します。これは、織り模様のテクスチャを持つモデルと、衝突処理用のHeightFieldShape で構成されるStaticRigidBody です。
StaticRigidBody {
position: Qt.vector3d(-15, -8, 0)
id: tablecloth
Model {
geometry: HeightFieldGeometry {
id: tableclothGeometry
extents: Qt.vector3d(150, 20, 150)
source: "maps/cloth-heightmap.png"
smoothShading: false
}
materials: PrincipledMaterial {
baseColor: "#447722"
roughness: 0.8
normalMap: weaveNormal
normalStrength: 0.7
}
}
collisionShapes: HeightFieldShape {
id: hfShape
extents: tableclothGeometry.extents
source: "maps/cloth-heightmap.png"
}
}カップ
カップは、モデルとコリジョン形状としてTriangleMeshShape を持つDynamicRigidBody として定義します。eulerRotation およびposition プロパティには、これらがアニメーションの一部であるため、Behaviorが設定されています。
DynamicRigidBody {
id: diceCup
isKinematic: true
mass: 0
property vector3d bottomPos: Qt.vector3d(11, 6, 0)
property vector3d topPos: Qt.vector3d(11, 45, 0)
property vector3d unloadPos: Qt.vector3d(0, 45, 0)
position: bottomPos
kinematicPivot: Qt.vector3d(0, 6, 0)
kinematicPosition: bottomPos
collisionShapes: TriangleMeshShape {
id: cupShape
source: "meshes/simpleCup.mesh"
}
Model {
source: "meshes/cup.mesh"
materials: PrincipledMaterial {
baseColor: "#cc9988"
roughness: 0.3
metalness: 1
}
}
}タワー
タワーは、ModelとTriangleMeshShape を衝突形状として持つ、単なるStaticRigidBody です。
StaticRigidBody {
id: diceTower
x: -4
Model {
id: testModel
source: "meshes/tower.mesh"
materials: [
PrincipledMaterial {
baseColor: "#ccccce"
roughness: 0.3
},
PrincipledMaterial {
id: glassMaterial
baseColor: "#aaaacc"
transmissionFactor: 0.95
thicknessFactor: 1
roughness: 0.05
}
]
}
collisionShapes: TriangleMeshShape {
id: triShape
source: "meshes/tower.mesh"
}
}サイコロ
サイコロを生成するには、カスタム「Die」QMLオブジェクトと「Repeater3D 」を持つコンポーネントを使用します。
Component {
id: diceComponent
Die {}
}
Repeater3D {
id: dicePool
model: 25
delegate: diceComponent
delegateModelAccess: DelegateModel.ReadWrite
function restore() {
for (let i = 0; i < count; i++) {
(objectAt(i) as Die).restore()
}
}
}Die オブジェクトには、ConvexMeshShape と Model を持つDynamicRigidBody が含まれています。位置、色、スケール、メッシュソースは、サイコロごとにランダムに生成されます。
DynamicRigidBody {
id: thisBody
required property int index
Texture {
id: numberNormal
source: "maps/numbers-normal.png"
}
Texture {
id: numberFill
source: "maps/numbers.png"
generateMipmaps: true
mipFilter: Texture.Linear
}
function randomInRange(min, max) {
return Math.random() * (max - min) + min
}
function restore() {
reset(initialPosition, eulerRotation)
}
scale: Qt.vector3d(scaleFactor, scaleFactor, scaleFactor)
eulerRotation: Qt.vector3d(randomInRange(0, 360),
randomInRange(0, 360),
randomInRange(0, 360))
property vector3d initialPosition: Qt.vector3d(11 + 1.5 * Math.cos(index/(Math.PI/4)),
6 + index * 1.5,
0)
position: initialPosition
property real scaleFactor: randomInRange(0.8, 1.4)
property color baseCol: Qt.hsla(randomInRange(0, 1),
randomInRange(0.6, 1.0),
randomInRange(0.4, 0.7),
1.0)
collisionShapes: ConvexMeshShape {
id: diceShape
source: Math.random() < 0.25 ? "meshes/icosahedron.mesh"
: Math.random() < 0.5 ? "meshes/dodecahedron.mesh"
: Math.random() < 0.75 ? "meshes/octahedron.mesh"
: "meshes/tetrahedron.mesh"
}
Model {
id: thisModel
source: diceShape.source
receivesShadows: false
materials: PrincipledMaterial {
metalness: 1.0
roughness: thisBody.randomInRange(0.2, 0.6)
baseColor: thisBody.baseCol
emissiveMap: numberFill
emissiveFactor: Qt.vector3d(1, 1, 1)
normalMap: numberNormal
normalStrength: 0.75
}
}
}アニメーション
サイコロをカップからサイコロタワーへ移動させるため、カップをアニメーションで上に移動させた後、傾けて倒します。アニメーションが物理シミュレーションと確実に同期するように、AnimationController を使用し、これをPhysicsWorld のonFrameDone シグナルに接続します。シミュレーションの各フレーム終了後、経過したタイムステップ分だけアニメーションを進めます。
Connections {
target: physicsWorld
property real totalAnimationTime: 7500
function onFrameDone(timeStep) {
let progressStep = timeStep / totalAnimationTime
animationController.progress += progressStep
if (animationController.progress >= 1) {
animationController.completeToEnd()
animationController.reload()
animationController.progress = 0
}
}
}
AnimationController {
id: animationController
animation: SequentialAnimation {
PauseAnimation { duration: 2500 }
PropertyAnimation {
target: diceCup
property: "kinematicPosition"
to: diceCup.topPos
duration: 2500
}
ParallelAnimation {
PropertyAnimation {
target: diceCup
property: "kinematicEulerRotation.z"
to: 130
duration: 1500
}
PropertyAnimation {
target: diceCup
property: "kinematicPosition"
to: diceCup.unloadPos
duration: 1500
}
}
PauseAnimation { duration: 1000 }
ParallelAnimation {
PropertyAnimation {
target: diceCup
property: "kinematicEulerRotation.z"
to: 0
duration: 1500
}
PropertyAnimation {
target: diceCup
property: "kinematicPosition"
to: diceCup.topPos
duration: 1500
}
}
PropertyAnimation { target: diceCup; property: "kinematicPosition"; to: diceCup.bottomPos; duration: 1500 }
PauseAnimation { duration: 2000 }
ScriptAction { script: dicePool.restore() }
}
}コントローラー
最後に、キーボードを使ってカメラを制御できるように、WasdController が追加されます:
WasdController {
keysEnabled: true
controlledObject: camera
speed: 0.2
}ファイル:
- customshapes/CMakeLists.txt
- customshapes/Die.qml
- customshapes/customshapes.pro
- customshapes/main.cpp
- customshapes/main.qml
- customshapes/qml.qrc
- customshapes/resources.qrc
画像:
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.