ParallaxScrollingBackground2.qml Example File

demos/ChickenOutbreak2/qml/ParallaxScrollingBackground2.qml
 import Felgo 3.0
 import QtQuick 2.0

 Item {

   id: bgi
   objectName: "parallaxScrollingBackgroundItem"

   /*!
     The sourceImage property is used to provide the image data for the background. This needs to be set accordingly or the ParallaxScrollingBackground will not work correctly.
     */
   property string sourceImage: ""
   property url filenameImage: ""

   /*!
     The sourceImage2 property is not always used. It can be used to provide an additional second image which will alternate with the sourceImage during scrolling. If the property sourceImage2 is left empty, the first image from the property sourceImage will be used and mirrored automatically.
     */
   property string sourceImage2: ""
   property url filenameImage2: ""

   /*!
     The image alias can be used to retrieve the size of the used MultiResolutionImage. This is neccessary if the ParallaxScrollingBackground should be centered in the scene.

     \qml
     import Felgo 3.0
     import QtQuick 2.0

     GameWindow {

       Scene {
         ParallaxScrollingBackground {
           id: pxBg
           sourceImage: "img/background-wood2-sd.png"
           x: -(pxBg.image.width-scene.width)/2
           y: -(pxBg.image.height-scene.height)/2
           mirrorSecondImage: false
           movementVelocity: Qt.point(-75, 0)
         }
       } // end of Scene

     } // end of GameWindow
     \endqml
     */
   property alias image:mt1

   /*!
     Set this property if you want to mirror the sourceImage2 image. By default, if you only have specified the sourceImage property but no sourceImage2 property, the second image will be mirrored. If you explicitly set the sourceImage2 property, it will not be mirrored.

     The mirroring is performed automatically based on the movementVelocity: if you set its x property, the image will be mirrored horizontally, or vertically when the y property is set.
    */
   property bool mirrorSecondImage: sourceImage2==""

   /*! \internal just for making calculations shorter*/
   property int __totalWidth: (mt1.width + mt2.width)*2*__inversRatio.x
   /*! \internal */
   property int __totalHeight: (mt1.height + mt2.height)*2*__inversRatio.y

   /*!
     The movementVelocity property is used to set the direction and speed of the movement.
     \list
       \li Left achieved with movementVelocity: Qt.point(10,0)
       \li Right achieved with movementVelocity: Qt.point(-10,0)
       \li Up achieved with movementVelocity: Qt.point(0,-10)
       \li Down achieved with movementVelocity: Qt.point(0,10)
     \endlist
   */
   property variant movementVelocity: Qt.point(0,0)

   /*!
     The ratio property is used to set how much shift should occur between parent and image.
     \list
       \li Left/Right faster than parent achieved with ratio: Qt.point(1.5,1.0)
       \li Left/Right slower than parent achieved with ratio: Qt.point(0.5,1.0)
       \li Up/Down faster than parent achieved with ratio: Qt.point(1.0,1.5)
       \li Up/Down slower than parent achieved with ratio: Qt.point(1.0,0.5)
     \endlist
   */
   property variant ratio: Qt.point(1.0,1.0)

   /*!
     \internal
     Inverse ratio used for positioning accordingly.
   */
   property variant __inversRatio: Qt.point(1/ratio.x,1/ratio.y)

   /*!
     If set to \c true, the background images will scroll with the specified movementVelocity. If set to to \c false, the scrolling will pause. By default, it is set to \c true.
    */
   property bool running: externalMovementAnimation ? externalMovementAnimation.target ? false : true : true

   /*! \internal */
   function resetBackgrounds() {
     movingItem.x = 0
     movingItem.y = 0
     parallaxItem1.x = 0
     parallaxItem1.y = 0
     parallaxItem1.limiterX = mt1.width*1.5*__inversRatio.x
     parallaxItem1.limiterY = mt1.height*1.5*__inversRatio.y

     parallaxItem2.x = movementVelocity.x ? (movementVelocity.x>0 ? -mt1.width*__inversRatio.x : mt1.width*__inversRatio.x) : 0
     parallaxItem2.y = movementVelocity.y ? (movementVelocity.y>0 ? -mt1.height*__inversRatio.y : mt1.height*__inversRatio.y) : 0
     parallaxItem2.limiterX = (mt1.width+mt2.width*1.5)*__inversRatio.x
     parallaxItem2.limiterY = (mt1.height+mt2.height*1.5)*__inversRatio.y

     parallaxItem3.x = movementVelocity.x ? (movementVelocity.x>0 ? -(mt1.width+mt2.width)*__inversRatio.x : (mt1.width+mt2.width)*__inversRatio.x ) : 0
     parallaxItem3.y = movementVelocity.y ? (movementVelocity.y>0 ? -(mt1.height+mt2.height)*__inversRatio.y : (mt1.height+mt2.height)*__inversRatio.y ) : 0
     parallaxItem3.limiterX = (mt1.width*2.5+mt2.width)*__inversRatio.x
     parallaxItem3.limiterY = (mt1.height*2.5+mt2.height)*__inversRatio.y

     parallaxItem4.x = movementVelocity.x ? (movementVelocity.x>0 ? -(mt1.width*2+mt2.width)*__inversRatio.x : (mt1.width*2+mt2.width)*__inversRatio.x ) : 0
     parallaxItem4.y = movementVelocity.y ? (movementVelocity.y>0 ? -(mt1.height*2+mt2.height)*__inversRatio.y : (mt1.height*2+mt2.height)*__inversRatio.y ) : 0
     parallaxItem4.limiterX = (mt1.width*2.5+mt2.width*2)*__inversRatio.x
     parallaxItem4.limiterY = (mt1.height*2.5+mt2.height*2)*__inversRatio.y
   }

   // move the movingPlatform with constant speed right/left to see the parallax effect if movementVelocity.x is set
   MovementAnimation {
     target: movingItem
     property: "x"
     velocity: movementVelocity.x
     running: bgi.running
   }

   // move the movingPlatform with constant speed down/upwards to see the parallax effect if movement movementVelocity.y is set
   MovementAnimation {
     target: movingItem
     property: "y"
     velocity: movementVelocity.y
     running: bgi.running
   }

   property variant externalMovementAnimation

   Connections {
     target: externalMovementAnimation ? externalMovementAnimation.target ? externalMovementAnimation.target : null : null

     onXChanged: {
       movingItem.x = externalMovementAnimation.target.x
     }
     onYChanged: {
       movingItem.y = externalMovementAnimation.target.y
     }
   }

   // do not move the root item directly, becaus then a use-case like in ChickenOutbreak (see file Level.qml) would not be possible:
   // when the parent of the ParallaxScrollingBackground item is scrolling (like the whole level), then the position must be reverted
   // thus a second item which is used for the internal movement is required
   Item {
     id: movingItem

     /*!
       \internal
       Moves the picture horizontal to the other edge once the limiter was reached.
      */
     onXChanged:  {
       if(typeof movementVelocity != "undefined" ) {
         //      console.debug("x:", x, ", inversRatioX:", __inversRatio.x)
         //      console.debug("parallaxItem1 limterX:", parallaxItem1.limiterX, ", mt1.width:", mt1.width)
         //      console.debug("parallaxItem2 limterX:", parallaxItem2.limiterX, ", mt2.width:", mt2.width)
         //      console.debug("parallaxItem3 limterX:", parallaxItem3.limiterX, ", mt3.width:", mt3.width)
         //      console.debug("parallaxItem4 limterX:", parallaxItem4.limiterX, ", mt4.width:", mt4.width)

         if(movementVelocity.x>0) { // Moving Left
           if(x > parallaxItem1.limiterX) { // Move first picture left to the second picture
             parallaxItem1.x -= __totalWidth // calculate position
             parallaxItem1.limiterX += __totalWidth // Move the limiter forward to the next border

             //          console.debug("SHIFT! - new x value of parallax1:", parallaxItem1.x, ", new limiterX1:", mt1.limiterX)
           }
           if(x > parallaxItem2.limiterX) { // Move second picture left to the first picture
             parallaxItem2.x -= __totalWidth
             parallaxItem2.limiterX += __totalWidth

             //          console.debug("SHIFT! - new x value of parallax2:", parallaxItem2.x, ", new limiterX2:", mt2.limiterX)
           }
           if(x > parallaxItem3.limiterX) {
             parallaxItem3.x -= __totalWidth
             parallaxItem3.limiterX += __totalWidth
             //          console.debug("SHIFT! - new x value of parallax3:", parallaxItem3.x, ", new limiterX3:", mt3.limiterX)
           }
           if(x > parallaxItem4.limiterX) {
             parallaxItem4.x -= __totalWidth
             parallaxItem4.limiterX += __totalWidth
             //          console.debug("SHIFT! - new x value of parallax4:", parallaxItem4.x, ", new limiterX4:", mt4.limiterX)
           }
         } else { // Moving Right
           if(x < parallaxItem1.limiterX*(-1)) { // Move first picture right to the second picture
             parallaxItem1.x += __totalWidth
             parallaxItem1.limiterX += __totalWidth
           }
           if(x < parallaxItem2.limiterX*(-1)) { // Move second picture right to the first picture
             parallaxItem2.x += __totalWidth
             parallaxItem2.limiterX += __totalWidth
           }
           if(x < parallaxItem3.limiterX*(-1)) { // Move second picture right to the first picture
             parallaxItem3.x += __totalWidth
             parallaxItem3.limiterX += __totalWidth
           }
           if(x < parallaxItem4.limiterX*(-1)) { // Move second picture right to the first picture
             parallaxItem4.x += __totalWidth
             parallaxItem4.limiterX += __totalWidth
           }
         }
       }
     }

     /*!
       \internal
       Moves the picture vertical to the other edge once the limiter was reached.
     */
     onYChanged:  {
       if(typeof movementVelocity != "undefined" ) {
         //      console.debug("y:", y, ", inversRatio y:", __inversRatio.y, ", movementVelocity y:", movementVelocity.y)
         //      console.debug("parallaxItem1 limterY:", parallaxItem1.limiterY, ", mt1.width:", mt1.width)
         //      console.debug("parallaxItem2 limterY:", parallaxItem2.limiterY, ", mt2.width:", mt2.width)
         //      console.debug("parallaxItem3 limterY:", parallaxItem3.limiterY, ", mt3.width:", mt3.width)
         //      console.debug("parallaxItem4 limterY:", parallaxItem4.limiterY, ", mt4.width:", mt4.width)

         if(movementVelocity.y >0) { // Moving Down
           if(y > parallaxItem1.limiterY) {
             parallaxItem1.y -= __totalHeight // calculate position
             parallaxItem1.limiterY += __totalHeight // Move the limiter forward to the next border
           }
           if(y > parallaxItem2.limiterY) {
             parallaxItem2.y -= __totalHeight
             parallaxItem2.limiterY += __totalHeight
           }
           if(y > parallaxItem3.limiterY) {
             parallaxItem3.y -= __totalHeight
             parallaxItem3.limiterY += __totalHeight
           }
           if(y > parallaxItem4.limiterY) {
             parallaxItem4.y -= __totalHeight
             parallaxItem4.limiterY += __totalHeight
           }
         } else { // Moving Up
           if(y < parallaxItem1.limiterY*(-1)) {
             parallaxItem1.y += __totalHeight
             parallaxItem1.limiterY += __totalHeight
           }
           if(y < parallaxItem2.limiterY*(-1)) {
             parallaxItem2.y += __totalHeight
             parallaxItem2.limiterY += __totalHeight
           }
           if(y < parallaxItem3.limiterY*(-1)) {
             parallaxItem3.y += __totalHeight
             parallaxItem3.limiterY += __totalHeight
           }
           if(y < parallaxItem4.limiterY*(-1)) {
             parallaxItem4.y += __totalHeight
             parallaxItem4.limiterY += __totalHeight
           }
         }
       }
     }

     // first background image
     ParallaxItem {
       id: parallaxItem1
       objectName: "parallaxItem"
       ratio: bgi.ratio

       // The property limiterX is used to set the next switch limit of the image in this parallaxItem
       property real limiterX: mt1.width*1.5*__inversRatio.x
       // The property limiterX is used to set the next switch limit of the image in this parallaxItem
       property real limiterY: mt1.height*1.5*__inversRatio.y

       /*Text {
         text: "PX1 =="
         color: "white"
       }*/

       SingleSpriteFromFile {
         id: mt1
         source: sourceImage
         filename: filenameImage
         translateToCenterAnchor: false
       }
     }

     // second background image (on the right side of the first one)
     ParallaxItem {
       id: parallaxItem2
       objectName: "parallaxItem2"
       ratio: bgi.ratio

       x: movementVelocity.x ? (movementVelocity.x>0 ? -mt1.width*__inversRatio.x : mt1.width*__inversRatio.x) : 0
       y: movementVelocity.y ? (movementVelocity.y>0 ? -mt1.height*__inversRatio.y : mt1.height*__inversRatio.y) : 0

       property real limiterX: (mt1.width+mt2.width*1.5)*__inversRatio.x
       property real limiterY: (mt1.height+mt2.height*1.5)*__inversRatio.y

       /*Text {
         text: "PX2 ==== "
         color: "white"
       }*/

       SingleSpriteFromFile {
         id: mt2
         mirrorX: movementVelocity.x ? mirrorSecondImage : false
         mirrorY: movementVelocity.y ? mirrorSecondImage : false
         source: bgi.sourceImage2=="" ? bgi.sourceImage : bgi.sourceImage2
         filename: bgi.filenameImage2=="" ? bgi.filenameImage : bgi.filenameImage2
         translateToCenterAnchor: false
       }
     }

     // third background image (on the left side of the first one)
     ParallaxItem {
       id: parallaxItem3
       objectName: "parallaxItem3"
       ratio: bgi.ratio

       x: movementVelocity.x ? (movementVelocity.x>0 ? -(mt1.width+mt2.width)*__inversRatio.x : (mt1.width+mt2.width)*__inversRatio.x ) : 0
       y: movementVelocity.y ? (movementVelocity.y>0 ? -(mt1.height+mt2.height)*__inversRatio.y : (mt1.height+mt2.height)*__inversRatio.y ) : 0

       property real limiterX: (mt1.width*2.5+mt2.width)*__inversRatio.x
       property real limiterY: (mt1.height*2.5+mt2.height)*__inversRatio.y

       /*Text {
         text: "PX3 ======"
         color: "white"
       }*/

       SingleSpriteFromFile {
         id: mt3
         mirrorX: movementVelocity.x ? mirrorSecondImage : false
         mirrorY: movementVelocity.y ? mirrorSecondImage : false
         source: sourceImage
         filename: filenameImage
         translateToCenterAnchor: false
       }
     }

     ParallaxItem {
       id: parallaxItem4
       objectName: "parallaxItem4"
       ratio: bgi.ratio

       x: movementVelocity.x ? (movementVelocity.x>0 ? -(mt1.width*2+mt2.width)*__inversRatio.x : (mt1.width*2+mt2.width)*__inversRatio.x ) : 0
       y: movementVelocity.y ? (movementVelocity.y>0 ? -(mt1.height*2+mt2.height)*__inversRatio.y : (mt1.height*2+mt2.height)*__inversRatio.y ) : 0

       property real limiterX: (mt1.width*2.5+mt2.width*2)*__inversRatio.x
       property real limiterY: (mt1.height*2.5+mt2.height*2)*__inversRatio.y

       /*Text {
         text: "PX4 ========"
         color: "white"
       }*/

       SingleSpriteFromFile {
         id: mt4
         mirrorX: movementVelocity.x ? mirrorSecondImage : false
         mirrorY: movementVelocity.y ? mirrorSecondImage : false
         source: bgi.sourceImage2=="" ? bgi.sourceImage : bgi.sourceImage2
         filename: bgi.filenameImage2=="" ? bgi.filenameImage : bgi.filenameImage2
         translateToCenterAnchor: false
       }
     }
   }// Moving Item
 }

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