Sur cette page

Qt Quick 3D Physics - Exemple sur la masse

Illustre différentes façons de définir la masse et l'inertie d'un corps.

Capture d'écran d'un rendu 3D représentant trois sphères empilées sur une surface verte, de couleur bleue, violette et rouge de gauche à droite

Cet exemple présente trois méthodes différentes pour définir la masse et l'inertie d'un corps. La scène se compose de trois corps, constitués de trois sphères empilées verticalement. Ces corps ont tous la même masse, mais leurs centres de masse et leurs tenseurs d'inertie diffèrent, ce qui leur confère un comportement différent en cas de collision.

Configuration

Nous commençons par ajouter nos « PhysicsWorld » :

PhysicsWorld {
    running: true
    gravity: Qt.vector3d(0, -9.81, 0)
    typicalLength: 1
    typicalSpeed: 10
    scene: viewport.scene
}

Nous procédons à la configuration habituelle avec un environnement, une caméra et des lumières :

environment: SceneEnvironment {
    clearColor: "lightblue"
    backgroundMode: SceneEnvironment.Color
}

PerspectiveCamera {
    id: camera
    position: Qt.vector3d(0, 2, 5)
    eulerRotation: Qt.vector3d(-10, 0, 0)
    clipFar: 100
    clipNear: 0.01
}

DirectionalLight {
    eulerRotation.x: -45
    eulerRotation.y: 45
    castsShadow: true
    brightness: 1
    shadowFactor: 50
    shadowBias: 0.1
    pcfFactor: 0.01
}

Objets physiques

Nous disposons de notre plan statique habituel :

StaticRigidBody {
    position: Qt.vector3d(0, 0, 0)
    eulerRotation: Qt.vector3d(-90, 0, 0)
    collisionShapes: PlaneShape {}
    Model {
        source: "#Rectangle"
        materials: PrincipledMaterial {
            baseColor: "green"
        }
        castsShadows: false
        receivesShadows: true
    }
}

Nous définissons une classe QML personnalisée pour notre corps, que nous appelons RolyPoly, car il se comporte comme un jouet « roly-poly ». Le RolyPoly est un « DynamicRigidBody » doté de trois formes de collision sphériques :

DynamicRigidBody {
    id: body
    property string color: "blue"

    collisionShapes: [
        SphereShape {
            id: sphere0
            diameter:  1
        },
        SphereShape {
            id: sphere1
            diameter:  0.8
            position: Qt.vector3d(0, 0.6, 0)
        },
        SphereShape {
            id: sphere2
            diameter:  0.6
            position: Qt.vector3d(0, 1.1, 0)
        }
    ]

    Model {
        source: "#Sphere"
        position: sphere0.position
        scale: Qt.vector3d(1,1,1).times(sphere0.diameter*0.01)
        materials: PrincipledMaterial {
            baseColor: body.color
        }
    }

    Model {
        source: "#Sphere"
        position: sphere1.position
        scale: Qt.vector3d(1,1,1).times(sphere1.diameter*0.01)
        materials: PrincipledMaterial {
            baseColor: body.color
        }
    }

    Model {
        source: "#Sphere"
        position: sphere2.position
        scale: Qt.vector3d(1,1,1).times(sphere2.diameter*0.01)
        materials: PrincipledMaterial {
            baseColor: body.color
        }
    }
}

Nous ajoutons ensuite trois « roly-polys » à notre scène :

RolyPoly {
    position: Qt.vector3d(-2, 0.5, 0)
    color: "blue"

    mass: 0.9
    centerOfMassPosition: Qt.vector3d(0, -0.5, 0)
    inertiaTensor: Qt.vector3d(0.217011, 0.0735887, 0.217011)
    massMode: DynamicRigidBody.MassAndInertiaTensor
}

RolyPoly {
    position: Qt.vector3d(0, 0.5, 0)
    color: "purple"

    mass: 0.9
    centerOfMassPosition: Qt.vector3d(0, -0.5, 0)
    inertiaTensor: Qt.vector3d(0.05, 100, 100)
    massMode: DynamicRigidBody.MassAndInertiaTensor
}

RolyPoly {
    position: Qt.vector3d(2, 0.5, 0)
    color: "red"

    mass: 0.9
    massMode: DynamicRigidBody.Mass
}

Les RolyPoly violets et bleus possèdent un centre de masse et un tenseur d’inertie personnalisés. Étant donné que les corps utilisent par défaut une densité uniforme et calculent automatiquement la masse et l’inertie, nous définissons `massMode` sur ` DynamicRigidBody.MassAndInertiaTensor` pour nos corps violets et bleus afin d’utiliser à la place les tenseurs de masse et d’inertie que nous avons fournis. Le centre de masse plus bas fera en sorte que les corps se redressent toujours après avoir été renversés. Le tenseur d’inertie du corps violet fait en sorte qu’il vacillera facilement dans un sens mais à peine dans l’autre. Le corps rouge possède un centre de masse calculé automatiquement et restera donc couché après avoir été renversé.

Lancer de balles

Pour tester le comportement des différents corps, nous ajoutons un nœud permettant de tirer des balles :

Node {
    id: shapeSpawner
    property var spheres: []
    property var sphereComponent: Qt.createComponent("Sphere.qml")

    function createBall(position, forward) {
        let diameter = 0.2
        let speed = 20
        let sphere = sphereComponent.createObject(shapeSpawner, {
                                                      "position": position,
                                                      "sphereDiameter": diameter
                                                  })
        sphere.setLinearVelocity(forward.times(speed))

        var pair = {
            "sphere": sphere,
            "date": Date.now()
        }

        spheres.push(pair)

        if (sphere === null) {
            console.log("Error creating object")
        }
    }

    function clean() {
        spheres = spheres.filter(sphere => {
            let diff = Date.now() - sphere['date'];
            if (diff > 5000) {
                sphere['sphere'].destroy();
                return false;
            }
            return true;
        });
    }

    Timer {
        interval: 200
        running: true
        repeat: true
        onTriggered: shapeSpawner.clean()
    }
}

Nous ajoutons ensuite un « WasdController » pour pouvoir déplacer la caméra, viser et tirer des balles sur les corps :

WasdController {
    speed: 0.01
    shiftSpeed: 0.1
    controlledObject: camera
    Keys.onPressed: (event) => {
        handleKeyPress(event);
        if (event.key === Qt.Key_Space) {
            shapeSpawner.createBall(camera.position, camera.forward);
        }
    }
    Keys.onReleased: (event) => { handleKeyRelease(event) }
}

Fichiers :

© 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.