collision.lua 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. local collision = lib.object.create()
  2. function collision:init()
  3. self.grid = {}
  4. self.gridSize = {
  5. x = 70,
  6. y = 70 / 1.5
  7. }
  8. end
  9. collision.handlers = {
  10. ['circle:circle'] = function(o1, o2)
  11. local x1, y1, x2, y2, r1, r2 = o1.position.x, o1.position.y, o2.position.x, o2.position.y, o1.config.radius, o2.config.radius
  12. local dis = util.distance(x1, y1, x2, y2)
  13. if dis <= r1 + r2 then
  14. local dir = util.angle(x1, y1, x2, y2)
  15. local overlap = (r1 + r2) - dis
  16. return overlap * math.cos(dir), overlap * math.sin(dir)
  17. end
  18. end,
  19. ['ellipse:ellipse'] = function(o1, o2)
  20. local x1, y1, a1, b1 = o1.position.x, o1.position.y, o1.config.radius, o1.config.radius / o1.config.perspective
  21. local x2, y2, a2, b2 = o2.position.x, o2.position.y, o2.config.radius, o2.config.radius / o2.config.perspective
  22. local dir = util.angle(x1, y1, x2, y2)
  23. local theta = math.abs(math.sin(dir))
  24. local d1 = util.lerp(a1, b1, theta)
  25. local d2 = util.lerp(a2, b2, theta)
  26. local overlap = (d1 + d2) - util.distance(x1, y1, x2, y2)
  27. if overlap > 0 then
  28. return overlap * math.cos(dir), overlap * math.sin(dir)
  29. end
  30. end,
  31. ['circle:ellipse'] = function(o1, o2)
  32. local x1, y1, x2, y2, r1, a, b = o1.position.x, o1.position.y, o2.position.x, o2.position.y, o1.config.radius, o2.config.radius, o2.config.radius / o2.config.perspective
  33. local dis = util.distance(x1, y1, x2, y2)
  34. local dir = util.angle(x1, y1, x2, y2)
  35. local r2 = (a * b) / math.sqrt((b * math.cos(dir)) ^ 2 + (a * math.sin(dir)) ^ 2)
  36. local ex = x2 + math.cos(dir + math.pi) * r2
  37. local ey = y2 + math.sin(dir + math.pi) * r2
  38. local overlap = r1 - util.distance(x1, y1, ex, ey)
  39. if overlap > 0 then
  40. return overlap * math.cos(dir), overlap * math.sin(dir)
  41. end
  42. end
  43. }
  44. function collision:bind()
  45. return {
  46. love.update
  47. :subscribe(function()
  48. for cell, objects in pairs(self.grid) do
  49. for object in pairs(objects) do
  50. self:refresh(object)
  51. for neighbor in pairs(self:neighbors(object)) do
  52. local dx, dy = self:resolve(object, neighbor)
  53. if dx and dy and object._collisions then
  54. object._collisions(neighbor, dx, dy)
  55. end
  56. end
  57. end
  58. end
  59. end)
  60. }
  61. end
  62. function collision:refresh(object)
  63. local cell = self:serialize(self:cell(object.position.x, object.position.y))
  64. if object._cell ~= cell then
  65. if object._cell and self.grid[object._cell] then
  66. self.grid[object._cell][object] = nil
  67. end
  68. object._cell = cell
  69. self.grid[cell] = self.grid[cell] or {}
  70. self.grid[cell][object] = object
  71. end
  72. end
  73. function collision:add(object)
  74. self:refresh(object)
  75. object._collisions = lib.rx.Subject.create()
  76. return object._collisions
  77. end
  78. function collision:remove(object)
  79. if object._collisions then
  80. object._collisions:onCompleted()
  81. object._collisions = nil
  82. end
  83. if object._cell and self.grid[object._cell] then
  84. self.grid[object._cell][object] = nil
  85. object._cell = nil
  86. end
  87. end
  88. function collision:serialize(x, y)
  89. return x .. ':' .. y
  90. end
  91. function collision:neighbors(object)
  92. local neighbors = {}
  93. local ox, oy = self:cell(object.position.x, object.position.y)
  94. for x = ox - 1, ox + 1 do
  95. for y = oy - 1, oy + 1 do
  96. local cell = self.grid[self:serialize(x, y)]
  97. if cell then
  98. for object in pairs(cell) do
  99. neighbors[object] = object
  100. end
  101. end
  102. end
  103. end
  104. neighbors[object] = nil
  105. return neighbors
  106. end
  107. function collision:at(x, y)
  108. return self.grid[self:serialize(x, y)]
  109. end
  110. function collision:resolve(o1, o2)
  111. local s1, s2 = o1.config.shape, o2.config.shape
  112. local dx, dy = f.try(self.handlers[s1 .. ':' .. s2], o1, o2)
  113. if dx and dy then return dx, dy end
  114. return self:invert(f.try(self.handlers[s2 .. ':' .. s1], o2, o1))
  115. end
  116. function collision:invert(x, y)
  117. if not x or not y then return x, y end
  118. return -x, -y
  119. end
  120. function collision:cell(x, y)
  121. return math.ceil(x / self.gridSize.x), math.ceil(y / self.gridSize.y)
  122. end
  123. return collision