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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
|
-- Class: Gamepad
-- This represents a single gamepad connected to the user's computer. This offers the
-- usual functions to check on the status of buttons; you can also inspect other
-- controls, like analog joysticks, by checking properties like <axes> and <hats>.
-- (Incidentally, a hat is used for things like a digital control pad.)
--
-- Normally, gamepad buttons are indexed by number. This class also adds virtual buttons
-- named 'left', 'right', 'up', 'down'. This consults the first two analog axes
-- and the first hat of the gamepad (if they exist) to set these.
--
-- A gamepad is only recognized if it is plugged in when LOVE starts up. There's no way
-- to tell if the user has unplugged it after that; it just acts inert. A gamepad object
-- with no connected hardware at startup will exist and respond to calls like <pressed()>,
-- but it will always return false. You can tell it's unplugged because its active property
-- will be false, and its name will always be 'NO DEVICE CONNECTED'.
--
-- Extends:
-- <Sprite>
Gamepad = Sprite:extend
{
-- Property: number
-- The index of the gamepad, starting at 1.
-- Property: name
-- The name of the gamepad, e.g. "XBox Controller".
-- Property: numAxes
-- The number of available axes, e.g. for analog controls.
-- Property: numBalls
-- The number of available balls.
-- Property: numButtons
-- The number of available buttons.
-- Property: numHats
-- The number of available hats, e.g. digital movement controls.
-- Property: axes
-- The state of all analog axes on the gamepad, indexed by number.
-- Values range from -1 to 1, where 0 is completely neutral.
-- Property: balls
-- The amount of motion by a each ball on the gamepad, indexed by number.
-- Not sure what the range of values is here.
-- Property: hats
-- The state of each hat on the gamepad, indexed by number. Each one has
-- one of these values: https://love2d.org/wiki/JoystickConstant
-- Property: deadZone
-- Any motion by an analog control (from 0 to 1) less than this value is
-- ignored when simulating digital controls.
deadZone = 0.1,
-- private property: _thisFrame
-- what keys are pressed this frame
-- if you are interested in this, use allPressed() instead
_thisFrame = {},
-- private property: _lastFrame
-- what buttons were pressed last frame
_lastFrame = {},
new = function (self, obj)
obj = self:extend(obj)
assert(type(obj.number) == 'number', 'must set a gamepad number')
obj.axes = {}
obj.balls = {}
obj.hats = {}
if obj.number <= love.joystick.getNumJoysticks() then
if not love.joystick.isOpen(obj.number) then love.joystick.open(obj.number) end
obj.name = love.joystick.getName(obj.number)
obj.numAxes = love.joystick.getNumAxes(obj.number)
obj.numBalls = love.joystick.getNumBalls(obj.number)
obj.numButtons = love.joystick.getNumButtons(obj.number)
obj.numHats = love.joystick.getNumHats(obj.number)
-- set initial values for axes and balls
-- hat values are strings so nil comparisons are safe
for i = 1, obj.numAxes do
obj.axes[i] = 0
end
for i = 1, obj.numBalls do
obj.balls[i] = { x = 0, y = 0 }
end
else
obj.name = 'NO DEVICE CONNECTED'
obj.numAxes = 0
obj.numBalls = 0
obj.numButtons = 0
obj.numHats = 0
end
love.joystickpressed = Gamepad._dispatchPress
love.joystickreleased = Gamepad._dispatchRelease
if obj.onNew then obj:onNew() end
return obj
end,
-- Method: pressed
-- Are *any* of the buttons passed held down this frame?
--
-- Arguments:
-- button numbers passed as individual arguments
--
-- Returns:
-- boolean
pressed = function (self, ...)
local buttons = {...}
for _, value in pairs(buttons) do
if self._thisFrame[value] then
return true
end
end
return false
end,
-- Method: justPressed
-- Are *any* of the buttons passed pressed for the first time this frame?
--
-- Arguments:
-- button numbers passed as individual arguments
--
-- Returns:
-- boolean
justPressed = function (self, ...)
local buttons = {...}
for _, value in pairs(buttons) do
if self._thisFrame[value] and not self._lastFrame[value] then
return true
end
end
return false
end,
-- Method: released
-- Are *all* of the buttons passed not held down this frame?
--
-- Arguments:
-- button numbers passed as individual arguments
--
-- Returns:
-- boolean
released = function (self, ...)
local buttons = {...}
for _, value in pairs(buttons) do
if self._thisFrame[value] then
return false
end
end
return true
end,
-- Method: justReleased
-- Are *any* of the buttons passed released after being held last frame?
--
-- Arguments:
-- button numbers passed as individual arguments
--
-- Returns:
-- boolean
justReleased = function (self, ...)
local buttons = {...}
for _, value in pairs(buttons) do
if self._lastFrame[value] and not self._thisFrame[value] then
return true
end
end
return false
end,
-- Method: allPressed
-- Returns all buttons currently pressed this frame.
--
-- Arguments:
-- none
--
-- Returns:
-- string button descriptions; if nothing is pressed, nil
allPressed = function (self)
local result = {}
for key, value in pairs(self._thisFrame) do
if value then table.insert(result, key) end
end
return unpack(result)
end,
-- Method: allJustPressed
-- Returns all buttons just pressed this frame.
--
-- Arguments:
-- none
--
-- Returns:
-- string button descriptions; if nothing is just pressed, nil
allJustPressed = function (self)
local result = {}
for key, value in pairs(self._thisFrame) do
if value and not self._lastFrame[key] then table.insert(result, key) end
end
return unpack(result)
end,
-- Method: allJustReleased
-- Returns all buttons just released this frame.
--
-- Arguments:
-- none
--
-- Returns:
-- string button descriptions; if nothing is just pressed, nil
allJustReleased = function (self)
local result = {}
for key, value in pairs(self._thisFrame) do
if not value and self._lastFrame[key] then table.insert(result, key) end
end
return unpack(result)
end,
buttonPressed = function (self, button)
self._thisFrame[button] = true
end,
buttonReleased = function (self, button)
self._thisFrame[button] = false
end,
endFrame = function (self, elapsed)
-- move button values to the previous frame
for key, value in pairs(self._thisFrame) do
self._lastFrame[key] = value
end
-- set values
for i = 1, self.numAxes do
self.axes[i] = love.joystick.getAxis(self.number, i)
end
for i = 1, self.numBalls do
self.balls[i].x, self.balls[i].y = love.joystick.getBall(self.number, i)
end
for i = 1, self.numHats do
self.hats[i] = love.joystick.getHat(self.number, i)
end
-- simulate digital controls
self._thisFrame['up'] = false
self._thisFrame['down'] = false
self._thisFrame['left'] = false
self._thisFrame['right'] = false
if self.numHats > 0 then
local hat = self.hats[1]
if hat == 'u' then
self._thisFrame['up'] = true
elseif hat == 'd' then
self._thisFrame['down'] = true
elseif hat == 'l' then
self._thisFrame['left'] = true
elseif hat == 'r' then
self._thisFrame['right'] = true
elseif hat == 'lu' then
self._thisFrame['up'] = true
self._thisFrame['left'] = true
elseif hat == 'ru' then
self._thisFrame['up'] = true
self._thisFrame['right'] = true
elseif hat == 'ld' then
self._thisFrame['down'] = true
self._thisFrame['left'] = true
elseif hat == 'rd' then
self._thisFrame['down'] = true
self._thisFrame['right'] = true
end
end
if self.numAxes > 1 then
local xAxis = self.axes[1]
local yAxis = self.axes[2]
if math.abs(xAxis) > self.deadZone then
if xAxis < 0 then
self._thisFrame['left'] = true
else
self._thisFrame['right'] = true
end
end
if math.abs(yAxis) > self.deadZone then
if yAxis < 0 then
self._thisFrame['up'] = true
else
self._thisFrame['down'] = true
end
end
end
Sprite.endFrame(self)
end,
-- private method: _dispatchPress
-- receives a Love joystickpressed callback and hands it off
-- to the appropriate gamepad.
_dispatchPress = function (number, button)
if the.gamepads[number] then
the.gamepads[number]:buttonPressed(button)
end
end,
-- private method: _dispatchRelease
-- receives a Love joystickreleased callback and hands it off
-- to the appropriate gamepad.
_dispatchRelease = function (number, button)
if the.gamepads[number] then
the.gamepads[number]:buttonReleased(button)
end
end
}
|