Skip to content

Commit

Permalink
Unstable collision detection
Browse files Browse the repository at this point in the history
  • Loading branch information
N1ckn1ght authored Dec 2, 2022
1 parent 76d02bc commit 5791c65
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 27 deletions.
59 changes: 51 additions & 8 deletions boid.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
Boid = {}
Boid.__index = Boid

function Boid:create(size)
function Boid:create(x, y, size, k)
local boid = {}
setmetatable(boid, Boid)

boid.location = Vector:create(x, y)
boid.acceleration = Vector:create(0, 0)
boid.velocity = Vector:create(0, 0)
boid.angle = 0
boid.size = size

boid.location = Vector:create(50 + size / 2, Height / 3)
boid.vertices = {0, -size * 1.6, -size, size * 1.6, 0, size, size, size * 1.6}
boid.velocity = Vector:create(0, 0)
boid.angle = 0
boid.size = size
boid.k = k
boid.vertices = {0, -size * k, -size, size * k, 0, size, size, size * k}
boid.hitboxes = {{1, 3, 2}, {1, 4, 3}}
boid.color = {0, 1, 1, 1}

return boid
end
Expand All @@ -37,7 +39,7 @@ function Boid:draw()

-- save love graphics color
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(0, 1, 1, 1)
love.graphics.setColor(self.color)
love.graphics.polygon("fill", self.vertices)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.polygon("line", self.vertices)
Expand All @@ -49,4 +51,45 @@ end

function Boid:applyForce(force)
self.acceleration:add(force)
end

-- Collision detection stuff --

function Boid:getNormals(part)
local normals = {}
local current_dot = nil
local previous_dot = nil
for i = 1, #self.hitboxes[part] do
previous_dot = current_dot or self:getDot(self.hitboxes[part][#self.hitboxes[part]])
current_dot = self:getDot(self.hitboxes[part][i])
local px = current_dot.x - previous_dot.x
local py = current_dot.y - previous_dot.y
normals[i] = Vector:create(-py, px)
end
return normals
end

function Boid:getMinMaxProj(part, axis)
local dot = self:getDot(self.hitboxes[part][1])
local min_proj = dotProduct(Vector:create(dot.x, dot.y), axis)
local max_proj = min_proj
for i = 2, #self.hitboxes[part] do
dot = self:getDot(self.hitboxes[part][i])
local curr_proj = dotProduct(Vector:create(dot.x, dot.y), axis)
if (curr_proj < min_proj) then
min_proj = curr_proj
end
if (curr_proj > max_proj) then
max_proj = curr_proj
end
end
return {min_proj, max_proj}
end

function Boid:getDot(index)
local x = self.vertices[index * 2 - 1]
local y = self.vertices[index * 2]
local xReal = x * math.cos(self.angle) - y * math.sin(self.angle) + self.location.x
local yReal = x * math.sin(self.angle) + y * math.cos(self.angle) + self.location.y
return Vector:create(xReal, yReal)
end
74 changes: 70 additions & 4 deletions collisionDetector.lua
Original file line number Diff line number Diff line change
@@ -1,21 +1,87 @@
CollisionDetector = {}
CollisionDetector.__index = CollisionDetector

function CollisionDetector:create()
function CollisionDetector:create(onCollision)
local collisionDetector = {}
setmetatable(collisionDetector, CollisionDetector)

self.onCollision = onCollision
self.trackablePlayers = {}
self.trackableFields = {}
self.trackableBorders = {}

return collisionDetector
end

function CollisionDetector:update()

for _, player in pairs(self.trackablePlayers) do
if (self:fieldsCollisionCheckOnPlayer(player) or self:bordersCollisionCheckOnPlayer(player)) then
-- gimmic condition
end
end
end

function CollisionDetector:trackPlayer(player)
-- Will require such fields, as: hitboxes (reference to vertices), getVertice(n) with absolute positioning including angle calculations.
self.trackablePlayers[#self.trackablePlayers + 1] = player
end

function CollisionDetector:trackField(field)
-- For now it's really just for a specific field.lua class.
self.trackableFields[#self.trackableFields + 1] = field
end

function CollisionDetector:trackPlayer()
function CollisionDetector:trackBorder(border)
-- Border must be a table of two vectors, e.g. {Vector:create(0, 0), Vector:create(1, 1)}
self.trackableBorders[#self.trackableBorders + 1] = border
end

function CollisionDetector:bordersCollisionCheckOnPlayer(player)
return false
end

function CollisionDetector:trackObstacles()
function CollisionDetector:fieldsCollisionCheckOnPlayer(player)
-- distinct player boid to convex parts
local npps = {}
for i = 1, #player.hitboxes do
npps[i] = player:getNormals(i)
end

for _, field in pairs(self.trackableFields) do
-- cycling through all pipe pairs, starting from field.curr
for i = 1, field.count do
local index = (i + field.curr - 2) % field.count + 1
-- now cycling through all parts of given pipe pair (2 for lower, 2 for upper)
for j = 1, #field.pipe do
local npf = field:getNormals(index, j)
col = true
-- now cycling thourgh distinct convex part of player
for k = 1, #player.hitboxes do
local npp = npps[k]

for _, np in pairs({npp, nf}) do
for _, v in pairs(np) do
local p = player:getMinMaxProj(k, v)
local q = field:getMinMaxProj(index, j, v)

if ((p[2] < q[1]) or (q[2] < p[1])) then
col = false
break
end
end
if (not col) then
break
end
end

if (col) then
onCollision(player, index, rect)
return true
end
end
end
end
end

return false
end
62 changes: 52 additions & 10 deletions field.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
Field = {}
Field.__index = Field

function Field:create(velocity, pipeDistance, pipeGap, pipeWidth, pipeEndWidth, pipeEndHeight, acceleration, velocityLimit, accelerationType)
function Field:create(velocity, initialDistance, pipeDistance, pipeGap, pipeWidth, pipeEndWidth, pipeEndHeight, acceleration, velocityLimit, accelerationType)
local field = {}
setmetatable(field, Field)

field.velocity = velocity
field.initialDistance = initialDistance
field.pipeDistance = pipeDistance
field.pipeGap = pipeGap
field.pipeWidth = pipeWidth
Expand All @@ -14,12 +15,13 @@ function Field:create(velocity, pipeDistance, pipeGap, pipeWidth, pipeEndWidth,
field.acceleration = acceleration or 0
field.limit = velocityLimit or velocity
field.accelerationType = accelerationType or 1
field.pipe = {}
field.pipes = {}
field.curr = 1
field.last = 0
field.count = 0
field.distance = 0

field.pipe = {}
field.pipes = {}
field.curr = 1
field.last = 0
field.count = 0
return field
end

Expand All @@ -33,18 +35,19 @@ function Field:init()

self.count = math.ceil((Width + math.max(self.pipeWidth, self.pipeEndWidth)) / self.pipeDistance + 1)
for i = self.curr, self.count do
self.pipes[i] = self:randomPipe(Width / 2 + i * self.pipeDistance)
self.pipes[i] = self:randomPipe(self.initialDistance + i * self.pipeDistance)
end
self.last = self.count
end

function Field:update(dt)
local dbf = dt * self.velocity
self.distance = self.distance + dbf -- general distance counter
for k, v in pairs(self.pipes) do
-- always moving backwards
v[1] = v[1] - dt * self.velocity
v[1] = v[1] - dbf -- always moving pipes backwards
if (v[1] < -math.max(self.pipeWidth, self.pipeEndWidth)) then
-- respawn pipe with random y (if it's not on screen anymore)
self.pipes[k] = self:randomPipe(self.pipes[self.last][1] + self.pipeDistance)

self.last = self.curr
self.curr = self.curr + 1
if (self.curr > self.count) then
Expand Down Expand Up @@ -89,4 +92,43 @@ function Field:randomPipe(x)
-- x center is the pipeWidth / 2
-- y center is the center of pipeGap
return {x, math.random() * (Height - self.pipeEndHeight * 2 - self.pipeGap * 2) + self.pipeEndHeight + self.pipeGap, {math.random(), math.random(), math.random()}}
end

-- Collision detection stuff --

function Field:getNormals(index, rect)
local normals = {}
local current_dot = nil
local previous_dot = nil
for i = 1, #self.pipe[rect] / 2 do
previous_dot = current_dot or self:getDot(index, rect, #self.pipe[rect] / 2)
current_dot = self:getDot(index, rect, i)
local px = current_dot.x - previous_dot.x
local py = current_dot.y - previous_dot.y
normals[i] = Vector:create(-py, px)
end
return normals
end

function Field:getMinMaxProj(index, rect, axis)
local dot = self:getDot(index, rect, 1)
local min_proj = dotProduct(Vector:create(dot.x, dot.y), axis)
local max_proj = min_proj
for i = 2, #self.pipe[rect] / 2 do
dot = self:getDot(index, rect, i)
local curr_proj = dotProduct(Vector:create(dot.x, dot.y), axis)
if (curr_proj < min_proj) then
min_proj = curr_proj
end
if (curr_proj > max_proj) then
max_proj = curr_proj
end
end
return {min_proj, max_proj}
end

function Field:getDot(index, rect, point)
local x = self.pipe[rect][point * 2 - 1] + self.pipes[index][1]
local y = self.pipe[rect][point * 2] + self.pipes[index][2]
return Vector:create(x, y)
end
37 changes: 32 additions & 5 deletions main.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "vector"
require "boid"
require "field"
require "collisionDetector"

function love.load()
math.randomseed(os.time())
Expand All @@ -10,15 +11,36 @@ function love.load()
G = 1500
VelocityOnClick = 520

Player = Boid:create(13)
Pipes = Field:create(250, 550, 150, 60, 80, 30, 0.003, 1000, 2)
InitialDistance = Width / 2
InitialX = 150

Player = Boid:create(InitialX, Height / 5, 13, 1.6)
Pipes = Field:create(250, InitialDistance, 550, 150, 60, 80, 30, 0.003, 1000, 2)
Detector = CollisionDetector:create(onCollision)

Pipes:init()
Detector:trackPlayer(Player)
Detector:trackField(Pipes)
Detector:trackBorder({Vector:create(0, 0), Vector:create(Width, 0)})
Detector:trackBorder({Vector:create(0, Height), Vector:create(Width, Height)})

Score = 0
Flag = true
end

function love.update(dt)
Player:applyForce(Vector:create(0, G) * dt)
Player:update(dt)
Pipes:update(dt)
if (Flag) then
Player:applyForce(Vector:create(0, G) * dt)
Player:update(dt)
Pipes:update(dt)
Detector:update()

local newScore = math.floor((Pipes.distance - InitialDistance + InitialX) / Pipes.pipeDistance)
if (newScore > Score) then
Score = newScore
print("New score:", Score)
end
end
end

function love.draw()
Expand All @@ -31,3 +53,8 @@ function love.mousepressed(x, y, button)
Player.velocity.y = -VelocityOnClick
end
end

function onCollision(player, index, rect)
Flag = false
player.color = {1, 0, 0, 1}
end
4 changes: 4 additions & 0 deletions vector.lua
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,8 @@ end

function Vector:distTo(other)
return math.sqrt((other.x - self.x) * (other.x - self.x) + (other.y - self.y) * (other.y - self.y))
end

function dotProduct(vec1, vec2)
return (vec1.x * vec2.x + vec1.y * vec2.y)
end

0 comments on commit 5791c65

Please sign in to comment.