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

Forums

OverviewFelgo 3 Support (Qt 5) › My code on the Flappy Bird clone

Viewing 4 posts - 1 through 4 (of 4 total)
  • Author
    Posts
  • #15505

    Isak

    Hello,

    I followed the Flappy Birds tutorial but wanted to improve it a little, would be great if i could get some comments about my code. As this is my first project i most certainly could code this better, one thing i was thinking of was that i maybe have more properties than i need? It has the functionality that i want though:) I dont know if i am allowed to post all my qml here like this? Hoping its ok?

    main.qml

    import Felgo 3.0
    import QtQuick 2.0
    
    GameWindow {
      id: gameWindow
    
    
      activeScene: scene
    
      // the size of the Window can be changed at runtime by pressing Ctrl (or Cmd on Mac) + the number keys 1-8
      // the content of the logical scene size (480x320 for landscape mode by default) gets scaled to the window size based on the scaleMode
      // you can set this size to any resolution you would like your project to start with, most of the times the one of your main target device
      screenWidth: 320
      screenHeight: 480
    
    
    
      EntityManager {
        id: entityManager
      }
    
      Scene {
    
        property string gameState: "gameOver"
        property int score: 0
        property bool firstRun: true
        property bool isDead: false
    
        id: scene
    
        PhysicsWorld {
            id: world
            running: scene.firstRun == false
            debugDrawVisible: false
            z: 1000
            gravity.y: 27
    
        }
    
    
        sceneAlignmentY: "bottom"
    
        // the "logical size" - the scene content is auto-scaled to match the GameWindow size
        width: 320
        height: 480
    
        Image {
            id: bg
            source: "../assets/bg.png"
            anchors.horizontalCenter: scene.horizontalCenter
            anchors.bottom: scene.gameWindowAnchorItem.bottom
        }
    
    
        Pipe {
            id: pipe1
            x: 400
            y: 30 + Math.random()*200
        }
    
        Pipe {
            id: pipe2
            x: 640
            y: 30 + Math.random()*200
        }
    
        Ground {
            anchors.horizontalCenter: scene.horizontalCenter
            anchors.bottom: scene.gameWindowAnchorItem.bottom
        }
    
        Player {
            id: player
            x: 160
            y:180
        }
    
        Text {
            text: scene.score
            color: "white"
            anchors.horizontalCenter: scene.horizontalCenter
            y: 30
            font.pixelSize: 30
        }
    
    
        Rectangle {
            id: playButton
            visible: true
            enabled: true
            y: 240
            x: 110
            width: 100
            height: 50
            color: "transparent"
            border.color: "transparent"
            border.width: 5
            z: 1000
    
    
            Text {
                id: playText
                text: "Play"
                color: "white"
                font.pixelSize: 60
                anchors.centerIn: parent
            }
    
        }
    
        MouseArea {
            id: touchPlay
    
            enabled: true
            anchors.fill: playButton
            onPressed: {
                if(scene.gameState == "gameOver") {
                    scene.startGame()
                    player.push()
                } else if(scene.gameState == "play") {
                    player.push()
                }
            }
        }
    
    
        MouseArea {
            id: touch
            enabled: false
            anchors.fill: scene.gameWindowAnchorItem
            onPressed: {
                if(scene.gameState == "play") {
                    player.push()
                }
    
    
            }
        }
    
    
        function startGame () {
            scene.gameState = "play"
            pipe1.x = 400
            pipe1.y = 30 + Math.random()*200
            pipe2.x = 640
            pipe2.y = 30 + Math.random()*200
            player.x = 160
            player.y = 180
            scene.score = 0
    
            touch.enabled = true
            playButton.enabled = false
            playButton.visible = false
            playButton.y = -50
            firstRun = false
            scene.isDead = false
    
    
      }
    
        function stopGame() {
            scene.gameState = "gameOver"
            scene.isDead = true
            touch.enabled = false
            playButton.enabled = true
            playButton.visible = true
            anim.running = true
            playButton.y = -50
            anim.restart()
    
    
    
        }
    
    
            NumberAnimation {
                id: anim
                running: false
                target: playButton
                property: "y"
                duration: 200
                to: 200
                easing.type: Easing.InOutQuad
    
            }
    
    
    
    
    
    
    
    
    
      }
    }
    
    

    Ground.qml

    import Felgo 3.0
    import QtQuick 2.0
    
    EntityBase {
    
        id: "ground"
        entityType: "ground"
        width: sprite.width
        height: sprite.height
    
    
        SpriteSequenceVPlay {
            id: sprite
    
            running: scene.gameState != "gameOver"
    
            SpriteVPlay {
                frameCount: 2
                frameRate: 4
                frameWidth: 368
                frameHeight: 90
                source: "../assets/land.png"
            }
        }
    
        BoxCollider {
            anchors.fill: parent
            bodyType: Body.Static
            fixture.onBeginContact: {
    
                if(scene.gameState == "play") {
                    scene.stopGame()
                }
    
    
            }
        }
    
    }
    

    Pipe.qml

    import Felgo 3.0
    import QtQuick 2.0
    
    EntityBase {
        id: pipe
        entityType: "pipe"
    
        height: 90
        width: 1
    
        Image {
            id: lowerPipe
            source: "../assets/pipe.png"
            anchors.top: pipe.bottom
            anchors.horizontalCenter: pipe.horizontalCenter
        }
    
        Image {
            id: upperPipe
            source: "../assets/pipe.png"
            mirror: true
            rotation: 180
            anchors.bottom: pipe.top
            anchors.horizontalCenter: pipe.horizontalCenter
        }
    
    
        BoxCollider {
            anchors.fill: pipe
            bodyType: Body.Static
            collisionTestingOnlyMode: true
            fixture.onBeginContact: {
                scene.score++
            }
        }
    
        BoxCollider {
            anchors.fill: lowerPipe
            bodyType: Body.Static
            collisionTestingOnlyMode: true
            fixture.onBeginContact: {
                if(scene.gameState == "play") {
                    scene.stopGame()
                }
            }
        }
    
        BoxCollider {
            anchors.fill: upperPipe
            bodyType: Body.Static
            collisionTestingOnlyMode: true
            fixture.onBeginContact: {
                if(scene.gameState == "play") {
                    scene.stopGame()
                }
            }
        }
    
    
        MovementAnimation {
            id: movement
            target: pipe
            property: "x"
            minPropertyValue: -80
            velocity: -120
            running: scene.gameState != "gameOver"
            onLimitReached: {
                pipe.x = 400
                pipe.y = 30 + Math.random()*200
            }
        }
    
    
    
    }
    

    Player.qml

    import Felgo 3.0
    import QtQuick 2.0
    
    EntityBase {
    
        id: "player"
        entityType: "player"
        width: 26
        height: 26
    
        SpriteSequenceVPlay {
            id: bird
    
    
            running: scene.isDead == false
            rotation: collider.linearVelocity.y / 10
    
    
            anchors.centerIn: parent
    
    
            SpriteVPlay {
                frameCount: 3
                frameRate: 10
                frameWidth: 34
                frameHeight: 24
                source: "../assets/bird.png"
            }
        }
    
        CircleCollider {
            id: collider
            radius: 5
    
        }
    
        function push() {
            collider.body.linearVelocity = Qt.point(0,0)
            var localForwardVector = collider.body.toWorldVector(Qt.point(0, -280))
            collider.body.applyLinearImpulse(localForwardVector, collider.body.getWorldCenter())
        }
    
    
    }
    

    /Isak

     

    #15514

    Günther
    Felgo Team

    Hi Isak!

    For a small project like that it’s ok to post your code – if you want to share bigger projects I would recommend using e.g. using github.

    As it is a small project it’s totally ok to not have everything structured perfectly (I think it looks quite good actually). But here are a few things that came to my mind when quickly reading over the code:

    • It’s always a good idea to see game entities as individual types that should not rely too much on global properties. For example the isDead state of the Player is defined by scene.isDead. Imagine you have many entities in a big project – if you have much references like these between different items and entities it can get a bit confusing. It might be a good idea to think about adding properties and signals to the entities for a clear interface to other components.
    • Creating reusable components: I would also recommend to create reusable components with clear interfaces not only for game entities but also for other UI elements, like the play button. You can for example create a button with a Rectangle, Text/Image and MouseArea and reuse it in different scenes. The actual button-text and onClicked actions can be different every-time for each button if you simply add some properties and signals.
    • Multiple Scenes: At the moment you only have one scene for the different screens like menu, in-game or game-over. Using Multiple Scenes helps to better structure your project, have a look this tutorial for a guide: Creating mobile games with multiple scenes and levels.
    • Properties, Property Bindings and Signals: These are main core concepts when programming in QML and when correctly used they become really powerful also for keeping your code simple and well structured. A small example: At the moment you manually disable and hide the play button – you could also use a property binding to automatically show or hide them depending on the current game state. If you learn to understand and correctly use these concepts, I’m sure your projects will also be better structured without much additional effort.

    Hope this feedback helps you or other newcomers 😉

    Cheers,
    Günther

     

    #15524

    Isak

    Hello Gunther and thanks for the answers=)

    The game i work on dont work good with scenes, because i want it to be only on one screen. Its an space shooter where the space(stars) scroll in the background and first i animate in the menus and when you press play they animate out and the “real” game animates in. Hope you understand how i intend it to work.

     

    About the Resuable components, lets say i have a button that i animate in. Should the animation be in the button qml or in main.qml? The most logic for me would be that its in the main and in the button.qml i only have the button itself. Or am i wrong thinking like that?

     

    Thanks

     

    /Isak

    #15534

    Günther
    Felgo Team

    Hi Isak!

    Yes I understand, you do not need a dedicated menu scene as it makes more sense for that game/look you are going for. How you structure your project is of course also a matter of taste and depends on the use-case – so there’s no real “right or wrong” in my opinion.

    For the buttons: If all your menu-buttons should animate in the same way, it makes sense to add the animation to the button as you can avoid redundant definitions of the animation for every button instance. If you want to fade in/out all your buttons as a group, you can also create an additional wrapper item which holds all buttons (without animations) and adds the animation to all of them.

    The main reason why I mentioned the points above is that I personally like to keep my code simple and group items and logic that belong together in sub-components – even if some items aren’t even used multiple times in different places.

    Cheers,
    Günther

Viewing 4 posts - 1 through 4 (of 4 total)

RSS feed for this thread

You must be logged in to reply to this topic.

Qt_Technology_Partner_RGB_475 Qt_Service_Partner_RGB_475_padded