Tetris V2 - Add Swipe Gesture
Posted on: June 22, 2023
There is no buttons to control tetris pieces in Tetris V2, instead users can use keyboard on desktops or swipe to move pieces on mobile devices. In this blog post, I am going to discuss the way I have implemented the swipe gestures and solved the issues.
Swipe Actions
Firstly, let me explain what swipe actions this app has.
-
Swipe Left: piece moves left
-
Swipe Right: piece moves right
-
Swipe Down: piece soft drops down
-
Swipe Down with a Fast Speed: piece hard drops to bottom
-
Swipe Up: hold the piece
-
Tap: rotate piece
Trials and Issues
To achieve the swipe actions described above, I have tried a few trials to detect the swipe movement and its direction.
Trial 1: useSwipe Hook Version 1
I have created a hook useSwipe that returns onTouchStart
, onTouchMove
, onTouchEnd
and onClick
functions. Inside this hook, I used useState to record the start and end positions of the touch, which can be retrieved via the event
interface. When touch ends, the distance between the start and end positions will be calculated in horizontal and vertical directions respectively. And thus the sign of the distance can determine whether the swipe is left or right, up or down. I can then pass in corresponding move piece function to the hook to respond to the swipe. In addition, I have added a swipe distance threshold to prevent unintended touch.
The problem of this version is the piece only moves after the swipe ends, instead of moving along while swiping.
Trial 2: useSwipe Hook Version 2
To allow the piece moving along with the swipe, calculating distance and responding to the swipe is now done in the onTouchMove
function, rather than in onTouchEnd
function. Then end positions state will be reset to null in onTouchEnd
.
To distinguish a swipe down is a soft drop or hard drop, I added a useState to record the start time of the touch in onTouchStart
function. Next when touch ends, get the end time and end vertical position of the touch to find out the duration and distance of the swipe. From this, the swipe speed can be calculated. If the speed is over a threshold, it is recognised as a fast swipe down and performs hard drop piece.
The recoginition and response of swipe is working using this useSwipe Hook, but I found out that the piece is moving too fast and too much when swiping for just a very small distance.
Trial 3: useSwipe Hook Version 3
I have investigated the use of useState might be the cause of the piece is moving too much issue. This is because useState hook triggers the component to rerender everytime the state changes, i.e. when the position of the touch changes during the swipe, so it is a loop. Therefore I have changed useState
to use useRef
hook for holding startPosition and startTime state, and set the startPosition state to the current position after every movePiece during onTouchMove. The difference between useState and useRef hook is that useRef does not cause rerender when the mutable value is updated.
On top of that, I have adjusted the swipe down speed threshold for a few times, in order to allow the users to hard drop piece easily but still be able to distinguish from a soft drop swipe down. And now, swipe actions are operating as expected.