Forum Archive

Virtual Joystick

smath

I'm working on a joystick UI element, and have run into some behavior I don't understand. For one, I find that the touch location seems to jump back and forth between the actual touch location, and some other location. The other is that I can pull the stick beyond the lower right boundry of the parent view even though the movement is successfully restricted in other directions.

Edit: fixed formatting. Thanks guys.


import ui
from math import sin, cos, atan2, sqrt, pi

class joystick(ui.View):
    #Sets up a view for the outside frame and one for the actual stick
    def __init__(self, stick_size, width):
        self.width = width
        self.height = width
        self.background_color = 'grey'
        self.corner_radius = self.width/2
        self.border_width = 1
        stick = ui.View()
        stick.width, stick.height = stick_size, stick_size
        stick.x, stick.y = self.width/2 - stick.width/2, self.width/2 - stick.width/2
        stick.background_color = 'blue'
        stick.corner_radius = stick_size/2
        stick.name = 'stick'
        self.pos = (0,0)
        self.add_subview(stick)

    def calc_pos(self, touch):
        x_comp = touch.location[0] - self.width/2
        y_comp = self.width/2-touch.location[1]
        max_dist = self.width/2 - self['stick'].width/2
        dist = sqrt(x_comp**2 + y_comp**2)
        #If the stick is not being pulled beyond the edge of the parent view, we just need to return the touch location
        if dist < max_dist:
            self.pos = (x_comp, y_comp)
            return touch.location
        #If the stick is pulled beyond the parent view boundry, limit the movement to the edge of the parent view
        else:           
            angle = atan2(y_comp, x_comp)
            new_x = max_dist*cos(angle)
            new_y = max_dist*sin(angle)
            x_display = self.width/2 - self['stick'].width/2 + new_x
            y_display = self.width/2 - self['stick'].width/2 - new_y
            self.pos = (new_x, new_y)
            return x_display, y_display

    def touch_moved(self, touch):
        self['stick'].x, self['stick'].y = self.calc_pos(touch)

    def touch_ended(self, touch):
        self['stick'].x, self['stick'].y = self.width/2 - self['stick'].width/2, self.width/2 - self['stick'].width/2

stick = joystick(100, 200)
stick.present('popover')

ccc

It looks like you have three single quotes surrounding your code above instead of three backticks.

To properly format your code, try clicking edit at the top right of your post and removing your code from the post (leaving the text in place). Then run your code through Three_Backticks.py and paste the clipboard contents into the post and save.

ywangd

Made a few changes. It works on my device.

import ui 

class joystick(ui.View): 

    def __init__(self, stick_size, size):
        self.width = size 
        self.height = size 
        self.background_color = 'grey'
        self.corner_radius = self.width/2
        self.border_width = 1
        stick = ui.View()
        self.add_subview(stick)
        stick.width, stick.height = stick_size, stick_size
        stick.x, stick.y = self.width/2 - stick.width/2, self.height/2 - stick.height/2
        stick.background_color = 'blue'
        stick.corner_radius = stick_size/2
        stick.name = 'stick'

    def calc_pos(self, touch):  
        x_comp = touch.location[0] - touch.prev_location[0]
        x = max(min(self['stick'].x + x_comp, self.width - self['stick'].width), 0)

        y_comp = touch.location[1] - touch.prev_location[1]
        y = max(min(self['stick'].y + y_comp, self.height - self['stick'].height), 0)
        return x, y

    def touch_moved(self, touch):
        self['stick'].x, self['stick'].y = self.calc_pos(touch)

    def touch_ended(self, touch):
        self['stick'].x, self['stick'].y = self.width/2 - self['stick'].width/2, self.height/2 - self['stick'].height/2

stick = joystick(100, 200) 
stick.present('popover') 
stick.touch_ended(None)  # center the stick
ccc

touch_ended() can be simplified:

    def touch_ended(self, touch):
        self['stick'].center = self.bounds.center

A similar thing can be done in __init__().

Also, you can replace [0] with .x and [1] with .y to improve readability.

smath

@ywang, I like your solution alot. I wish I'd known it could be that simple. Thanks!

@ccc, I'd like to simplify it, but self.bounds.center throws an error because the bounds attribute doesn't have a center attribute. I'm confused as to why I can't just do:


self['stick'].center = self.center

ccc

Difference between Pythonista v1.6 beta on my iPad and v1.5 on yours?

smath

I am on 1.5, so that's possible. I put my name on the list for the beta a while back but never heard anything in response.