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)
             }
         }
     }
 }