Learn what Felgo offers to help your business succeed. Start your free evaluation today! Felgo for Your Business

CarChallenge 3D Demo

 import QtQuick 2.0
 import Felgo 3.0

 import Qt3D.Core 2.0 as Qt3D // for calculating the distance from the car back to the cam

 import "hud" // for the IngameMenu

 GameWindow {
     id: window
     screenWidth: 960
     screenHeight: 640
     color: "black"

     activeScene: scene

     // You get free licenseKeys from https://felgo.com/licenseKey
     // With a licenseKey you can:
     //  * Publish your games & apps for the app stores
     //  * Remove the Felgo Splash Screen or set a custom one (available with the Pro Licenses)
     //  * Add plugins to monetize, analyze & improve your apps (available with the Pro Licenses)
     //licenseKey: "<generate one from https://felgo.com/licenseKey>"

     // for creating and destroying entities at runtime (rockets)
     EntityManager {
         id: entityManager
         entityContainer: level
     }

     // is needed by the Render3D component - to this entity the Entity objects are added to
     property alias rootEntity: scene3D.rootEntity

     // so the EditableComponent knows about the itemEditor
     property alias itemEditor: ingameMenu.itemEditor

     // for shorter access of player1
     property alias player: level.player_red

     // to make referencing it easier without ingameSettings.*
     property alias fullscreen2DModeEnabled: ingameSettings.fullscreen2DModeEnabled

     Item {
       id: ingameSettings

       property bool physicsDebugVisible: false // allows toggling the Box2D physics debugView - disable by default
       // if this is set, the scale of the Scene is set to 1 again and the 3d scene is hidden
       property bool fullscreen2DModeEnabled: false
       // this is the amount we move "back" from the player car to make a following cam; point towards the negative x direction when facing 0 degree
       property real cameraDistanceHorizontal: -120
       // distance we move the camera on top into the z axis
       property real cameraDistanceVertical: -40

       // this adds a modifiable Settings dialog with sliders and switches
       // it is displayed in the ItemEditor
       EditableComponent {
         editableType: "CamType"
         properties: {
           "Camera": {
             "cameraDistanceHorizontal": {"min": -500, "max": -10},
             "cameraDistanceVertical": {"min": -500, "max": -10},
             "physicsDebugVisible": {},
             "fullscreen2DModeEnabled": {},
           }
         }
       }
     }

     // calculates the position where we need to place the target cam
     // the position is defined by the values behind (=horizontal) and above (=vertical) from the player
     TargetCamTransformHelper {
       id: cameraTransformHelper
       // connect with the player position & rotation internally
       player: window.player
       cameraDistanceHorizontal: ingameSettings.cameraDistanceHorizontal
       cameraDistanceVertical: ingameSettings.cameraDistanceVertical
     }

     // currently it is not recommended to use a Scene3D within a Scene, because the Scene applies content scaling
     // also, it does not make sense to mix the Scene3D and Scene directly - rather create a HUD and other UI elements in a separate scene like done with hud below
     // if it would be placed in the scene, the content scaling of the Scene would need to be reverted then here
     // TODO: test use cases when mixing 3d models within a 2d scene (e.g. if only the car would be rendered from top-to-bottom
     Scene3D {
       id: scene3D
       anchors.fill: parent

       visible: !fullscreen2DModeEnabled

       //opacity: 0.7 // no transparency needed in normal 3d testing, only useful to display it on top of the 2d scene

       cameraFarPlane: -2000

       skyboxCameraRotationAngle: 270
       skyboxCameraRotationAxis: Qt.vector3d(1,0,0)
       skyboxBaseName: Qt.resolvedUrl("../assets/3d/cubemaps/mountains/mountains")
       skyboxExtension: ".bmp"

       // use this for a top-down cam
 //      positionZ: -500
 //      // DONE: why is the cam upVector changed when we change the viewCenter? -> only if isControlledCamera is true
 //      cameraUpVector: Qt.vector3d(0,0,-1)
 //      cameraViewCenter: Qt.vector3d(-10,-10,0)
 //      cameraViewCenter: Qt.vector3d(-level.width/2, -level.height/2, 0) // move the camera so the point 0/0 of the world is centered

       // use this for a follow cam
       cameraViewCenter: Qt.vector3d( -player.x , -player.y , 0)
       cameraPosition: Qt.vector3d( cameraTransformHelper.targetX, cameraTransformHelper.targetY, cameraTransformHelper.targetZ)
       cameraUpVector: Qt.vector3d(0,0,-1)

       // to avoid the wrong camera tilting, we can either disable the controlledCamera of the default camera
       // or choose a completely own frameGraph; this is more flexible, as we can e.g. display a minimap or a rear mirror
       // it makes sense to disable the controlledCamera, because the automatic upVector calculation of the camera is inflexible, as it is based on the cross-product calculation of the x-axis
       // e.g. in CarChallenge3D, this would lead to incorrect target cam behavior when the position & viewCenter change
       isControlledCamera: false
       // use the custom frameGraph for e.g. a minimap, or 2 player cameras, or a rear mirror
       // the custom Framegraph is not working atm, because of the wrong multi-viewport atm: (also see MultiViewportMain.qml for a more detailed analysis)
 //      frameGraph: customFrameGraphEntity.frameGraph
 //      rootEntityComponents: [customFrameGraphEntity.frameGraph]
 //      activeFrameGraph: customFrameGraphEntity.frameGraph.activeFrameGraph

       // does not work if added here (only the clearColor is displayed) if it is added directly in Scene3D, it does work though
       // just by adding it here it gets displayed!
       // if we use a CustomFrameGraph, it must be a child of Scene3D, otherwise this assert:
       // ASSERT failure in void __thiscall Qt3D::Render::FrameGraphVisitor::traverse(class Qt3D::Render::FrameGraphNode *,class Qt3D::Render::Renderer *,class QVector<class QSharedPointer<class Qt3D::QAspectJob> > *): "The FrameGraphRoot is null", file backend\framegraph\framegraphvisitor.cpp, line 65
 //      CustomFramegraphCamera {
 //        id: customFrameGraphEntity
 //        cameraViewCenter: Qt.vector3d( -player.x , -player.y , 0)
 //        cameraPosition: Qt.vector3d( cameraTransformHelper.targetX, cameraTransformHelper.targetY, cameraTransformHelper.targetZ)
 //        cameraUpVector: Qt.vector3d(0,0,-1)
 //      }
     }

     Scene {
         id: scene

         width: 640
         height: 640
         //visible: false // to disable rendering of the 2d view completely - but then focus handling doesnt work anymore because the key presses are not forwarded
         opacity: 0.5
         // put it on top of the 3d world by default
         // is toggleable from the ingame menu - if it is hidden, it is set to z:-1
         z: 1

         // do not apply the scale here directly, but to the Level
         sceneAlignmentX: fullscreen2DModeEnabled ? "center" : "left"
         sceneAlignmentY: fullscreen2DModeEnabled ? "center" : "top"

         Level {
             // this gets accessed by its id from JoystickControllerHUD below
             id: level

             // either display it fullscreen (with scale 1), or as a minimap (with scale 0.4)
             scale: fullscreen2DModeEnabled ? 1 : 0.4
             transformOrigin: Item.TopLeft // is needed to scale the child correctly; if we want to position it top right, change it to TopRight

            // manual positioning would work like that:
 //         x: scene.gameWindowAnchorItem.x/scale // position left, if the scale would be applied to the Scene
 //         x: (scene.gameWindowAnchorItem.x+scene.gameWindowAnchorItem.width)/scale - width // positions it right
 //         anchors.right: scene.gameWindowAnchorItem.right // anchoring doesnt work when a scale is used

         }

         // we cannot set focus directly to GameWindow, thus create a new Item here
         focus: true
         // forward the input keys to both players
         // this only works if the Scene is visible! key forwarding does not work if the Scene is invisible
         // however, the onPressed signal here in the Sce is still received
         Keys.forwardTo: [level.player_red.controller, level.player_blue.controller]

         Keys.onPressed: {
           //console.debug("key pressed in scene:", event.key)
           // if positionZ is changed, we remove the binding and the cam does not follow the car anymore!
           // NOTE: pressing +/- breaks the property binding, and you then do not have a targetCam anymore but a static camera
           if(event.key === Qt.Key_Minus) {
             scene3D.positionZ -= 10
           } else if(event.key === Qt.Key_Plus) {
             scene3D.positionZ += 10
           } else if(event.key === Qt.Key_PageDown) {
             ingameSettings.cameraDistanceVertical += 10
           } else if(event.key === Qt.Key_PageUp) {
             ingameSettings.cameraDistanceVertical -= 10
           }
         }
     }//GameScene

     // by putting the hud elements in a scene, content-scaling is applied
     // however, setting the sizes with dp() and sp() works equally well
     // but a Scene has the advantage that the ItemEditor UI elements and GameButton components get scaled correctly, thus use a Scene
     Scene {
     //Item {
 //      anchors.fill: parent
       id: hud
       z: 3

       MouseArea {
         anchors.fill: hud.gameWindowAnchorItem
         onWheel: {
           //console.debug("wheel.x: ", wheel.x, wheel.y, wheel.angleDelta, wheel.pixelDelta)

           // angleDelta is between +360 ... -360
           var yDelta = wheel.angleDelta.y

           cameraTransformHelper.cameraDistanceHorizontal += yDelta*0.1
         }
         onClicked: {
           // only enable firing of weapon by clicking on mobile
           if(system.desktopPlatform)
             return

           // fire the player weapon if clicked anywhere on the screen
           // alternatively, we could display a button to fire a weapon
           player.handleInputAction("fire")
         }
       }

 /*
   for debugging only

       Text {
         font.pixelSize: hud.sp(20)
         text: "Camera pos: " + scene3D.cameraPosition + "\n upVector: " + scene3D.cameraUpVector + "\n viewCenter: " + scene3D.cameraViewCenter +
               "\nCamera Horizontal Distance: " + cameraTransformHelper.cameraDistanceHorizontal + "\nCamera Up Distance: " + cameraTransformHelper.cameraDistanceVertical
         color: "white"

         // NOTE: with the Skybox shader, displaying the Text elements (all of them, including the button texts below) on top does not work anymore!
         // by setting the renderType to NativeRendering, it works again (for ALL texts!)
         // does not matter which text element we change the renderType, only one of them is sufficient
         // UPDATE: when added in a Scene, the renderType shall not be NativeRendering otherwise it's blurred; also, after setting z to 3 the z issues are resolved
         // UPDATE2: still problems when it is not set to NativeRendering; somehow only with this Text element (if set below it does not help!)
         // if the IngameMenu is invisible in the beginning, the z error does not occur -> only if ItemEditor is visible in the beginning the error occurs! thus set it to invisible
         //renderType: Text.NativeRendering
       }

       Row {
         spacing: dp(10)
         anchors.bottom: parent.bottom
         anchors.right: parent.right

         // only for debugging & during development
         GameButton {
           text: "Reset Cam"

           onClicked: {
             scene3D.cameraPosition =  Qt.vector3d( 0,0,-1000 )
             scene3D.cameraUpVector =  Qt.vector3d( 0,1,0 )
             scene3D.cameraViewCenter =  Qt.vector3d( -250,-250,0 )
           }
         }
         GameButton {
           text: "Toggle UpVector"
           property int mode: 0

           onClicked: {
             console.debug("this mode we set now:", mode)
             if(mode === 0)
               scene3D.cameraUpVector =  Qt.vector3d( 0,1,0 )
             else if(mode === 1)
               scene3D.cameraUpVector =  Qt.vector3d( 1,0,0 )
             else if(mode === 2)
               scene3D.cameraUpVector =  Qt.vector3d( 0,0,1 )
             else if(mode === 3)
               scene3D.cameraUpVector =  Qt.vector3d( 0,-1,0 )
             else if(mode === 4)
               scene3D.cameraUpVector =  Qt.vector3d( -1,0,0 )
             else if(mode === 5)
               scene3D.cameraUpVector =  Qt.vector3d( 0,0,-1 )

             mode++
             if(mode === 6)
               mode = 0
           }
         }

         GameButton {
           text: "Toggle UpVector CustomFrameGraph"
           property int mode: 0

           onClicked: {
             console.debug("this mode we set now:", mode)
             if(mode === 0)
               customFrameGraphEntity.cameraUpVector =  Qt.vector3d( 0,1,0 )
             else if(mode === 1)
               customFrameGraphEntity.cameraUpVector =  Qt.vector3d( 1,0,0 )
             else if(mode === 2)
               customFrameGraphEntity.cameraUpVector =  Qt.vector3d( 0,0,1 )
             else if(mode === 3)
               customFrameGraphEntity.cameraUpVector =  Qt.vector3d( 0,-1,0 )
             else if(mode === 4)
               customFrameGraphEntity.cameraUpVector =  Qt.vector3d( -1,0,0 )
             else if(mode === 5)
               customFrameGraphEntity.cameraUpVector =  Qt.vector3d( 0,0,-1 )

             mode++
             if(mode === 6)
               mode = 0
           }
         }
       }// Row
 */

       JoystickControllerHUD {
         // only show if in the game state
         visible: ingameMenu.state === "game"
         anchors.bottom: parent.bottom
         // like this we could position it to the very left of the screen, but it feels better if there is some space from the left on widescreen devices, thus leave the default
         // there is some spacing, because the Hud Scene is centered with content scaling (test it by changing the window size at runtime to see this effect)
         //anchors.left: hud.gameWindowAnchorItem.left

         // the joystick width is the space from the left to the start of the logical scene, so the radius is its half
         joystickRadius: hud.dp(90)

         // this allows setting custom images for the JoystickControllerHUD component
         source: "../assets/img/joystick_background.png"
         thumbSource: "../assets/img/joystick_thumb.png"

         // this is the mapping between the output of the JoystickControllerHUD to the input of the player's TwoAxisController
         // this is a performance improvement, to not have to bind every time the position changes
         property variant playerTwoxisController: level.player_red.getComponent("TwoAxisController")
         onControllerXPositionChanged: playerTwoxisController.xAxis = controllerXPosition;
         onControllerYPositionChanged: playerTwoxisController.yAxis = controllerYPosition;
       }// JoystickControllerHUD

       IngameMenu {
         id: ingameMenu
         // if it is visible from the beginning and also the ItemEditor is visible, there are errors again with text rendering!
         // as the ItemEditor is now invisible initially, make the menu visible in the beginning
         // initially the about screen is shown and a play button
         //visible: false // gets visible if the menu button is pressed

         anchors.fill: hud.gameWindowAnchorItem

         // initial value based on our current scene.z value
         // can then be changed by the user
         miniMapOn: scene.z === 1

         onMiniMapOnChanged: {
           // making it inisible is not working, because then the focus and input handling of the cars would not work anymore
           // thus hide it below the Scene3D or bring it back again
           if(miniMapOn)
             scene.z = 1
           else
             scene.z = -1
         }
       }
Qt_Technology_Partner_RGB_475 Qt_Service_Partner_RGB_475_padded