Nanfeng

Notes on software development, code, and curious ideas

Controlling Movement with a Virtual Joystick in Cocos2d-x Lua

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
local Joystick = {}
Joystick.__index = Joystick

function Joystick.new(parent, center, radius, onChange)
local self = 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)
return self
end

function Joystick: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 and self.radius / distance or 1
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 > 0 then 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:

1
2
3
4
player:setPosition(
player:getPositionX() + directionX * speed * dt,
player:getPositionY() + directionY * speed * dt
)

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.

+