How do I make a radial thumb control for mobile?

  • Hi! I created a circle sprite for the right thumb to control the player sprite.

    I used this code to get the angle from the center to the touch position to rotate towards the angle and accelerate in the direction. But the angle() returns 0 to 180 for the lower half of the circle and -0 to -180 for the upper half. But the action to set/rotate towards angle uses 360 degrees?

    + Touch: Is in touch

    + Touch: Is touching UI_SteeringCircle

    -> Player: Accelerate Custom 140 at angle angle(UI_SteeringCircle.X,UI_SteeringCircle.Y,Touch.AbsoluteX,Touch.AbsoluteY)

    -> Player: Rotate 10 degrees toward angle(UI_SteeringCircle.X,UI_SteeringCircle.Y,Touch.AbsoluteX,Touch.AbsoluteY)

    Does anyone know how to make this work to move the player in the direction you put your thumb on the circle?

  • While at the Schools and Academies show with Laura_D last week we talked a bit about mapping touch and gamepad controls to tile movement. I think she's planning to put some tutorials out about it, but I'll put the gist of the maths up here for you.

    This example is for a player that moves towards the touch location.

    	// 10 is rather arbitrary here, it needs adjusting based on your usage
    	DEAD_ZONE = 10
    	neutral_x = Player.x
    	neutral_y = Player.y
    	target_x = Touch.x
    	target_y = Touch.y
    	delta_x = target_x - neutral_x
    	delta_y = target_y - neutral_y
    	magnitude = sqrt(delta_x * delta_x + delta_y * delta_y)
    	// add 180 to the angle, so that it's in the range 0..360
    	theta = angle(0, 0, delta_x, delta_y) + 180
    	// checks to see if we have a sufficient offset from the neutral position
    	if (magnitude > DEAD_ZONE) {
    		// rotate the angle by 45 degrees ( range is now 45..405 )
    		theta = (theta + 45);
    		// divide by 90 to get a value from 0.5..4.5
    		// then round the value so that is is an whole number ( 1/2/3/4 )
    		// finally modulo the value by 4 so that it wraps around
    		// the range will now be 0..4
    		theta = round(theta / 90) % 4;
    		// theta is now 0 for west, 1 for north, 2 for east and 3 for south

    This should be relatively easy to map to a virtual analog stick, just use the center of the stick for the neutral position. You can ditch the math inside the if block if you just want the angle, it's just for mapping the angle to a 4 direction value. Also that math could be tweaked for 8 direction if wanted.

  • Hello Bl4ckSh33p;

    I tried to get the kind of control you are talking about to work with the car movement. Here is what I came up with.



    + Touch: Is touching moveControl

    // tam

    | Local number thetaOpp‎ = 0

    | Local number theta‎ = 0

    | Local boolean finished‎ = false

    | Local number curPilot‎ = 0

    | Local constant number steeringAngleExtra‎ = 0.1

    | Local string asc‎ =

    | Local string apc‎ =

    | Local constant number reverseBackAngle‎ = 30


    ----+ (no conditions)

    -----> [DISABLED] System: Set curPilot to Functions.retrieveStore("currentPilot")

    -----> System: Set curPilot to Functions.getCurrentPilot

    -----> System: Set theta to angle(moveControl.X,moveControl.Y,Touch.X("hudTch"),Touch.Y("hudTch"))

    -----> System: Set thetaOpp to theta+180

    -----> System: Set asc to Functions.spCoord(moveControl.UID)

    ----+ pilot: Pick instance with UID curPilot

    --------+ pilot: Within reverseBackAngle degrees of thetaOpp

    ------------+ (no conditions)

    -------------> pilot: Simulate pilotCar pressing Brake

    -------------> redMoveArrow: Set position to (moveControl.X, moveControl.Y)

    -------------> redMoveArrow: Set angle to theta degrees

    -------------> redMoveArrow: Set width to moveControl.Width

    -------------> redMoveArrow: redArrowFade: restart fade

    -------------> System: Set finished to True

    ----------------+ pilot: [X] Within 2 degrees of thetaOpp

    --------------------+ pilot: Is clockwise from thetaOpp

    ------------------------+ (no conditions)

    -------------------------> pilot: Simulate pilotCar pressing Steer left

    --------------------+ System: Else

    ------------------------+ (no conditions)

    -------------------------> pilot: Simulate pilotCar pressing Steer right

    --------------------+ (no conditions)

    ---------------------> (no actions)

    --------+ System: [X] Is finished

    --------+ pilot: Within (180-reverseBackAngle) degrees of theta

    ------------+ (no conditions)

    -------------> pilot: Simulate pilotCar pressing Accelerate

    -------------> greenMoveArrow: Set position to (moveControl.X, moveControl.Y)

    -------------> greenMoveArrow: Set angle to theta degrees

    -------------> greenMoveArrow: Set width to moveControl.Width

    -------------> greenMoveArrow: greenArrowFade: restart fade

    -------------> System: Set finished to True

    ------------+ pilot: [X] Within 2 degrees of theta

    ----------------+ pilot: Is clockwise from theta

    --------------------+ (no conditions)

    ---------------------> pilot: Simulate pilotCar pressing Steer left

    ---------------------> pilot: Rotate steeringAngleExtra degrees counter-clockwise

    ----------------+ System: Else

    --------------------+ (no conditions)

    ---------------------> pilot: Simulate pilotCar pressing Steer right

    ---------------------> pilot: Rotate steeringAngleExtra degrees clockwise

    ----------------+ (no conditions)

    -----------------> (no actions)

  • Thanks. I tried to recreate it in C3 Nepeo but sqrt does only work with 1 param and theta does not seem to work.

    -> System: Set delta_x to Player.X-UI_SteeringCircle.X

    -> System: Set delta_y to Player.Y-UI_SteeringCircle.Y

    -> System: Set theta to angle(0, 0, delta_x, delta_y) + 180

    It's always flying in the same direction no matter where I hit the circle.

    When I switch player and center of the UI element the player sprite moves in the opposite direction.

    Your example is hard to understand winkr7.

    I tried to copy the code from newt and found it works if UI element is not on a 0,0 parallax layer. As soon as I disable parallax for the UI layer its no longer working with the negative angles.

    -> Player: Accelerate Custom 140 at angle angle(UI_SteeringCircle.X,UI_SteeringCircle.Y,Touch.XAt(0),Touch.YAt(0))

    Any idea how to do it with 0,0 parallax for UI elements?

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • I think I found it. It works with the Layer coords of Touch and the negative angles seem to work fine without any extra code.

    -> Player: Accelerate Custom 140 at angle angle(UI_SteeringCircle.X,UI_SteeringCircle.Y,Touch.X("HUD"),Touch.Y("HUD"))

    -> Player: Rotate 10 degrees toward angle(UI_SteeringCircle.X,UI_SteeringCircle.Y,Touch.X("HUD"),Touch.Y("HUD"))

  • Ah yeah the sqrt thing is a typo it should be sqrt(x * x + y * y), basically using pythagoras to get the magnitude of the vector. I'll try work up a quick example when I can.

  • I'm a bit late for the party, but here are two examples I made for some other posts:

  • Okay I put together a little example of how to do movement with touch input.

    It's got a visual representation of a virtual analogue stick for each touch on screen. There's 3 examples included, each within a different group. You have to disable the other 2 and enable the one you want to see.

    The 3 examples are 4 direction movement, 8 direction movement and analogue movement. First 2 use 8 direction with simulated input. Last one just moves the character around manually.

    At the moment all of the virtual analogue sticks are contributing to movement, and there are as many of those as touches... You probably only want to use the first one!

  • Nepeo I like your analogue stick better than the one I've been using. I have a question though. How do you change the player speed? Changing the speed setting in the player's 8Direction behavior doesn't seem to have any effect at all. Changing the local speed variable does seem to work either.

    Edit: Player speed seems to be tired to the MAX_STICK_MOVEMENT variable, but changing that breaks the effect.

  • Thank you very much for the examples!

  • Tiny Martian yes the max speed is tied to the magnitude and the max_stick_movement at the moment. You can however fix this by normalising the value ( divide by it's maximum to get a value in the range 0...1 ) then multiplying it by the new maximum. Like so (min(magnitude, MAX_STICK_MOVEMENT) / MAX_STICK_MOVEMENT) * TRUE_MAX_SPEED. This will give you a speed between 0 and TRUE_MAX_SPEED which can be any positive value you want.

    EDIT: I've updated the example with this behaviour.

  • Nepeo, you are awesome! Thank you.

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