- Qt 6.9
- Qt Quick 3D Physics
- Qt Quick 3D Physics Examples and Tutorials
- Qt Quick 3D Physics - CharacterController Example
- Qt Quick 3D Physics | Commercial or GPLv3

# Qt Quick 3D Physics - CharacterController Example

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

     Item {
         id: root

         property real xSpeed: 0.1
         property real ySpeed: 0.1

         property bool xInvert: false
         property bool yInvert: true

         property bool mouseEnabled: true
         property bool keysEnabled: true

         readonly property bool inputsNeedProcessing: status.useMouse

         property alias acceptedButtons: dragHandler.acceptedButtons

         implicitWidth: parent.width
         implicitHeight: parent.height
         focus: keysEnabled

         property bool moveForwards: false
         property bool moveBackwards: false
         property bool moveLeft: false
         property bool moveRight: false
         property bool moveUp: false
         property bool moveDown: false

         property vector2d cameraRotation: Qt.vector2d(0, 0)

         property bool sprintActive: false
         property bool crouchActive: false

         DragHandler {
             id: dragHandler
             target: null
             enabled: mouseEnabled
             onCentroidChanged: {
                 mouseMoved(Qt.vector2d(centroid.position.x, centroid.position.y))
             }

             onActiveChanged: {
                 if (active)
                     mousePressed(Qt.vector2d(centroid.position.x,
                                              centroid.position.y))
                 else
                     mouseReleased(Qt.vector2d(centroid.position.x,
                                               centroid.position.y))
             }
         }

         TapHandler {
             onTapped: root.forceActiveFocus()
         }

         Keys.onPressed: (event) => { if (keysEnabled) handleKeyPress(event) }
         Keys.onReleased: (event) => { if (keysEnabled) handleKeyRelease(event) }

         function mousePressed(newPos) {
             root.forceActiveFocus()
             status.currentPos = newPos
             status.lastPos = newPos
             status.useMouse = true
         }

         function mouseReleased(newPos) {
             status.useMouse = false
         }

         function mouseMoved(newPos) {
             status.currentPos = newPos
         }

         function forwardPressed() {
             moveForwards = true
             moveBackwards = false
         }

         function forwardReleased() {
             moveForwards = false
         }

         function backPressed() {
             moveBackwards = true
             moveForwards = false
         }

         function backReleased() {
             moveBackwards = false
         }

         function rightPressed() {
             moveRight = true
             moveLeft = false
         }

         function rightReleased() {
             moveRight = false
         }

         function leftPressed() {
             moveLeft = true
             moveRight = false
         }

         function leftReleased() {
             moveLeft = false
         }

         function upPressed() {
             moveUp = true
             moveDown = false
         }

         function upReleased() {
             moveUp = false
         }

         function downPressed() {
             moveDown = true
             moveUp = false
         }

         function downReleased() {
             moveDown = false
         }

         function sprintPressed() {
             sprintActive = true
             crouchActive = false
         }

         function sprintReleased() {
             sprintActive = false
         }

         function sneakPressed() {
             crouchActive = true
             sprintActive = false
         }

         function sneakReleased() {
             crouchActive = false
         }

         function handleKeyPress(event) {
             switch (event.key) {
             case Qt.Key_W:
             case Qt.Key_Up:
                 forwardPressed()
                 break
             case Qt.Key_S:
             case Qt.Key_Down:
                 backPressed()
                 break
             case Qt.Key_A:
             case Qt.Key_Left:
                 leftPressed()
                 break
             case Qt.Key_D:
             case Qt.Key_Right:
                 rightPressed()
                 break
             case Qt.Key_R:
             case Qt.Key_PageUp:
                 upPressed()
                 break
             case Qt.Key_F:
             case Qt.Key_PageDown:
                 downPressed()
                 break
             case Qt.Key_Shift:
                 sprintPressed()
                 break
             case Qt.Key_Control:
                 sneakPressed()
                 break
             }
         }

         function handleKeyRelease(event) {
             switch (event.key) {
             case Qt.Key_W:
             case Qt.Key_Up:
                 forwardReleased()
                 break
             case Qt.Key_S:
             case Qt.Key_Down:
                 backReleased()
                 break
             case Qt.Key_A:
             case Qt.Key_Left:
                 leftReleased()
                 break
             case Qt.Key_D:
             case Qt.Key_Right:
                 rightReleased()
                 break
             case Qt.Key_R:
             case Qt.Key_PageUp:
                 upReleased()
                 break
             case Qt.Key_F:
             case Qt.Key_PageDown:
                 downReleased()
                 break
             case Qt.Key_Shift:
                 sprintReleased()
                 break
             case Qt.Key_Control:
                 sneakReleased()
                 break
             }
         }

         Timer {
             id: updateTimer
             interval: 16
             repeat: true
             running: root.inputsNeedProcessing
             onTriggered: {
                 processInputs()
             }
         }

         function processInputs() {
             if (root.inputsNeedProcessing)
                 status.processInput()
         }

         QtObject {
             id: status

             property bool useMouse: false

             property vector2d lastPos: Qt.vector2d(0, 0)
             property vector2d currentPos: Qt.vector2d(0, 0)

             function processInput() {
                 if (useMouse) {
                     // Get the delta
                     var delta = Qt.vector2d(lastPos.x - currentPos.x,
                                             lastPos.y - currentPos.y)
                     // rotate x
                     var rotateX = delta.x * xSpeed
                     if (xInvert)
                         rotateX = -rotateX
                     cameraRotation.x += rotateX

                     // rotate y
                     var rotateY = delta.y * -ySpeed
                     if (yInvert)
                         rotateY = -rotateY
                     cameraRotation.y += rotateY
                     lastPos = currentPos
                 }
             }
         }
     }