Implementing a touch-driven virtual joystick with normalized direction and a limited thumb radius.
A virtual joystick turns a touch position into a direction and optional magnitude. It usually consists of a fixed base, a movable thumb, and a controller that reports normalized input to the player object.
functionJoystick.new(parent, center, radius, onChange) localself = setmetatable({}, Joystick) self.center = center self.radius = radius self.onChange = onChange self.base = cc.Sprite:create("joystick-base.png") self.thumb = cc.Sprite:create("joystick-thumb.png") self.base:setPosition(center) self.thumb:setPosition(center) parent:addChild(self.base) parent:addChild(self.thumb) self:installTouchListener(parent) returnself end
functionJoystick:updateTouch(point) local dx = point.x - self.center.x local dy = point.y - self.center.y local distance = math.sqrt(dx * dx + dy * dy) local scale = distance > self.radius andself.radius / distance or1 self.thumb:setPosition(self.center.x + dx * scale, self.center.y + dy * scale)
local magnitude = math.min(distance / self.radius, 1) local nx, ny = 0, 0 if distance > 0then nx, ny = dx / distance, dy / distance end self.onChange(nx, ny, magnitude) end
The listener should claim a touch only when it begins inside the base, call updateTouch while moving, and reset the thumb and output to zero when the touch ends or is cancelled.
Apply movement using frame time rather than pixels per touch event:
Add a small dead zone to suppress jitter near the center. Keep touch coordinates in the same node space as the joystick, handle cancellation, and decide whether magnitude controls speed or only direction.