Guest User

Untitled

a guest
Nov 18th, 2018
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.64 KB | None | 0 0
  1. --# Emitter
  2. Emitter = class()
  3.  
  4. function Emitter:init(args)
  5. self.particleMesh = mesh()
  6. self.particleMesh.texture = args.tex
  7. self.minLife = args.minLife or 1
  8. self.maxLife = args.maxLife or 1
  9. self.spread = args.spread or 360
  10. self.angle = args.angle or 0
  11. self.minSpeed = args.minSpeed or 10
  12. self.maxSpeed = args.maxSpeed or 50
  13. self.minSize = args.minSize or 10
  14. self.maxSize = args.maxSize or 15
  15. self.growth = args.growth or 1
  16. self.startColor = args.startColor or color(255, 255, 255, 255)
  17. self.endColor = args.endColor or color(0, 0, 0, 0)
  18. self.streak = args.streak or false
  19. self.streakMult = args.streakMult or 2
  20. self.accel = args.accel or vec2(0,0)
  21. self.rAccel = args.rAccel or vec2(0,0)
  22.  
  23. self.particles = {}
  24. self.pCount = 0
  25. for i = 1,100 do
  26. table.insert(self.particles, Particle())
  27. end
  28. end
  29.  
  30. function Emitter:emit(pos,count)
  31. for i = 1,#self.particles do
  32. local p = self.particles[i]
  33. if p.dead then
  34. self.pCount = math.max(i, self.pCount)
  35. p.dead = false
  36. p.pos = pos
  37. p.life = math.random(self.minLife, self.maxLife)
  38. p.size = math.random(self.minSize, self.maxSize)
  39. p.maxLife = p.life
  40. p.vel = vec2(0, math.random(self.minSpeed, self.maxSpeed))
  41. p.vel = p.vel:rotate(math.rad(self.angle + math.random(-self.spread/2, self.spread/2)))
  42. count = count - 1
  43. if count == 0 then return end
  44. end
  45. end
  46. end
  47.  
  48. function Emitter:draw()
  49. -- update
  50. self.particleMesh:clear()
  51. for i = 1,self.pCount do
  52. local p = self.particles[i]
  53. if not p.dead then
  54. p.prevPos = p.pos
  55. p.pos = p.pos + p.vel * DeltaTime
  56. p.vel = p.vel + (self.accel + vec2(math.random(-self.rAccel.x, self.rAccel.x), math.random(-self.rAccel.y, self.rAccel.y))) * DeltaTime
  57. p.life = math.max(0, p.life - DeltaTime)
  58. p.size = p.size + DeltaTime * self.growth
  59. if p.life == 0 then p.dead = true end
  60. local interp = p.life / p.maxLife
  61.  
  62. p.col.r = interp * self.startColor.r + (1-interp) * self.endColor.r
  63. p.col.g = interp * self.startColor.g + (1-interp) * self.endColor.g
  64. p.col.b = interp * self.startColor.b + (1-interp) * self.endColor.b
  65. p.col.a = interp * self.startColor.a + (1-interp) * self.endColor.a
  66. local ind = self.particleMesh:addRect(p.pos.x, p.pos.y, p.size, p.size)
  67. self.particleMesh:setRectColor(ind, p.col)
  68. if self.streak then
  69. local dir = (p.pos - p.prevPos)
  70. local len = dir:len()
  71. local pos = (p.pos + p.prevPos) * 0.5
  72. local ang = math.atan2(dir.y, dir.x)
  73. self.particleMesh:setRect(ind, pos.x, pos.y, p.size * self.streakMult, p.size, ang)
  74. end
  75. end
  76. end
  77. self.particleMesh:draw()
  78. end
  79.  
  80.  
  81. Particle = class()
  82.  
  83. function Particle:init()
  84. self.pos = vec2(0,0)
  85. self.prevPos = vec2(0,0)
  86. self.vel = vec2(0,0)
  87. self.life = 0
  88. self.maxLife = 0
  89. self.dead = true
  90. self.col = color(0, 0, 0, 255)
  91. self.size = 0
  92. end
  93.  
  94. --# SlammerPod
  95. SlammerPod = class()
  96.  
  97. function SlammerPod:init(x,y)
  98. self.name = "pod"
  99. self.health = 1
  100.  
  101. self.chassis = createBox(x, y, 60, 80)
  102. self.chassis.gravityScale = 1
  103. self.chassis.density = 3
  104. self.chassis.fixedRotation = true
  105. self.chassis.info = self
  106.  
  107. self.rock = createCircle(self.chassis.x,self.chassis.y - 150, 35)
  108. self.rock.density = 0.75
  109. self.rock.info = self
  110. owners[self.rock] = self
  111. self.rock.angularDamping = 0.1
  112.  
  113. self.rope = physics.joint(ROPE, self.chassis, self.rock, self.chassis.position, self.rock.position, 150)
  114. debugDraw:addJoint(self.rope)
  115.  
  116. self.links = {}
  117. local lc = 10
  118. local len = (self.chassis.y - self.rock.y) / lc
  119. local p = self.chassis.position - vec2(0, len/2)
  120. local prev = self.chassis
  121. for i = 1,lc do
  122. local link = createBox(p.x, p.y, 5, len)
  123. link.mask = {10}
  124. local j = physics.joint(REVOLUTE, prev, link, p + vec2(0, len/2))
  125. prev = link
  126. debugDraw:addJoint(j)
  127. table.insert(self.links, link)
  128. p.y = p.y - len
  129. end
  130. local j = physics.joint(REVOLUTE, prev, self.rock, self.rock.position)
  131. debugDraw:addJoint(j)
  132.  
  133. self.stick = VirtualStick({
  134. moved = function(v) self:stickMoved(v) end,
  135. released = function(v) self:stickReleased(v) end,
  136. radius = 80,
  137. deadZoneRadius = 10,
  138. })
  139. self.stick:activate()
  140. self.dir = vec2(0,0)
  141.  
  142. self.anim = 0.0
  143.  
  144. --sprite("Cargo Bot:Smoke Particle")
  145. self.sparks = Emitter({tex = "Tyrian Remastered:Bullet Fire A",
  146. minSize = 5,
  147. maxSize = 5,
  148. minSpeed = 100,
  149. maxSpeed = 150,
  150. accel = vec2(0,-1000),
  151. rAccel = vec2(1500,1500),
  152. streak = true,
  153. streakMult = 3})
  154.  
  155. self.smoke = Emitter({tex = "Cargo Bot:Smoke Particle",
  156. minSize = 5,
  157. maxSize = 25,
  158. minSpeed = 0,
  159. maxSpeed = 0,
  160. accel = vec2(0,1000),
  161. rAccel = vec2(1500,1500),
  162. })
  163. end
  164.  
  165. function SlammerPod:stickMoved(v)
  166. self.dir = v
  167. end
  168.  
  169. function SlammerPod:stickReleased(v)
  170. self.dir = vec2(0,0)
  171. end
  172.  
  173. function SlammerPod:draw()
  174.  
  175. self.anim = self.anim + DeltaTime
  176.  
  177. local gain = 250 * self.chassis.mass
  178. local damp = 0.75
  179. local vel = self.chassis.linearVelocity
  180. self.chassis:applyForce(gain * self.dir - damp * vel)
  181. self.chassis.linearDamping = vel:len() / 50
  182.  
  183. pushMatrix()
  184. translate(self.chassis.x, self.chassis.y)
  185.  
  186. rotate(self.chassis.angle)
  187. sprite("Tyrian Remastered:Part H", 0, 0, 60)
  188.  
  189. pushMatrix()
  190. rotate(self.anim * 100)
  191. sprite("Tyrian Remastered:Orb 2", 0,0,30)
  192. popMatrix()
  193.  
  194. popMatrix()
  195.  
  196. pushMatrix()
  197. translate(self.rock.x, self.rock.y)
  198. rotate(self.rock.angle)
  199. sprite("Tyrian Remastered:Mine Spiked Huge", 0, 0, self.rock.radius * 2.5)
  200. popMatrix()
  201.  
  202. for k,v in ipairs(self.links) do
  203. pushMatrix()
  204. translate(v.x,v.y)
  205. rotate(v.angle)
  206. sprite("Tyrian Remastered:Ring", 0,0,15,25)
  207. popMatrix()
  208. end
  209.  
  210. self.sparks:draw()
  211.  
  212. --self.()stick:draw()
  213. end
  214.  
  215. function SlammerPod:hit(other, contact, first)
  216. if contact.state == BEGAN then
  217. local imp = math.max(0, (contact.normalImpulse-25) / 25)
  218. if imp > 0.0 then
  219. self.sparks:emit(contact.position, 10)
  220. sound(SOUND_HIT, 47051, imp)
  221. if other and other.damage then other:damage(imp) end
  222. end
  223. end
  224. end
  225.  
  226. --# Worm
  227. Worm = class()
  228.  
  229. function Worm:init(x,y,t)
  230. self.name = "worm"
  231.  
  232. -- you can accept and set parameters here
  233. self.body = createCircle(x,y,35)
  234. self.body.info = self
  235. self.body.gravityScale = 0
  236. self.target = t
  237. self.dir = vec2(0,0)
  238. self.health = 1
  239. self.power = 1
  240. self.stun = 0
  241.  
  242. self.base = physics.body(CIRCLE, 35)
  243. self.base.fixedRotation = true
  244. self.base.gravityScale = 0
  245. self.base.position = self.body.position
  246. self.base.mask = {15}
  247. self.servo = physics.joint(REVOLUTE, self.base, self.body, self.base.position)
  248. self.servo.enableMotor = true
  249. self.servo.maxMotorTorque = 1000
  250. self.motorSpeed = 0
  251. end
  252.  
  253. function Worm:draw()
  254. self.dir = (self.target.chassis.position - self.body.position)
  255. self.dir = self.dir:normalize()
  256. -- Codea does not automatically call this method
  257. local gain = 29.400 * self.body.mass * self.power
  258. local damp = 0.75 * self.power
  259. local vel = self.body.linearVelocity
  260. self.body:applyForce(gain * self.dir - damp * vel)
  261. self.body.linearDamping = vel:len() / 100 * self.power
  262. self.body.gravityScale = 1-self.power
  263.  
  264. local tAng = math.atan2(self.dir.y, self.dir.x) + math.pi/2
  265. self.servo.motorSpeed = (tAng - math.rad(self.servo.jointAngle)) * 100
  266. --if self.power == 0
  267.  
  268. pushMatrix()
  269. translate(self.body.x, self.body.y)
  270. rotate(self.body.angle)
  271. sprite("Tyrian Remastered:Organic Claw", 0,-14,60)
  272. sprite("Tyrian Remastered:Part N")
  273. popMatrix()
  274.  
  275. fill(120, 30, 30, 255)
  276. rect(self.body.x,self.body.y+50,50,10)
  277. fill(255, 0, 0, 255)
  278. rect(self.body.x,self.body.y+50,50*self.health,10)
  279. end
  280.  
  281. function Worm:damage(dmg)
  282. self.health = math.max(0, self.health - dmg)
  283. if self.health == 0 then
  284. self.power = 0
  285. self.servo.enableMotor = false
  286. sound(SOUND_RANDOM, 41402, 1)
  287.  
  288. end
  289. end
  290.  
  291.  
  292. WormPart = class()
  293.  
  294. function WormPart:init(x,y,r,worm,parent)
  295. self.body = physics.body(CIRCLE, r)
  296. self.body.x = x
  297. self.body.y = y
  298.  
  299. self.worm = worm
  300. self.health = 1
  301. end
  302.  
  303.  
  304.  
  305. --# Main
  306. supportedOrientations(LANDSCAPE_ANY)
  307. --displayMode(FULLSCREEN)
  308. physics.resume()
  309.  
  310. -- Use this function to perform your initial setup
  311. function setup()
  312. debugDraw = PhysicsDebugDraw()
  313.  
  314. -- test classes
  315. -- to add your own, make sure to define setup() and cleanup()
  316. -- in addition to draw() and touched()
  317. tests = {Test1()}
  318. iparameter("test_number", 1, #tests)
  319. iparameter("use_accelerometer", 0, 1)
  320.  
  321. currentTestIndex = 1
  322. currentTest = nil
  323. setTest(currentTestIndex)
  324.  
  325. defaultGravity = physics.gravity()
  326. end
  327.  
  328. function setTest(t)
  329. if currentTest then
  330. if currentTest.cleanup then
  331. currentTest:cleanup()
  332. end
  333. cleanup()
  334. end
  335.  
  336. currentTestIndex = t
  337. currentTest = tests[t]
  338. currentTest:setup()
  339. end
  340.  
  341. function nextTest()
  342. local t = currentTestIndex + 1
  343. if t > #tests then
  344. t = 1
  345. end
  346. setTest(t)
  347. end
  348.  
  349. function createCircle(x,y,r)
  350. local circle = physics.body(CIRCLE, r)
  351. -- enable smooth motion
  352. circle.interpolate = true
  353. circle.x = x
  354. circle.y = y
  355. circle.restitution = 0.25
  356. debugDraw:addBody(circle)
  357. return circle
  358. end
  359.  
  360. function createBox(x,y,w,h)
  361. -- polygons are defined by a series of points in counter-clockwise order
  362. local box = physics.body(POLYGON, vec2(-w/2,h/2), vec2(-w/2,-h/2), vec2(w/2,-h/2), vec2(w/2,h/2))
  363. box.interpolate = true
  364. box.x = x
  365. box.y = y
  366. box.restitutions = 0.25
  367. debugDraw:addBody(box)
  368. return box
  369. end
  370.  
  371. function createGround()
  372. local ground = physics.body(POLYGON, vec2(0,20), vec2(0,0), vec2(WIDTH,0), vec2(WIDTH,20))
  373. ground.type = STATIC
  374. debugDraw:addBody(ground)
  375. return ground
  376. end
  377.  
  378. function cleanup()
  379. debugDraw:clear()
  380. end
  381.  
  382. -- This function gets called once every frame
  383. function draw()
  384. -- This sets the background color to black
  385. background(0, 0, 0)
  386.  
  387. if test_number ~= currentTestIndex then
  388. setTest(test_number)
  389. end
  390.  
  391. currentTest:draw()
  392. --debugDraw:draw()
  393.  
  394. local str = string.format("Test %d - %s", currentTestIndex, currentTest.title)
  395.  
  396. text(str, WIDTH/2, HEIGHT - 20)
  397.  
  398. if use_accelerometer == 1 then
  399. physics.gravity(Gravity)
  400. else
  401. --physics.gravity(defaultGravity)
  402. end
  403. end
  404.  
  405. function touched(touch)
  406. if debugDraw:touched(touch) == false then
  407. currentTest:touched(touch)
  408. end
  409. end
  410.  
  411. function collide(contact)
  412. --debugDraw:collide(contact)
  413. if currentTest.collide then
  414. currentTest:collide(contact)
  415. end
  416. end
  417.  
  418. --# Test1
  419. Test1 = class()
  420.  
  421. function Test1:init()
  422. self.title = "SlammerFall"
  423. end
  424.  
  425. function Test1:setup()
  426. POD = 0
  427. WORM = 1
  428.  
  429. owners = {}
  430.  
  431. self.levelWidth = 1024*4
  432. self.levelHeight = 768*2
  433.  
  434. self.stars = mesh()
  435. self.stars.texture = "SpaceCute:Star"
  436. self.stars2 = mesh()
  437. self.stars2.texture = "SpaceCute:Star"
  438. for i = 1,200 do
  439. local size = math.random(5,15)
  440. self.stars:addRect(math.random(0,self.levelWidth), math.random(0,self.levelHeight), size,size)
  441. self.stars2:addRect(math.random(0,self.levelWidth*2), math.random(0,self.levelHeight*2), size,size)
  442. end
  443.  
  444. self.pod = SlammerPod(self.levelWidth/2, HEIGHT/2)
  445. self.worm1 = Worm(self.levelWidth, HEIGHT, self.pod)
  446. self.worm1.name = "worm1"
  447. self.worm2 = Worm(self.levelWidth/2, HEIGHT, self.pod)
  448. self.worm2.name = "worm2"
  449. self.worm3 = Worm(9, HEIGHT, self.pod)
  450. self.worm3.name = "worm3"
  451.  
  452. self.camPos = self.pod.chassis.position
  453.  
  454. self.landMesh = mesh()
  455. local points = {vec2(0,0)}
  456. for x = 0,self.levelWidth,50 do
  457. table.insert(points, vec2(x,50 + math.random(0, 25)))
  458. end
  459. table.insert(points, vec2(self.levelWidth, 0))
  460. self.landMesh.vertices = triangulate(points)
  461. self.landMesh:setColors(128, 128, 128)
  462. self.land = physics.body(CHAIN, true, unpack(points))
  463. self.land.type = STATIC
  464. self.land.restitution = 0.3
  465.  
  466. self.leftBarrier = physics.body(EDGE, vec2(0,0), vec2(0,self.levelHeight))
  467. self.rightBarrier = physics.body(EDGE, vec2(self.levelWidth,0), vec2(self.levelWidth,self.levelHeight))
  468. self.topBarrier = physics.body(EDGE, vec2(0,self.levelHeight), vec2(self.levelWidth,self.levelHeight))
  469. end
  470.  
  471. function Test1:draw()
  472. -- Codea does not automatically call this method
  473.  
  474. pushMatrix()
  475. local interp = 0.95
  476. self.camPos = self.camPos * interp + self.pod.chassis.position * (1-interp)
  477. self.camPos.x = math.max(self.camPos.x, WIDTH/2)
  478. self.camPos.x = math.min(self.camPos.x, self.levelWidth - WIDTH/2)
  479. self.camPos.y = math.max(self.camPos.y, HEIGHT/2)
  480. self.camPos.y = math.min(self.camPos.y, self.levelHeight - HEIGHT/2)
  481.  
  482. pushMatrix()
  483. translate(-self.camPos.x/2 + WIDTH/2, -self.camPos.y/2 + HEIGHT/2)
  484. self.stars2:draw()
  485. popMatrix()
  486.  
  487. translate(-self.camPos.x + WIDTH/2, -self.camPos.y + HEIGHT/2)
  488.  
  489. self.stars:draw()
  490. self.landMesh:draw()
  491. self.pod:draw()
  492. self.worm1:draw()
  493. self.worm2:draw()
  494. self.worm3:draw()
  495.  
  496. popMatrix()
  497. end
  498.  
  499. function Test1:collide(contact)
  500. if contact.state ~= BEGAN then return end
  501.  
  502. local infoA = contact.bodyA.info
  503. local infoB = contact.bodyB.info
  504.  
  505. if infoA and infoA.hit then
  506. infoA:hit(infoB, contact, true)
  507. end
  508. if infoB and infoB.hit then
  509. infoB:hit(infoA, contact, false)
  510. end
  511. end
  512.  
  513. --# PhysicsDebugDraw
  514. PhysicsDebugDraw = class()
  515.  
  516. function PhysicsDebugDraw:init()
  517. self.bodies = {}
  518. self.joints = {}
  519. self.touchMap = {}
  520. self.contacts = {}
  521. end
  522.  
  523. function PhysicsDebugDraw:addBody(body)
  524. table.insert(self.bodies,body)
  525. end
  526.  
  527. function PhysicsDebugDraw:addJoint(joint)
  528. table.insert(self.joints,joint)
  529. end
  530.  
  531. function PhysicsDebugDraw:clear()
  532. -- deactivate all bodies
  533.  
  534. for i,body in ipairs(self.bodies) do
  535. body:destroy()
  536. end
  537.  
  538. for i,joint in ipairs(self.joints) do
  539. joint:destroy()
  540. end
  541.  
  542. self.bodies = {}
  543. self.joints = {}
  544. self.contacts = {}
  545. self.touchMap = {}
  546. end
  547.  
  548. function PhysicsDebugDraw:draw()
  549.  
  550. pushStyle()
  551. smooth()
  552. strokeWidth(5)
  553. stroke(128,0,128)
  554.  
  555. local gain = 2.0
  556. local damp = 0.5
  557. for k,v in pairs(self.touchMap) do
  558. local worldAnchor = v.body:getWorldPoint(v.anchor)
  559. local touchPoint = v.tp
  560. local diff = touchPoint - worldAnchor
  561. local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
  562. v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)
  563.  
  564. line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
  565. end
  566.  
  567. stroke(0,255,0,255)
  568. strokeWidth(5)
  569. for k,joint in pairs(self.joints) do
  570. local a = joint.anchorA
  571. local b = joint.anchorB
  572. line(a.x,a.y,b.x,b.y)
  573. end
  574.  
  575. stroke(255,255,255,255)
  576. noFill()
  577.  
  578.  
  579. for i,body in ipairs(self.bodies) do
  580. pushMatrix()
  581. translate(body.x, body.y)
  582. rotate(body.angle)
  583.  
  584. if body.type == STATIC then
  585. stroke(255,255,255,255)
  586. elseif body.type == DYNAMIC then
  587. stroke(150,255,150,255)
  588. elseif body.type == KINEMATIC then
  589. stroke(150,150,255,255)
  590. end
  591.  
  592. if body.shapeType == POLYGON then
  593. strokeWidth(5.0)
  594. local points = body.points
  595. for j = 1,#points do
  596. a = points[j]
  597. b = points[(j % #points)+1]
  598. line(a.x, a.y, b.x, b.y)
  599. end
  600. elseif body.shapeType == CHAIN or body.shapeType == EDGE then
  601. strokeWidth(5.0)
  602. local points = body.points
  603. for j = 1,#points-1 do
  604. a = points[j]
  605. b = points[j+1]
  606. line(a.x, a.y, b.x, b.y)
  607. end
  608. elseif body.shapeType == CIRCLE then
  609. strokeWidth(5.0)
  610. line(0,0,body.radius-3,0)
  611. strokeWidth(2.5)
  612. ellipse(0,0,body.radius*2)
  613. end
  614.  
  615. popMatrix()
  616. end
  617.  
  618. stroke(255, 0, 0, 255)
  619. fill(255, 0, 0, 255)
  620.  
  621. for k,v in pairs(self.contacts) do
  622. for m,n in ipairs(v.points) do
  623. ellipse(n.x, n.y, 10, 10)
  624. end
  625. end
  626.  
  627. popStyle()
  628. end
  629.  
  630. function PhysicsDebugDraw:touched(touch)
  631. local touchPoint = vec2(touch.x, touch.y)
  632. if touch.state == BEGAN then
  633. for i,body in ipairs(self.bodies) do
  634. if body.type == DYNAMIC and body:testPoint(touchPoint) then
  635. self.touchMap[touch.id] = {tp = touchPoint, body = body, anchor = body:getLocalPoint(touchPoint)}
  636. return true
  637. end
  638. end
  639. elseif touch.state == MOVING and self.touchMap[touch.id] then
  640. self.touchMap[touch.id].tp = touchPoint
  641. return true
  642. elseif touch.state == ENDED and self.touchMap[touch.id] then
  643. self.touchMap[touch.id] = nil
  644. return true;
  645. end
  646. return false
  647. end
  648.  
  649. function PhysicsDebugDraw:collide(contact)
  650. if contact.state == BEGAN then
  651. self.contacts[contact.id] = contact
  652. elseif contact.state == MOVING then
  653. self.contacts[contact.id] = contact
  654. elseif contact.state == ENDED then
  655. self.contacts[contact.id] = nil
  656. end
  657. end
  658.  
  659. --# Controller
  660. -- Base class for controllers
  661. --
  662. -- Controllers translate touch events into callbacks to functions
  663. -- that do something in the app. (Model/View/Controller style).
  664. --
  665. -- Controllers can draw a representation of their current state on
  666. -- the screen, but you can choose not to.
  667. --
  668. -- A controller can be installed as the global handler for touch
  669. -- events by calling its activate() method
  670.  
  671. Controller = class()
  672.  
  673. function Controller:activate()
  674. touched = function(t)
  675. self:touched(t)
  676. end
  677. end
  678.  
  679. function Controller:draw()
  680. -- nothing
  681. end
  682.  
  683. -- Utility functions
  684.  
  685. function touchPos(t)
  686. return vec2(t.x, t.y)
  687. end
  688.  
  689. function clamp(x, min, max)
  690. return math.max(min, math.min(max, x))
  691. end
  692.  
  693. function clampAbs(x, maxAbs)
  694. return clamp(x, -maxAbs, maxAbs)
  695. end
  696.  
  697. function clampLen(vec, maxLen)
  698. return vec:normalize() * math.min(vec:len(), maxLen)
  699. end
  700.  
  701. -- projects v onto the direction represented by the given unit vector
  702. function project(v, unit)
  703. return v:dot(unit)
  704. end
  705.  
  706. function sign(x)
  707. if x == 0 then
  708. return 0
  709. elseif x < 0 then
  710. return -1
  711. elseif x > 0 then
  712. return 1
  713. else
  714. return x -- x is NaN
  715. end
  716. end
  717.  
  718. function doNothing()
  719. end
  720.  
  721. --# VirtualStick
  722. -- A virtual analogue joystick with a dead-zone at the center,
  723. -- which activates wherever the user touches their finger
  724. --
  725. -- Arguments:
  726. -- radius - radius of the stick (default = 100)
  727. -- deadZoneRadius - radius of the stick's dead zone (default = 25)
  728. -- moved(v) - Called when the stick is moved
  729. -- v : vec2 - in the range vec2(-1,-1) and vec2(1,1)
  730. -- pressed() - Called when the user starts using the stick (optional)
  731. -- released() - Called when the user releases the stick (optional)
  732.  
  733. VirtualStick = class(Controller)
  734.  
  735. function VirtualStick:init(args)
  736. self.radius = args.radius or 100
  737. self.deadZoneRadius = args.deadZoneRadius or 25
  738. self.releasedCallback = args.released or doNothing
  739. self.steerCallback = args.moved or doNothing
  740. self.pressedCallback = args.pressed or doNothing
  741. end
  742.  
  743. function VirtualStick:touched(t)
  744. local pos = touchPos(t)
  745.  
  746. if t.state == BEGAN and self.touchId == nil then
  747. self.touchId = t.id
  748. self.touchStart = pos
  749. self.stickOffset = vec2(0, 0)
  750. self.pressedCallback()
  751. elseif t.id == self.touchId then
  752. if t.state == MOVING then
  753. self.stickOffset = clampLen(pos - self.touchStart, self.radius)
  754. self.steerCallback(self:vector())
  755. elseif t.state == ENDED then
  756. self:reset()
  757. self.releasedCallback()
  758. end
  759. end
  760. end
  761.  
  762. function VirtualStick:vector()
  763. local stickRange = self.radius - self.deadZoneRadius
  764. local stickAmount = math.max(self.stickOffset:len() - self.deadZoneRadius, 0)
  765. local stickDirection = self.stickOffset:normalize()
  766.  
  767. return stickDirection * (stickAmount/stickRange)
  768. end
  769.  
  770. function VirtualStick:reset()
  771. self.touchId = nil
  772. self.touchStart = nil
  773. self.stickOffset = nil
  774. end
  775.  
  776. function VirtualStick:draw()
  777. if self.touchId ~= nil then
  778. pushStyle()
  779. ellipseMode(RADIUS)
  780. strokeWidth(1)
  781. stroke(255, 255, 255, 255)
  782. noFill()
  783.  
  784. pushMatrix()
  785. translate(self.touchStart.x, self.touchStart.y)
  786. ellipse(0, 0, self.radius, self.radius)
  787. ellipse(0, 0, self.deadZoneRadius, self.deadZoneRadius)
  788. translate(self.stickOffset.x, self.stickOffset.y)
  789. ellipse(0, 0, 25, 25)
  790. popMatrix()
  791.  
  792. popStyle()
  793. end
  794. end
Add Comment
Please, Sign In to add comment