How to make proper Touch Buttons for your mobile game - Replacement for "On Touch object end"

1 favourites
  • 8 posts
From the Asset Store
This is a single chapter from the "Construct Starter Kit Collection". It is the Student Workbook for its Workshop.
  • 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.

  • You can also modify the function to always return 1 if a controller is being used, this way, you can have controller + touch input in the same button event with an or function and the code still gets executed.

    Also, I added an update for this solution (in the main post) because it had a flaw. If you touched a button and held, then tapped somewhere else, the button would revert to its initial state because the „On any touch end“ event would trigger (and wouldn’t do the action)

    This isn’t a huge deal but the new solution is far more elegant because it mitigates this issue :)

  • I don't get it why you need all that complex code. "On Tap" event is specifically designed for buttons and it checks that the finger has not moved away from the initial location.

    Another alternative is to add a second condition to the "On Touch End" event - "Touch is touching Button".

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • On tap does not trigger on long touches.

    The main issue is that the buttons in my game (and many other games) play an animation when they are initially being tapped and play a second animation once they are released.

    Now, you could just seperate these 2 things into different events - 1 for the action of the button (which triggers on tap object) and one for the button animation, which triggers on touched object and on any touch end (+ a second condition to check whether the button was pressed before). However, this eliminates long touches and the buttons feel very unresponsive. If you hold something for longer or move your finger just slightly, the tap does not register.

    The issue with the „On touch end“ event in combination with the „is touching object“ event is that the finger can start touching the screen somewhere else and move to the button and then release & activate it. This is very unnatural. Yes, you could store wether the button had been touched with a variable and then check on the second event with the „is touching object“ condition wether it was also initially touched, but that requires way more code than my solution. In addition to that, you would need another event to make this work, one for playing the initial animation once the object has been touched and one for playing the second / the „back“ animation once the touch has ended & the player was touching the button in the first place - otherwise it would play all button anomations at the same time. You can’t just put this revert animation under the „on touch end“ + „is touching object“ event because then the button would stay small in my case if you would touch the button and then move your finger away from it because then there is nothing that reverts it back.

    So yes, you could make this work, but it would require 3 events for every button and a lot of complicated logic on top of that.

  • If I understood your task correctly, you can do this:

    If you have many button objects following the same logic, you can add them to a family and reuse these three events for the entire family.

  • Thanks for that solution. This sorta works but has a few issues compared to mine ;) First of all, if one finger is on a button, and the other one taps somewhere else, the animation gets reset. I combat this issue in my game by checking if the touch with a specific index ends (as explained at the end of my post).

    Second, we would now need a place to execute the actions of the button. Usually, that happens when you lift up your finger, so we would need a combination of "on any touch end" + "is in touch" and then check if the sprite is currently playing the "intouch" animation.

    Now, because your buttons all got the same animation (a sprite change) you can use a group for that. However, in my game buttons shrink (that's done with the tween behavior or an animation), so that is harder to track. It would work with an instance variable (a boolean) though.

    Another issue is that if you place your finger on the button, then move it away slightly and then back on the button, the action would not trigger because the button animation would have already reset.

    So your solution would work as well if we modified it a bit but it would need at least 2 events per button which is not ideal.

  • Ok, seems like your task is pretty specific and you've found a good working solution for it. I've never had to program such complex buttons :) It's usually really straightforward - play an animation when touched, reset to default when released.

  • Yeah so far it works well :) Yes, it is quite specific... because most buttons in my game use different animations etc. and I only want 1 event per button because otherwise the code becomes more cluttered and it's hard for me to get an overview. If you just have simple tap buttons that don't react to the touch / lift off of the finger then it is way simpler. (or if you are fine with long touches not registering)

    Overall I think it would be very cool if you could fine tune the parameters of the built in "on tap (object)" event.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)