- Qt 6.8
- Qt Quick 3D Physics
- Qt Quick 3D Physics Examples and Tutorials
- Qt Quick 3D Physics - Cannon Example
- Qt Quick 3D Physics | Commercial or GPLv3

# Qt Quick 3D Physics - Cannon Example

     // Copyright (C) 2022 The Qt Company Ltd.
     // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
     import QtQuick
     import QtQuick3D
     import QtQuick3D.Physics
     import QtQuick3D.Helpers
     import QtQuick.Controls
     import QtQuick.Layouts

     Window {
         id: appWindow
         width: 800
         height: 600
         visible: true
         title: qsTr("Qt Quick 3D Physics - Cannon")

         PhysicsWorld {
             scene: viewport.scene
         }

         View3D {
             id: viewport
             width: parent.width
             height: parent.height
             focus: true

             environment: SceneEnvironment {
                 antialiasingMode: SceneEnvironment.MSAA
                 backgroundMode: SceneEnvironment.Color
                 clearColor: "#f0f0f0"
             }

             PerspectiveCamera {
                 id: camera
                 position: Qt.vector3d(-4000, 5000, 10000)
                 eulerRotation: Qt.vector3d(-20, -20, 0)
                 clipFar: 500000
                 clipNear: 100
             }

             DirectionalLight {
                 eulerRotation: Qt.vector3d(-45, 45, 0)
                 castsShadow: true
                 brightness: 1
                 shadowMapQuality: Light.ShadowMapQualityVeryHigh
                 shadowFactor: 100
             }

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

             Node {
                 id: shapeSpawner
                 property var instancesBoxes: []
                 property var instancesSpheres: []
                 property int stackCount: 0
                 property var boxComponent: Qt.createComponent("Box.qml")
                 property var sphereComponent: Qt.createComponent("Sphere.qml")

                 function createStack(stackZ, numStacks) {
                     let size = 10
                     let extents = 400

                     for (var i = 0; i < size; i++) {
                         for (var j = 0; j < size - i; j++) {
                             let x = j * 2 - size + i
                             let y = i * 2 + 1
                             let z = 5 * (stackZ - numStacks)
                             let center = Qt.vector3d(x, y, z).times(0.5 * extents)
                             let box = boxComponent.incubateObject(shapeSpawner, {
                                                                       "position": center,
                                                                       "xyzExtents": extents
                                                                   })
                             instancesBoxes.push(box)
                         }
                     }
                 }

                 function createBall(position, forward) {
                     var diameter = 600
                     var speed = 20000
                     let settings = {
                         "position": position,
                         "sphereDiameter": diameter
                     }
                     let sphere = sphereComponent.createObject(shapeSpawner, settings)
                     sphere.setLinearVelocity(forward.times(speed))
                     instancesSpheres.push(sphere)

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

                 function reset() {
                     // Only run method if previous stack has been created fully
                     for (var i = 0; i < instancesBoxes.length; i++)
                         if (!instancesBoxes[i].object)
                             return

                     instancesSpheres.forEach(sphere => {
                                                  sphere.collisionShapes = []
                                                  sphere.destroy()
                                              })
                     instancesBoxes.forEach(box => {
                                                box.object.collisionShapes = []
                                                box.object.destroy()
                                            })
                     instancesSpheres = []
                     instancesBoxes = []

                     for (var stackI = 0; stackI < stackSlider.value; stackI++) {
                         shapeSpawner.createStack(stackI, stackSlider.value)
                     }
                 }
             }
             Crosshair {
                 id: crossHair
                 anchors.centerIn: parent
             }
         }

         Component.onCompleted: {
             shapeSpawner.reset()
         }

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

         Frame {
             background: Rectangle {
                 color: "#c0c0c0"
                 border.color: "#202020"
             }
             anchors.top: parent.top
             anchors.left: parent.left
             anchors.margins: 10

             ColumnLayout {
                 Label {
                     text: "No. Stacks: " + stackSlider.value.toFixed(0)
                 }
                 Slider {
                     id: stackSlider
                     focusPolicy: Qt.NoFocus
                     from: 1
                     to: 9
                     value: 4
                     stepSize: 1
                     snapMode: Slider.SnapOnRelease
                 }
                 Button {
                     id: resetButton
                     Layout.alignment: Qt.AlignHCenter
                     text: "Reset scene"
                     onClicked: shapeSpawner.reset()
                 }
                 Button {
                     id: fireButton
                     Layout.alignment: Qt.AlignHCenter
                     text: "Fire!"
                     onClicked: shapeSpawner.createBall(camera.position,
                                                        camera.forward)
                 }
             }
         }
     }