GameScene.qml Example File

demos/StackTheBoxWithCommunityEditor/qml/scene-game/GameScene.qml
 import Felgo 3.0
 import QtQuick 2.0
 import QtSensors 5.3
 import "../common"
 import "../entities"

 SceneBase {
   id: gameScene

   property alias itemEditor: itemEditor

   // gets increased when a new box is created, and reset to 0 when a new game is started
   // start with 1, because initially 1 Box is created
   property int createdBoxes: 1

   // this is the required minimum distance from the left and from the right (the scene.width)
   // when the box is rotated at 90°, its distance from the center is box1.width*Sqrt2, because width and height are the same
   // the hint about this issue was kindly provided by Martin Eigel
 //  property real safetyDistance: -1
 //  onSafetyDistanceChanged: console.debug("safetyDistance "+safetyDistance)

   // gets set by Obstacle
   property variant selectedEntity: null

   property string cameFromScene

   // gets increased with every box
   property int score

   // is emitted when gameOver() is called; can be handled to increase the gameOver counter for achievements
   signal gameLost

   // state: "levelEditing" enables dragging and clicking of obstacles
   // change the state in the next line to start in levelEditing mode:
   // the gameScene state can either be "playing", "testing" or "levelEditing"
   //state: "playing"
   state: "levelEditing"

   onBackButtonPressed: {
     gameScene.stopGame()
     if(cameFromScene === "menu") {
       mainItem.state = "menu"
     } else {
       mainItem.state = "selectLevel"
     }
   }

   onStateChanged: {
     if(state === "levelEditing") {
       resetGame()
     }
   }

   // called from the LevelScene or when play is pressed in MainMenuScene
   function goToPlayMode() {
     state = "playing"

     // this is required so the highscore value is correct for gameNetwork.userHighscoreForCurrentActiveLeaderboard in the GameHUD
     // also, with this the leaderboard of this level is shown when opening the GameNetworkView from the main menu
     if(levelEditor.currentLevelId)
       gameNetwork.currentActiveLeaderboard = levelEditor.currentLevelId.toString()
   }

   function goToLevelEditingMode() {
     state = "levelEditing"
   }

   function entitySelected(entity) {
     // reset the internal state of the old selected entity, to stop blinking of the selected obstacle
     if(selectedEntity && selectedEntity !== entity) {
       selectedEntity.entityState = ""
     }
     selectedEntity = entity
     editEntityOverlay.obstacle = entity
   }

   function resetGame() {
     // remove all entities of type "box", but not the walls
     entityManager.removeEntitiesByFilter(["box"]);
     // reset the createdBoxes amount
     gameScene.createdBoxes = 0;

     // reset the interval levels to ther start values
     boxTimer.restart()
     spawnBoxItem.resetBar()
     score = 0
   }

   // is called if the back button is pressed in GameHUD
   function stopGame() {
     resetGame()
     state = "stopped"
   }

   // is called when the
   function gameOver() {
     audioManager.play(audioManager.idGAMEOVER)
     gameLost()

     // in training mode, the currentLevelId is 0 and no highscore should be reported because there is no leaderboard for it
     // if in state testing (when testing the own level) this should also not be entered because no highscore should be submitted during testing
     if(gameScene.state === "playing" && levelEditor.currentLevelId) {

       console.debug("current levelId for highscore:", levelEditor.currentLevelId)
       // if the level was started from the authorGeneratedLevels, the levelId is the local id and not the publishedLevelId!
       if(levelEditor.currentLevelStorageLocation === levelEditor.authorGeneratedLevelsLocation) {
         // this branch is only entered if the level was started to play (and not testing!) from the My Levels tab; so in that case, publishedLevelId must exist as otherwise the play mode cannot be entered
         gameNetwork.reportScore(score, levelEditor.currentLevelData.levelMetaData.publishedLevelId)
       } else if(levelEditor.currentLevelStorageLocation === levelEditor.userGeneratedLevelsLocation) {
         gameNetwork.reportScore(score, levelEditor.currentLevelId)
       } else if(levelEditor.currentLevelStorageLocation === levelEditor.downloadedLevelsLocation) {
         gameNetwork.reportScore(score, levelEditor.currentLevelId)
       }

       // ADD: connect to the onNewHighscore() signal of FelgoGameNetwork to check if the user has reached a new one
     }

     // ADD: show a gameover overlay here and don't restart instantly, with the info if a new highscore is reached

     resetGame()

   }

   MultiResolutionImage {
     source: "../../assets/img/menubg.png"
     anchors.centerIn: parent
   }

   MultiResolutionImage {
     // Apple appstore guidlines don't alow signs of mobiles on the screen of desktop applications.
     source: system.desktopPlatform ? "../../assets/img/bg-deco-desktop.png" : "../../assets/img/bg-deco.png"
     anchors.centerIn: parent
   }

   Item {
     width: 210
     anchors.right: parent.right
     anchors.rightMargin: 20
     y: 40
     ResponsiveText {
       text: levelEditor.currentLevelNameString
       anchors.right: parent.right
       color: "#185010"
       font.pixelSize: 25
       font.family: fontHUD.name
     }
   }

   Text {
     x: 58
     y: 20
     width: parent.width
     // don't show the current best highscore for this level, but rather the current score
     //text: "Score: " + gameNetwork.userHighscoreForCurrentActiveLeaderboard
     text: "Score: " + score
     color: "#185010"
     font.pixelSize: 19
     // do show in testing and play mode
     visible: gameScene.state !== "levelEditing"
     font.family: fontHUD.name

   }

   // display the amount of stacked boxes
 //  Text {
 //    x: 58
 //    y: 37
 //    width: parent.width
 //    text: "Boxes: " + gameScene.createdBoxes
 //    color: "#185010"
 //    font.pixelSize: 12
 //    visible: gameScene.state !== "levelEditing"
 //    font.family: fontHUD.name
 //  }

   GameHUD {
     id: gameHUD
     // put it on top of the gameScene contents
     z:1
   }

   ItemEditor {
     id: itemEditor
     opacity: 0.9
     z:1
     // start invisible
     visible: false
     // anchor box on the right bottom side
     anchors.right: parent.gameWindowAnchorItem.right
     anchors.bottom: parent.gameWindowAnchorItem.bottom
     // use 2/3 of parent height
     height: parent.height*2/3
   }

   Accelerometer {
     id: accelerometer
     active: !system.desktopPlatform && (gameScene.state === "playing" || gameScene.state === "testing")
     onReadingChanged: {
       world.gravity.x = reading.y*2
     }
   }

   PhysicsWorld {
     id: world
     gravity.y: 9.81 // make the objects fall faster
     z: 10 // draw the debugDraw on top of the entities

     // turn on when debugging physics
     debugDrawVisible: false

     // these are performance settings to avoid boxes colliding too far together
     // set them as low as possible so it still looks good
     updatesPerSecondForPhysics: 60
     velocityIterations: 5
     positionIterations: 5
   }

   Component {
     id: mouseJoint
     MouseJoint {
       // make this high enough so the box with its density is moved quickly
       maxForce: mainItem.maxForce * world.pixelsPerMeter
       // The damping ratio. 0 = no damping, 1 = critical damping. Default is 0.7
       dampingRatio: mainItem.dampingRatio
       // The response speed, default is 5
       frequencyHz: mainItem.frequencyHz
     }
   }

   // when the user presses a box, move it towards the touch position
   MouseArea {
     anchors.fill: parent

     property Body selectedBody: null
     property MouseJoint mouseJointWhileDragging: null

     onPressed: {

       // set the state of the entitySelection of the selectedEntity to "", otherwise the fade animation is continuously played
       // this stops blinking of the old selected obstacle
       gameScene.entitySelected(null)

       selectedBody = world.bodyAt(Qt.point(mouseX, mouseY));
       console.debug("selected body at position", mouseX, mouseY, ":", selectedBody);
       // if the user selected a body, this if-check is true
       if(selectedBody) {
         // create a new mouseJoint
         mouseJointWhileDragging = mouseJoint.createObject(world)

         // set the target position to the current touch position (initial position)
         mouseJointWhileDragging.target = Qt.point(mouseX, mouseY)

         // connect the joint with the body
         mouseJointWhileDragging.bodyB = selectedBody
       }
     }

     onPositionChanged: {
       // this check is necessary, because the user might also drag when no initial body was selected
       if (mouseJointWhileDragging)
         mouseJointWhileDragging.target = Qt.point(mouseX, mouseY)
     }
     onReleased: {
       // if the user pressed a body initially, remove the created MouseJoint
       if(selectedBody) {
         selectedBody = null
         if (mouseJointWhileDragging)
           mouseJointWhileDragging.destroy()
       }
     }
   }

   Ground {
     height: 16
     anchors {
       bottom: gameScene.bottom
       left: gameScene.left
       right: gameScene.right
     }
   }

   Wall {
     id: leftWall
     width: 48
     height: gameScene.height
     anchors {
       left: gameScene.left
     }
   }

   Wall {
     width: 16
     height: gameScene.height
     anchors {
       right: gameScene.right
     }
   }
   Ceiling {
     height: 16
     width: gameScene.width
     anchors {
       top: gameScene.top
       left: gameScene.left
       leftMargin: 32
     }
     onCollidedWithBox: {
       gameOver()
     }
   }

   Item {
     id: spawnBoxItem
     width: 32
     height: gameScene.height - 16
     y: 16
     Image {
       id: spawnBoxBar
       //color: "#ff7000"
       source: "../../assets/img/spawnbar.png"
       anchors.bottom: parent.bottom
       anchors.bottomMargin: 16
       width: parent.width
       height: parent.height
     }

     Timer {
       id: spawnBoxBarTimer
       interval: 25
       running: gameScene.state === "playing" || gameScene.state === "testing"
       repeat: true
       onTriggered: {
         spawnBoxBar.height -= spawnBoxItem.height / (spawnInterval / spawnBoxBarTimer.interval)
       }
     }

     Timer {
       id: spawnBarLockTimer
       interval: 350
     }

     function resetBar() {
       spawnBoxBar.height = spawnBoxItem.height
     }

     MouseArea {
       anchors.fill: parent
       enabled: gameScene.state === "playing" || gameScene.state === "testing"
       onClicked: {
         if(spawnBarLockTimer.running) return
         var bonus = Math.floor(5*spawnBoxBar.height/spawnBoxItem.height)
         gameScene.spawnBox(bonus)
         flurry.logEvent("Game.InstantBox")
       }
     }
   }

   Timer {
     id: boxTimer
     interval: spawnInterval
     running: gameScene.state === "playing" || gameScene.state === "testing"// start running from the beginning, when the scene is loaded
     repeat: true // otherwise restart wont work

     onTriggered: {
       gameScene.spawnBox(0)
     }
   }

   function spawnBox(bonusScore) {
     spawnBarLockTimer.start()
     spawnBoxItem.resetBar()
     spawnBoxBarTimer.restart()
     boxTimer.restart()
     var newEntityProperties = {
       // vary x between [ safetyZoneHoriztonal ... width-safetyZoneHoriztonal]
       x: utils.generateRandomValueBetween(68, gameScene.width-36),
       y: 48, // position on top of the scene, at least below the top wall
       rotation: Math.random()*360
     }

     entityManager.createEntityFromUrlWithProperties(
           Qt.resolvedUrl("../entities/Box.qml"),
           newEntityProperties);

     audioManager.play(audioManager.idSPAWN)

     // increase the createdBoxes number
     gameScene.createdBoxes++
     // increase highscore with every box
     score += 5 + bonusScore
   }

   Connections {
     target: mainItem
     onSpawnIntervalChanged: {
       if(gameScene.state == "testing") {
         spawnBoxItem.resetBar()
         spawnBoxBarTimer.restart()
         boxTimer.restart()
       }
     }
   }

   EditEntityOverlay {
     id: editEntityOverlay
     z: 100
   }

 }// GameScene

Voted #1 for:

  • Easiest to learn
  • Most time saving
  • Best support

Develop Cross-Platform Apps and Games 50% Faster!

  • Voted the best supported, most time-saving and easiest to learn cross-platform development tool
  • Based on the Qt framework, with native performance and appearance on all platforms including iOS and Android
  • Offers a variety of plugins to monetize, analyze and engage users
FREE!
create apps
create games
cross platform
native performance
3rd party services
game network
multiplayer
level editor
easiest to learn
biggest time saving
best support