TouchControls.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. local events = require("Engine.events")
  2. local onMobile = love.system.getOS() == "Android" or love.system.getOS() == "iOS"
  3. --Wrapper for setColor to use 0-255 values
  4. local function setColor(r,g,b,a)
  5. local r,g,b,a = r,g,b,a
  6. if type(r) == "table" then
  7. r,g,b,a = unpack(r)
  8. end
  9. if r then r = r/255 end
  10. if g then g = g/255 end
  11. if b then b = b/255 end
  12. if a then a = a/255 end
  13. love.graphics.setColor(r, g, b, a)
  14. end
  15. return function(config)
  16. local CPUKit = config.CPUKit
  17. if not CPUKit then error("TouchControls Peripheral can't work without the CPUKit passed") end
  18. local GPUKit = config.GPUKit
  19. if not GPUKit then error("TouchControls Peripheral can't work without the GPUKit passed") end
  20. --Three levels of alpha
  21. local alpha = config.alpha or 160 --The dpad outline
  22. local fg_alpha = config.fg_alpha or 100 --The dpad lines
  23. local bg_alpha = config.bg_alpha or 40 --The dpad background
  24. local devkit = {}
  25. local ControlsEnabled = false
  26. --DPAD Variables
  27. local dpad_radius = 160/2 --The radius of the depad circle
  28. local dpad_extra = 16 --The extra detection zone around the dpad
  29. local dpad_cx, dpad_cy = 100 --The dpad center position
  30. local dpad_line = math.sin(math.pi/4)*dpad_radius --The position of a point in pi/4 (For the cross line to draw)
  31. local touchangle --Touch variable
  32. --A Button
  33. local a_col = GPUKit._GetColor(11) --The color of the A button
  34. local a_cx, a_cy --The center of the A button circle
  35. --B Button
  36. local b_col = GPUKit._GetColor(8) --The color of the B button
  37. local b_cx, b_cy --The center of the B button circle
  38. --Start Button
  39. local start_col = GPUKit._GetColor(12) --The color of the Start button
  40. local start_w, start_h = dpad_radius*2*0.75, dpad_radius*0.75*0.75 --The size of the Start button rectangle
  41. local start_x, start_y = 30 + dpad_radius/8 --The position of the Start button
  42. local start_r = start_h/2 --The radius of the Start button corners
  43. --All Buttons (Shared)
  44. local btn_radius = dpad_radius/2 --The radius of button A and B circles
  45. local touchids = {} --1,2,3,4 for dpad (only 1 is used), 5,6,7 for other buttons
  46. local protrait --Is the device in protrait orientation
  47. devkit.resize = function(w,h)
  48. if h > w then protrait = true else protrait = false end --Detect if the device is in protrait.
  49. b_cx = w - (dpad_cx-dpad_radius/2) --Calculate the button B center X coord.
  50. a_cx = b_cx - dpad_radius --Calculate the button A center X coord.
  51. --Better button position when in protrait
  52. if protrait then
  53. local likoH = (GPUKit._LIKO_H*(w/GPUKit._LIKO_W)) --The LIKO-12 screen size.
  54. dpad_cy = likoH + (h - likoH)/2 --Calculate the dpad center Y coord.
  55. else
  56. dpad_cy = h/2 --Calculate the dpad center Y coord.
  57. end
  58. b_cy = dpad_cy --Calculate the button B center Y coord
  59. a_cy = b_cy + dpad_radius --Calculate the button A center Y coord
  60. start_y = h - (start_h+start_x/2) --Calculate the Start button Y coord
  61. end
  62. devkit.resize(love.graphics.getDimensions()) --Calculate the positions for the first time.
  63. events:register("love:resize", devkit.resize) --Register the resize event.
  64. local function isDpadPressed(id,angle)
  65. if not angle then return false end --If the user is not touching the dpad, then all buttons are not pressed
  66. local zero = (math.pi/2)*id
  67. local astart = zero - math.pi/10 --The start angle
  68. local aend = zero + math.pi/2 + math.pi/10 --The end angle
  69. if astart < 0 then
  70. return (angle >= math.pi*2+astart or angle <= aend)
  71. elseif aend > math.pi*2 then
  72. return (angle >= astart or angle <= aend - math.pi*2)
  73. else
  74. return (angle >= astart and angle <= aend)
  75. end
  76. end
  77. --Calculates the distance between 2 points
  78. local function calcDistance(x1,y1,x2,y2)
  79. return math.sqrt((x2-x1)^2 + (y2-y1)^2)
  80. end
  81. --Calculates the angle of a touch in dpad circle
  82. local function calcAngle(dy,dx)
  83. local angle = -math.atan2(dy,dx)
  84. if angle < 0 then angle = math.pi*2 + angle end
  85. angle = angle + math.pi/4
  86. angle = angle%(math.pi*2)
  87. return angle
  88. end
  89. --The buttons memory
  90. devkit.buttons = {false,false,false,false, false,false,false}
  91. devkit.bmap = {2,3,1,4} --Remap the dpad buttons ids to their real numbers.
  92. --Update dpad buttons.
  93. local function updateDpad()
  94. for i=0,3 do
  95. if isDpadPressed(i,touchangle) then
  96. if not devkit.buttons[i+1] then
  97. CPUKit.triggerEvent("touchcontrol",true,devkit.bmap[i+1])
  98. devkit.buttons[i+1] = true
  99. end
  100. else
  101. if devkit.buttons[i+1] then
  102. CPUKit.triggerEvent("touchcontrol",false,devkit.bmap[i+1])
  103. devkit.buttons[i+1] = false
  104. end
  105. end
  106. end
  107. end
  108. local function updateButtons(tid,state,tx,ty)
  109. for id=5, 7 do
  110. if id < 7 then --The AB buttons
  111. local cx, cy; if id == 5 then cx,cy = a_cx, a_cy else cx,cy = b_cx, b_cy end
  112. local dist = calcDistance(tx,ty,cx, cy)
  113. if state == "pressed" and not touchids[id] then
  114. if dist <= btn_radius then
  115. touchids[id] = tid
  116. devkit.buttons[id] = true
  117. CPUKit.triggerEvent("touchcontrol",true,id)
  118. end
  119. elseif state == "moved" and touchids[id] and touchids[id] == tid then
  120. if dist <= btn_radius then
  121. if not devkit.buttons[id] then
  122. devkit.buttons[id] = true
  123. CPUKit.triggerEvent("touchcontrol",true,id)
  124. end
  125. else
  126. if devkit.buttons[id] then
  127. devkit.buttons[id] = false
  128. CPUKit.triggerEvent("touchcontrol",false,id)
  129. end
  130. end
  131. elseif state == "released" and touchids[id] and touchids[id] == tid then
  132. touchids[id] = false
  133. if devkit.buttons[id] then
  134. devkit.buttons[id] = false
  135. CPUKit.triggerEvent("touchcontrol",false,id)
  136. end
  137. end
  138. else --The start button
  139. if state == "pressed" and not touchids[id] then
  140. if tx >= start_x and tx <= start_x + start_w then
  141. if ty >= start_y and ty <= start_y + start_h then
  142. touchids[id] = tid
  143. devkit.buttons[7] = true
  144. CPUKit.triggerEvent("touchcontrol",true,7)
  145. return
  146. end
  147. end
  148. elseif state == "moved" and touchids[id] and touchids[id] == tid then
  149. if tx >= start_x and tx <= start_x + start_w and ty >= start_y and ty <= start_y + start_h then
  150. if not devkit.buttons[7] then
  151. devkit.buttons[7] = true
  152. CPUKit.triggerEvent("touchcontrol",true,7)
  153. end
  154. else
  155. if devkit.buttons[7] then
  156. devkit.buttons[7] = false
  157. CPUKit.triggerEvent("touchcontrol",false,7)
  158. end
  159. end
  160. elseif state == "released" and touchids[id] and touchids[id] == tid then
  161. touchids[id] = nil
  162. if devkit.buttons[7] then
  163. devkit.buttons[7] = false
  164. CPUKit.triggerEvent("touchcontrol",false,7)
  165. end
  166. end
  167. end
  168. end
  169. end
  170. local function drawButtons()
  171. for id=5, 7 do
  172. love.graphics.setLineWidth(devkit.buttons[id] and 2 or 1)
  173. if id < 7 then --AB buttons
  174. local cx, cy, col; if id == 5 then cx,cy,col = a_cx,a_cy,a_col else cx,cy,col = b_cx,b_cy,b_col end
  175. col[4] = bg_alpha; setColor(col)
  176. love.graphics.circle("fill",cx, cy, btn_radius)
  177. if devkit.buttons[id] then love.graphics.circle("fill",cx, cy, btn_radius) end
  178. col[4] = alpha; setColor(col)
  179. love.graphics.circle("line",cx, cy, btn_radius)
  180. else --Start button
  181. start_col[4] = bg_alpha; setColor(start_col)
  182. love.graphics.rectangle("fill",start_x,start_y,start_w,start_h,start_r)
  183. if devkit.buttons[7] then love.graphics.rectangle("fill",start_x,start_y,start_w,start_h,start_r) end
  184. start_col[4] = alpha; setColor(start_col)
  185. love.graphics.rectangle("line",start_x,start_y,start_w,start_h,start_r)
  186. end
  187. end
  188. end
  189. local TC, yTC = {}, {}
  190. --Toggle the touch controls
  191. function TC.setInput(bool)
  192. ControlsEnabled = bool
  193. if onMobile then GPUKit.DevKitDraw(ControlsEnabled) end
  194. end
  195. --Buttons Touch
  196. events:register("love:touchpressed",function(id,x,y,dx,dy,p) updateButtons(id,"pressed",x,y) end)
  197. events:register("love:touchmoved",function(id,x,y,dx,dy,p) updateButtons(id,"moved",x,y) end)
  198. events:register("love:touchreleased",function(id,x,y,dx,dy,p) updateButtons(id,"released",x,y) end)
  199. --Dpad Touch
  200. events:register("love:touchpressed",function(id,x,y,dx,dy,p)
  201. if touchids[1] then return end
  202. local dist = calcDistance(x,y,dpad_cx,dpad_cy)
  203. if dist < dpad_radius + dpad_extra then
  204. touchangle = calcAngle(y - dpad_cy, x - dpad_cx)
  205. touchids[1] = id
  206. updateDpad()
  207. end
  208. end)
  209. events:register("love:touchmoved",function(id,x,y,dx,dy,p)
  210. if (not touchids[1]) or touchids[1] ~= id then return end
  211. touchangle = calcAngle(y - dpad_cy, x - dpad_cx)
  212. updateDpad()
  213. end)
  214. events:register("love:touchreleased",function(id,x,y,dx,dy,p)
  215. if (not touchids[1]) or touchids[1] ~= id then return end
  216. touchids[1] = false
  217. touchangle = false
  218. updateDpad()
  219. end)
  220. events:register("GPU:DevKitDraw",function()
  221. love.graphics.setLineStyle("smooth")
  222. --Buttons
  223. drawButtons()
  224. --DPAD
  225. love.graphics.setLineWidth(1)
  226. setColor(255,255,255,bg_alpha)
  227. love.graphics.circle("fill",dpad_cx, dpad_cy, dpad_radius)
  228. if touchangle then
  229. if devkit.buttons[1] then
  230. love.graphics.arc("fill","pie",dpad_cx, dpad_cy, dpad_radius,
  231. (math.pi/2)*0 - math.pi/4, (math.pi/2)*1 - math.pi/4)
  232. end
  233. if devkit.buttons[2] then
  234. love.graphics.arc("fill","pie",dpad_cx, dpad_cy, dpad_radius,
  235. (math.pi/2)*3 - math.pi/4, (math.pi/2)*4 - math.pi/4)
  236. end
  237. if devkit.buttons[3] then
  238. love.graphics.arc("fill","pie",dpad_cx, dpad_cy, dpad_radius,
  239. (math.pi/2)*2 - math.pi/4, (math.pi/2)*3 - math.pi/4)
  240. end
  241. if devkit.buttons[4] then
  242. love.graphics.arc("fill","pie",dpad_cx, dpad_cy, dpad_radius,
  243. (math.pi/2)*1 - math.pi/4, (math.pi/2)*2 - math.pi/4)
  244. end
  245. end
  246. setColor(255,255,255,alpha)
  247. love.graphics.circle("line",dpad_cx, dpad_cy, dpad_radius)
  248. --Draw the lines
  249. love.graphics.setLineWidth(0.5)
  250. setColor(255,255,255,fg_alpha)
  251. love.graphics.line(dpad_cx+dpad_line, dpad_cy-dpad_line,
  252. dpad_cx-dpad_line, dpad_cy+dpad_line)
  253. love.graphics.line(dpad_cx-dpad_line, dpad_cy-dpad_line,
  254. dpad_cx+dpad_line, dpad_cy+dpad_line)
  255. end)
  256. return TC, yTC, devkit
  257. end