Hey everyone,
making a great and responsive touch button that plays an animation when you first touch it and triggers the action once you release the button in construct 3 is not easy.
See, when you use "on tap object" a touch of a button does not register if you hold the button for longer.
If you want to make a button, that plays an animation (e.g becomes smaller) when you first touch it, and play another animation (e.g become big again) and then triggers and action once the touch has ended, there theoretically is a simple option:
1) You play the first button animation when you touch the object "on object touched"
2) You wait for a signal that you call something like "Wait for signal: TouchEnd"
3) You use a seperate event "on any touch end" and trigger the signal "TouchEnd"
4) The code that comes after the "Wait for Signal: TouchEnd" will be executed
However, this approach is flawed. See, when you touch the button, then move your finger away from it and release, the action still happens. This means, once your finger is on the button (might be an accidental touch), there is no way to cancel the action.
For a long time I used "On tap object" for all sensitive actions and this 2nd approach for everything else, but now I have found a great way to check if the finger was released / the touch ended on a certain object.
Here is how it works:
First, you add an event "on any touch end" that casts the signal "TouchEnd". Also, we add 2 global variables, TouchEndX and TouchEndY. We set these as seperate actions to Touch.X and Touch.Y respectively - If you have parallax or scrolling layers, you need to use Touch.XAt(TouchIndex,"NameOfUILayer") and the same for Touch.Y. This way, we store the last touch of the player.
Second, we add a new function that takes the parameter "ObjectUID" -> this will be set to the Object.UID of the object that has the touch action later.
The function also returns a number. 1 representing a valid touch, 0 meaning that the user has cancelled the touch.
Now, you need to create a family that consists of all buttons in your game (in my game it is called "Buttons"), that are supposed to use this approach.
The function, in its first event, now gets 2 conditions. One is called "Pick instance with UID ObjectUID" with ObjectUID being the variable we just assigned to the function. This ensures that only the object the player has clicked on is being used in this expression. The second condition is one of type "compare 2 values". We compare the first value:
---
Compare:
(Buttons.bboxright>TouchEndX)+(Buttons.bboxleft<TouchEndX)+(Buttons.BBoxBottom>TouchEndY)+(Buttons.BBoxTop<TouchEndY)
to:
4
What I‘m doing too (as you can see in the last screenshot) is to add 35 to the dimensions of the button in this expression so that if the user is moving his finger only slightly away from the button, it still counts as a touch.
---
So what this do? It checks, whether the last touch of the player was in the boundaries of the hitbox of the object / button that he has just touched. If one of these 4 conditions is true, it adds "1" to the expression, therefore the total should be 4.
If this condition is true, the function should return "1". If the condition is false (add an "else" event here), it should return 0.
Now let's have a look at the button:
The parent event is a simple "On touched object" event.
The (only) subevent consists of your 1st animation, then the "Wait for signal: TouchEnd" action, and after that, the actions that should be executed regardless if the touch ended on the object (so, the 2nd animation to revert the 1st one as well as a sound effect, maybe).
Now, we add a subevent of this subevent, which checks wether the function we created returns 1. When calling it, we have to specify its parameter (ObjectUID) which is the button.UID (the button of the event). Only if this condition is true, we execute the code of the button. This way, the animation of the button works independently from the action action and as compared to the animation only triggers when the touch ends on the object.
Note, that this basically eliminates multi - touch for all buttons that use this system. On any touch end, the signal will trigger and all buttons will return to their initial state (with the 2nd animation playing / reverting). However, only the one where the last touch has started (because we only have 2 variables) will cause a button to activate, assuming the touch has also ended on this exact button.
If you want to improve this with Multitouch, we would need a more complex system, where an object stores the X and Y values of the last touch and the ID of the touch would be taken into account.
- - - - -
Update (improvement):
If you want to make sure that the „on any touch end“ event will only store the position of the finger that touched the button in the first place (otherwise, if you press and hold a button and simultaneously tap somewhere else, it will trigger the event „on any touch end“ and the button will revert back to its initial state without executing the action) there is an easy way to add that to the existing code.
1) Add a Global variable called „TouchIndex“
2) Change the „on any touch end“ event to an „on nth touch end“ event and set the number of it to the variable (TouchIndex)
3) Add a new event „on touched object“ with the object being the Buttons family. Add an action „set value“ to the event and set the value of „TouchIndex“ to touch.TouchIndex . If you have many overlapping buttons, you may want to add another condition, something like „Buttons opacity = 100“ or „Buttons is visible“. If you have many layers that you make visible or invisible, consider also making them inactive when they are transparent.
-> this means, that only when the user ends a touch that initially started on a button, it will cast the signal „TouchEnd“ and store the position of where it ended.
In my case, I also got a condition to check whether a controller is being used, but this is how it looks like now:
Again, if you are using Parallax, you need to use TouchXAt() and TouchYAt() (and your UI layer in the brackets) instead of TouchX and TouchY.