Hi,
So now I am trying to figure out how to make a sprite play upon an event.
How would I make a bomb play my explosive sprite upon player colliding?
Hi,
So now I am trying to figure out how to make a sprite play upon an event.
How would I make a bomb play my explosive sprite upon player colliding?
Hi Todd,
In general there are few ways to do it.
But I think the flow is something like this, most of the time:
In your collider(the one one the player entity or bomb) you create event listener(singal listener in Qt case), and then either trigger another signal or call your custom handler explicitly.
Later, in your handler, you change your bomb sprites.
To create signal listener on collider check http://felgo.com/doc/vplay-boxcollider/, look for onBeginContact
, there you can execute some action on contact
To dynamically change animation for sprite, between “states” I strongly recommend to read this http://felgo.com/doc/vplay-spritesequencevplay/
In general, use SpriteSequenceVPlay
where you switch between animation “states”.
Check the docs I put, there are very good examples which cover basic and advanced usage.
There is no point to put any code because it would be almost 1:1 with the examples in the docs, at least in my case.
PS. I strongly recommend to read this http://felgo.com/doc/vplay-qml-communication-basics/, how to communicate between entities and which way to use, and when.
Hey thanks for the help,
I’m just now getting to this, been super busy with school.
So I am going in and trying to set an event handler but I cant quite get it to work…
Basically in my game I just want to set a sprite to play on collision when the plane hits the bomb.
This is my current code in the bomb.qml,
EntityBase { id: bomb entityType: “Bomb” width: 20 height: 20 property int bomb: 90 property int variationDistance: 130 property double delay: 0 MultiResolutionImage { id: bombs width: bomb.width height: bomb.height source: “../../assets/img/bomb.png” } BoxCollider { id: collider width: bomb.width height: bomb.height anchors.centerIn: bomb bodyType: Body.Static collisionTestingOnlyMode: true fixture.onBeginContact: { player.gameOver() } fixture.onBeginContact: { SpriteSequenceVPlay{ id: explosion defaultSource: “explosivesprite.png” SpriteVPlay{ name: “hit” frameWidth: 28 frameHeight: 26 frameCount: 21 startFrameColumn: 1 frameRate: 15 } } } MovementAnimation { id: animation target: bomb property: “x” velocity: -150 running: true minPropertyValue: -80 onLimitReached: { bomb.x = 400 bomb.y = 30+Math.random()*200 } function generateRandomValueBetween(minimum, maximum) { return Math.random()*(maximum-minimum) + minimum } function reset() { bomb.x = scene.gameWindowAnchorItem.width+bomb.width/2 bomb.y = generateRandomValueBetween(-variationDistance, variationDistance)-scene.height/3 } function start() { delayTimer.restart() } function stop() { animation.stop() delayTimer.stop() } Timer { id: delayTimer interval: delay*1000 repeat: false onTriggered: { animation.start() } } Component.onCompleted: { reset() } } } }
Again, thanks for the help!
Hi Todd!
The fixture.onBeginContact signal allows to define a handler function with code that should be executed when the signal is fired.
It is not possible to add a visual item within this code by simply declaring the QML item in this function. You can only add QML items in this declarative way within other QML items, but not within functions.
To dynamically add a visual QML Item in this code, it is necessary to manually create the item and add it as a child to one of your existing visual components.
Have a look at Dynamic Object Creation in QML if you’re interested in doing this. However, please note that this means that a new item will be created whenever the collision happens, but the item is never removed afterwards, which makes things a bit more complicated as you need to destroy the explosion-sprite again to avoid filling up your memory with more and more explosion-items.
I would recommend to add one explosion sprite directly to the entity, but hide it initially. Whenever the onBeginContact signal fires, you can simply start the animation and do not have to worry about creating and destroying QML items.
You can do that with something like this:
BoxCollider {
id: collider
width: bomb.width
height: bomb.height
anchors.centerIn: bomb
bodyType: Body.Static
collisionTestingOnlyMode: true
fixture.onBeginContact: {
player.gameOver()
}
fixture.onBeginContact: {
// play explosion
explosion.jumpTo("hit")
explosion.visible = true
}
}
// define explosion sprite as child of the bomb entity
SpriteSequenceVPlay {
id: explosion
defaultSource: "explosivesprite.png"
anchors.centerIn: parent
// initially hide the explosion sprite
visible: false
SpriteVPlay {
name: "hidden"
frameWidth: 28
frameHeight: 26
frameCount: 1
to: { "hidden" } // stay hidden if in hidden state
}
// the actual hit animation
SpriteVPlay{
name: "hit"
frameWidth: 28
frameHeight: 26
frameCount: 21
startFrameColumn: 1
frameRate: 15
to: { "hidden" } // go to hidden state after hit animation is finished
}
// set sprite invisible if it enters state "hidden"
onCurrentSpriteChanged: {
if(currentSprite === "hidden")
explosion.visible = false
}
}
Best,
Günther
Hey again,
yeah so I went in and attempted to do what you say and trying different things but I cant get the sprite to play… the game runs and everything but the sprite is not playing on contact, this is the way I have everything.
import Felgo 3.0
import QtQuick 2.0
EntityBase {
id: bomb
entityType: “Bomb”
width: 20
height: 20
property int bomb: 90
property int variationDistance: 130
property double delay: 0
MultiResolutionImage {
id: bombs
width: bomb.width
height: bomb.height
source: “../../assets/img/bomb.png”
}
BoxCollider {
id: collider
width: bomb.width
height: bomb.height
anchors.centerIn: bomb
bodyType: Body.Static
collisionTestingOnlyMode: true
fixture.onBeginContact: {
player.gameOver()
}
BoxCollider {
id: collider
width: bomb.width
height: bomb.height
anchors.centerIn: bomb
bodyType: Body.Static
collisionTestingOnlyMode: true
fixture.onBeginContact: {
player.gameOver()
}
fixture.onBeginContact: {
// play explosion
explosion.jumpTo(“hit”)
explosion.visible = true
}
}
// define explosion sprite as child of the bomb entity
SpriteSequenceVPlay {
id: explosion
defaultSource: “explosivesprite.png”
anchors.centerIn: parent
// initially hide the explosion sprite
visible: false
SpriteVPlay {
name: “hidden”
frameWidth: 28
frameHeight: 26
frameCount: 1
to: { “hidden” } // stay hidden if in hidden state
}
// the actual hit animation
SpriteVPlay{
name: “hit”
frameWidth: 28
frameHeight: 26
frameCount: 21
startFrameColumn: 1
frameRate: 15
to: { “hidden” } // go to hidden state after hit animation is finished
}
// set sprite invisible if it enters state “hidden”
onCurrentSpriteChanged: {
if(currentSprite === “hidden”)
explosion.visible = false
}
}
MovementAnimation {
id: animation
target: bomb
property: “x”
velocity: -150
running: true
minPropertyValue: -80
onLimitReached: {
bomb.x = 400
bomb.y = 30+Math.random()*200
}
function generateRandomValueBetween(minimum, maximum) {
return Math.random()*(maximum-minimum) + minimum
}
function reset() {
bomb.x = scene.gameWindowAnchorItem.width+bomb.width/2
bomb.y = generateRandomValueBetween(-variationDistance, variationDistance)-scene.height/3
}
function start() {
delayTimer.restart()
}
function stop() {
animation.stop()
delayTimer.stop()
}
Timer {
id: delayTimer
interval: delay*1000
repeat: false
onTriggered: {
animation.start()
}
}
Component.onCompleted: {
reset()
}
}
}
}
Thanks for working with me, I am learning a lot 😀
Yeah one thing I noticed is that the documentation is saying the dynamic components needs to be called by using the QTcreateComponent() function. Pretty unclear on this, does this function need to be put in under bomb.qml or does it need to be created with a new file?
Hi Todd,
the first thing that jumps to my eyes is that you are having 2 colliders that have the same size and both call player.gameOver() upon collision. And the second collider uses the fixture.onBeginContact signal twice, which should already give you a warning in the editor. So please merge both colliders to a single one and use fixture.onBeginContact only once per collider. This might already be the issue.
Cheers,
Alex
PS: when posting code in the forums, please use the “Code” button in the toolbar of the reply-textbox, that makes it more readable 🙂
Hi Todd!
Can you check if the SpriteSequence on its own is set up correctly? (image source, frame width/height/count settings, …)
I also made a small mistake before by accidentally defining fixture.onBeginContact twice (once for the gameover call and once for starting the sprite sequence). Sorry for that!
This is how it should look like correctly:
import Felgo 3.0
import QtQuick 2.0
EntityBase {
id: bomb
entityType: "Bomb"
width: 20
height: 20
property int bomb: 90
property int variationDistance: 130
property double delay: 0
MultiResolutionImage {
id: bombs
width: bomb.width
height: bomb.height
source: "../../assets/img/bomb.png"
}
BoxCollider {
id: collider
width: bomb.width
height: bomb.height
anchors.centerIn: bomb
bodyType: Body.Static
collisionTestingOnlyMode: true
fixture.onBeginContact: {
player.gameOver()
// play explosion
explosion.jumpTo("hit")
explosion.visible = true
}
}
// define explosion sprite as child of the bomb entity
SpriteSequenceVPlay {
id: explosion
defaultSource: "explosivesprite.png"
anchors.centerIn: parent
// initially hide the explosion sprite
visible: false
SpriteVPlay {
name: "hidden"
frameWidth: 28
frameHeight: 26
frameCount: 1
to: { "hidden": 1 } // stay hidden if in hidden state
}
// the actual hit animation
SpriteVPlay{
name: "hit"
frameWidth: 28
frameHeight: 26
frameCount: 21
startFrameColumn: 1
frameRate: 15
to: { "hidden": 1 } // go to hidden state after hit animation is finished
}
// set sprite invisible if it enters state “hidden”
onCurrentSpriteChanged: {
if(currentSprite === "hidden")
explosion.visible = false
}
}
MovementAnimation {
id: animation
target: bomb
property: "x"
velocity: -150
running: true
minPropertyValue: -80
onLimitReached: {
bomb.x = 400
bomb.y = 30+Math.random()*200
}
function generateRandomValueBetween(minimum, maximum) {
return Math.random()*(maximum-minimum) + minimum
}
function reset() {
bomb.x = scene.gameWindowAnchorItem.width+bomb.width/2
bomb.y = generateRandomValueBetween(-variationDistance, variationDistance)-scene.height/3
}
function start() {
delayTimer.restart()
}
function stop() {
animation.stop()
delayTimer.stop()
}
Timer {
id: delayTimer
interval: delay*1000
repeat: false
onTriggered: {
animation.start()
}
}
Component.onCompleted: {
reset()
}
}
}
About Qt.createComponent
It’s not required to focus on this for the beginning, but this is how it works:
Every QML file you create is a QML Component which usually has a visual representation and comes with signals and properties that allow to customize the component when you use it. For example, if you define a component MyButton.qml you will probably have a text property and a clicked signal. When using this component, you can then set a different text and clicked-behavior for each button that you add in your application:
MyButton {
text: "Click Me"
onClicked: {
doSomething()
}
}
Now let’s talk about component creation. In your qml folder you currently have one Main.qml file, which holds the GameWindow item. The GameWindow is the root component which holds all other items. When you run the app, the application automatically loads the Main.qml and creates the GameWindow with all it’s children. This means for example that your whole game is created at startup with all components like scenes and entities that you directly added to the QML files in your project.
The running game then also contains some game logic, e.g. signal handlers for reacting to user input, handlers to react to collisions or code that you run using a Timer. This code actually brings your game alive and links all the components together to create your game.
For example, you can change properties or call functions of the existing items in your game (like show and start the bomb sprite sequence).
To add a new component within this code that does not yet exist (like you initially tried with your bomb), it is required to dynamically create and add the component with Qt.createComponent. This is a bit complex and can be avoided by statically adding and reusing items (like my suggested solution for your bomb).
As you often need to dynamically add game entities like enemies or obstacles, we provide our EntityManager which automatically keeps track of all dynamically created components, allows to retrieve certain items via their id or e.g. remove all created enemies/obstacles when the game ends.
I hope this helps to shine some light on this topic?
Best,
Günther
Hey thank you!
I still havent been able to get the sprite to play, but yes i believe i have the sprite sequence set up right. However I get an error message from the output saying it cannot open the picture i have for the sprite…
qrc:///qml/VPlay/sprites/SpriteVPlay.qml:70:3: QML Sprite: Cannot open: file:///C:/Users/todd/Downloads/resources_flappybird_vplay2 (5)/qml/entities/explosivesprite.png
qrc:///qml/VPlay/sprites/SpriteVPlay.qml:70:3: QML Sprite: Cannot open: file:///C:/Users/todd/Downloads/resources_flappybird_vplay2 (5)/qml/entities/explosivesprite.png
qrc:///qml/VPlay/sprites/SpriteVPlay.qml:70:3: QML Sprite: Cannot open: file:///C:/Users/todd/Downloads/resources_flappybird_vplay2 (5)/qml/entities/explosivesprite.png
qrc:///qml/VPlay/sprites/SpriteVPlay.qml:70:3: QML Sprite: Cannot open: file:///C:/Users/todd/Downloads/resources_flappybird_vplay2 (5)/qml/entities/explosivesprite.png
Not sure why this is happening, didnt do it for any of the other assets
Update,
I got the sprite to play! Turned out i needed to add the resource file to the qrc for some reason!
You must be logged in to reply to this topic.
As part of the free Business evaluation, we offer a free welcome call for companies, to talk about your requirements, and how the Felgo SDK & Services can help you. Just sign up and schedule your call.
Sign up now to start your free Business evaluation: