collision.lua 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. local Collision = class()
  2. Collision.cellSize = 128
  3. local check = {}
  4. function Collision:init()
  5. self.hc = require 'lib/hc'
  6. ctx.event:on('collision.attach', f.cur(self.attach, self))
  7. ctx.event:on('collision.detach', f.cur(self.detach, self))
  8. ctx.event:on('collision.move', f.cur(self.move, self))
  9. end
  10. function Collision:attach(data)
  11. local obj = data.object
  12. local shape
  13. if obj.collision.shape == 'rectangle' then
  14. shape = self.hc.rectangle(obj.x, obj.y, obj.width, obj.height)
  15. elseif obj.collision.shape == 'circle' then
  16. shape = self.hc.circle(obj.x, obj.y, obj.radius)
  17. end
  18. obj.shape = shape
  19. shape.owner = obj
  20. return shape
  21. end
  22. function Collision:detach(data)
  23. self.hc.remove(data.object.shape)
  24. data.object.shape = nil
  25. end
  26. function Collision:move(data)
  27. local x, y = data.x or data.object.x, data.y or data.object.y
  28. if not x or not y then return end
  29. if data.object.collision.shape == 'rectangle' then
  30. x = x + data.object.width / 2
  31. y = y + data.object.height / 2
  32. end
  33. data.object.shape:moveTo(x, y)
  34. if data.resolve then
  35. self:resolve(data.object)
  36. end
  37. end
  38. function Collision:resolve(object)
  39. local a = object
  40. local check = {}
  41. for other, vector in pairs(self.hc.collisions(object.shape)) do
  42. local b, dx, dy = other.owner, vector.x, vector.y
  43. f.exe(a.collision.with and a.collision.with[b.collision.tag], a, b, dx, dy)
  44. f.exe(b.collision.with and b.collision.with[a.collision.tag], b, a, -dx, -dy)
  45. end
  46. end
  47. function Collision:pointTest(x, y, options)
  48. options = options or {}
  49. local tag, fn = options.tag, options.fn
  50. local detector = self.hc.point(x, y)
  51. for _, shape in pairs(self.hc.collisions(detector)) do
  52. if not tag or shape.owner.collision.tag == tag then
  53. if not fn or fn(shape.owner) then
  54. self.hc.remove(detector)
  55. return shape.owner
  56. end
  57. end
  58. end
  59. self.hc.remove(detector)
  60. return nil
  61. end
  62. function Collision:lineTest(x1, y1, x2, y2, options)
  63. local dis = math.distance(x1, y1, x2, y2)
  64. local _x1, _y1 = math.min(x1, x2), math.min(y1, y2)
  65. local _x2, _y2 = math.max(x1, x2), math.max(y1, y2)
  66. local tag, fn, first, all = options.tag, options.fn, options.first, options.all
  67. local mindis = first and math.huge or nil
  68. local res = all and {} or nil
  69. for shape in pairs(self.hc:hash():shapes()) do
  70. if (not tag) or shape.owner.collision.tag == tag then
  71. local intersects, d = shape:intersectsRay(x1, y1, x2 - x1, y2 - y1)
  72. if intersects and d >= 0 and d <= 1 then
  73. if (not fn) or fn(shape.owner) then
  74. if not first then
  75. if all then
  76. table.insert(res, shape.owner)
  77. else
  78. return shape.owner, d * dis
  79. end
  80. elseif d * dis < mindis then
  81. mindis = d * dis
  82. res = shape.owner
  83. end
  84. end
  85. end
  86. end
  87. end
  88. return res, first and mindis or nil
  89. end
  90. function Collision:circleTest(x, y, r, options)
  91. local detector = self.hc.circle(x, y, r)
  92. local tag, fn, all = options.tag, options.fn, options.all
  93. local res = all and {} or nil
  94. for shape in pairs(self.hc.collisions(detector)) do
  95. if (not tag) or shape.owner.collision.tag == tag then
  96. if (not fn) or fn(shape.owner) then
  97. if all then
  98. table.insert(res, shape.owner)
  99. else
  100. self.hc.remove(detector)
  101. return shape.owner
  102. end
  103. end
  104. end
  105. end
  106. self.hc.remove(detector)
  107. return res
  108. end
  109. return Collision