Compare commits

...
10 Commits
Author SHA1 Message Date
Quinn f473777684 Use # operator for vector length 2021-12-28 15:53:58 -08:00
Quinn 491d94d647 Change mag to length 2021-12-28 15:40:08 -08:00
Quinn 2949f0caec Operand order no longer affects vec2 * operator 2021-12-28 15:03:32 -08:00
Quinn 465053d73b Remove elasticity argument from bounce 2021-12-28 14:26:11 -08:00
Quinn 7bab9c56f9 Implement path drawing effect 2021-12-27 12:35:44 -08:00
Quinn 56942461e7 Fix mass function use 2021-12-26 22:10:14 -08:00
Quinn 936c8fe40f Add mass to collision calculation 2021-12-26 21:49:44 -08:00
Quinn 3ec46b6bbf Fix rewind step calculation 2021-12-26 21:36:54 -08:00
Quinn 23104ec1f1 Fix transfer of velocity 2021-12-26 21:07:14 -08:00
Quinn a66ea9feee Separate logic into class files 2021-12-26 20:57:41 -08:00
5 changed files with 260 additions and 199 deletions
+65
View File
@@ -0,0 +1,65 @@
ball = {
__count = 0,
id = nil,
pos = vec2:new(0,0),
last_pos = vec2:new(0,0),
vel = vec2:new(0,0),
rad = 1,
col = 7,
mass = function(self)
return self.rad
end,
draw = function(self, shadow)
if (shadow == true) then
circfill(self.last_pos.x, self.last_pos.y, self.rad, self.col + 8)
end
circfill(self.pos.x, self.pos.y, self.rad, self.col)
end,
move = function(self)
self.last_pos = self.pos
self.pos += self.vel
end,
edge_bounce = function(self)
edge_normal = vec2:new()
for axis in all({"x","y"}) do
val = self.pos[axis]
if (val - self.rad < 0) then
self.pos[axis] = self.rad
edge_normal[axis] = 1
elseif (val + self.rad > 127) then
self.pos[axis] = 127 - self.rad
edge_normal[axis] = -1
end
end
if (edge_normal.x != 0 or edge_normal.y != 0) then
self.vel = self.vel:bounce(edge_normal)
end
end,
check_collisions = function(self, balls)
for b in all(balls) do
if (b.id != self.id and (collisions[b] == nil or collisions[b][self] == nil)) then
local diff = self.pos - b.pos
if (#diff < self.rad + b.rad) then
collisions[self] = collisions[self] or {}
collisions[self][b] = diff
end
end
end
end,
}
ball.__index = ball
function ball:new(o)
ball.__count += 1
local o = o or {}
o.id = ball.__count
o.col = o.col or o.id % 8
setmetatable(o, self)
o.__index = self
return o
end
+20 -121
View File
@@ -1,15 +1,19 @@
pal()
cls()
ball_count = 16 ball_count = 16
ball_radius = 5 max_radius = 16
function _init() function _init()
balls = {} pal({9,10,11,12,13,2,15,136,137,138,139,140,141,130,143,8},1)
balls = {}
collisions = {} collisions = {}
for i=1,ball_count do for i=1,ball_count do
add(balls, ball:new({ add(balls, ball:new({
pos=vec2:new(rnd(127),rnd(127)), pos=vec2:new(rnd(127),rnd(127)),
vel=vec2:new(rnd(2)-1,rnd(2)-1):normal(), vel=vec2:new(rnd(2)-1,rnd(2)-1):normal(),
col=rnd(15)+1 rad = i < max_radius and i or max_radius,
})) }))
end end
end end
@@ -26,9 +30,9 @@ function _update60()
end end
function _draw() function _draw()
cls() -- cls()
for b in all(balls) do for b in all(balls) do
b:draw() b:draw(true)
end end
end end
@@ -42,129 +46,24 @@ function handle_collisions()
local t_xpoint = target.pos local t_xpoint = target.pos
repeat repeat
steps += 1 steps += 1
rewind += 0.1 * steps rewind = 0.1 * steps
s_xpoint = source.pos - source.vel * rewind s_xpoint = source.pos - source.vel * rewind
t_xpoint = target.pos - target.vel * rewind t_xpoint = target.pos - target.vel * rewind
until (s_xpoint - t_xpoint):mag() >= source.rad + target.rad until rewind > 1 or #(s_xpoint - t_xpoint) >= source.rad + target.rad
source.vel = collision_vel(s_xpoint, source.vel, t_xpoint, target.vel) local s_newvel = collision_vel(s_xpoint, source.vel, t_xpoint, target.vel, source:mass(), target:mass())
target.vel = collision_vel(t_xpoint, target.vel, s_xpoint, source.vel) local t_newvel = collision_vel(t_xpoint, target.vel, s_xpoint, source.vel, target:mass(), source:mass())
source.pos = s_xpoint + source.vel * rewind source.pos = s_xpoint + source.vel * rewind
target.pos = t_xpoint + target.vel * rewind target.pos = t_xpoint + target.vel * rewind
source.vel = s_newvel
target.vel = t_newvel
end end
end end
end end
vec2 = { function collision_vel(x1,v1,x2,v2,m1,m2)
x = 0, m1 = m1 or 1
y = 0, m2 = m2 or 1
mag = function(self)
return sqrt(self.x^2+self.y^2)
end,
dot = function(self,v)
return self.x * v.x + self.y * v.y
end,
normal = function(self)
local mag = self:mag()
return vec2:new(self.x/mag, self.y/mag)
end,
bounce = function(self, n, b)
local b = b or 1
local n = n:normal()
return (n * -2 * self:dot(n) + self) * b
end
}
vec2.__eq = function(v1,v2) return v1.x == v2.x and v1.y == v2.y end
vec2.__add = function(v1,v2) return vec2:new(v1.x+v2.x,v1.y+v2.y) end
vec2.__sub = function(v1,v2) return vec2:new(v1.x-v2.x,v1.y-v2.y) end
vec2.__mul = function(v,s) return vec2:new(v.x*s,v.y*s) end
vec2.__div = function(v,s) return vec2:new(v.x/s,v.y/s) end
vec2.__unm = function(v) return vec2:new(-v.x,-v.y) end
vec2.__tostring = function(v) return "{x: "..v.x..",y: "..v.y.."}" end
vec2.__index = vec2
function vec2:new(x,y)
local x = x or 0
local y = y or 0
local o = {x=x,y=y}
setmetatable(o, self)
o.__index = self
return o
end
function vec2:print_debug(v)
local v = v or vec2:new(0,1)
print("vec: "..vec2.__tostring(self))
print("mag: "..self:mag())
print("dot: "..self:dot(v))
print("normal: "..vec2.__tostring(self:normal()))
print("bounce: "..vec2.__tostring(self:bounce(v)))
print("inv: "..vec2.__tostring(-self))
end
ball = {
__count = 0,
id = nil,
pos = vec2:new(0,0),
vel = vec2:new(0,0),
rad = ball_radius,
col = 7,
draw = function(self)
circfill(self.pos.x, self.pos.y, self.rad, self.col)
end,
move = function(self)
self.pos += self.vel
end,
edge_bounce = function(self)
edge_normal = vec2:new()
for axis in all({"x","y"}) do
val = self.pos[axis]
if (val - self.rad < 0) then
self.pos[axis] = self.rad
edge_normal[axis] = 1
elseif (val + self.rad > 127) then
self.pos[axis] = 127 - self.rad
edge_normal[axis] = -1
end
end
if (edge_normal.x != 0 or edge_normal.y != 0) then
self.vel = self.vel:bounce(edge_normal)
end
end,
check_collisions = function(self, balls)
for b in all(balls) do
if (b.id != self.id and (collisions[b] == nil or collisions[b][self] == nil)) then
local diff = self.pos - b.pos
if ((diff):mag() < self.rad + b.rad) then
collisions[self] = collisions[self] or {}
collisions[self][b] = diff
end
end
end
end,
}
ball.__index = ball
function ball:new(o)
ball.__count += 1
local o = o or {}
o.id = ball.__count
o.col = o.id % 16
setmetatable(o, self)
o.__index = self
return o
end
function collision_vel(x1,v1,x2,v2)
local x_diff = x1 - x2 local x_diff = x1 - x2
local v_diff = v1 - v2 local v_diff = v1 - v2
return v1 - x_diff * v_diff:dot(x_diff) / x_diff:mag()^2 return v1 - x_diff * (2 * m2 / (m1 + m2)) * v_diff:dot(x_diff) / (#x_diff)^2
end end
+6 -5
View File
@@ -1,7 +1,8 @@
{ {
"picoFile":"qphys.p8", "picoFile":"qphys.p8",
"sources":[
"sources":[ "vec2.lua",
"main.lua" "ball.lua",
] "main.lua"
]
} }
+104 -72
View File
@@ -1,67 +1,15 @@
pico-8 cartridge // http://www.pico-8.com pico-8 cartridge // http://www.pico-8.com
version 34 version 34
__lua__ __lua__
ball_count = 16
ball_radius = 5
function _init()
balls = {}
collisions = {}
for i=1,ball_count do
add(balls, ball:new({
pos=vec2:new(rnd(127),rnd(127)),
vel=vec2:new(rnd(2)-1,rnd(2)-1):normal(),
col=rnd(15)+1
}))
end
end
function _update60()
for b in all(balls) do
b:move()
b:edge_bounce()
end
for b in all(balls) do
b:check_collisions(balls)
end
handle_collisions(balls)
collisions = {}
end
function _draw()
cls()
for b in all(balls) do
b:draw()
end
end
function handle_collisions()
for source,colls in pairs(collisions) do
for target,diff in pairs(colls) do
local n = diff:normal()
local steps = 0
local rewind = 0
local s_xpoint = source.pos
local t_xpoint = target.pos
repeat
steps += 1
rewind += 0.1 * steps
s_xpoint = source.pos - source.vel * rewind
t_xpoint = target.pos - target.vel * rewind
until (s_xpoint - t_xpoint):mag() >= source.rad + target.rad
source.vel = collision_vel(s_xpoint, source.vel, t_xpoint, target.vel)
target.vel = collision_vel(t_xpoint, target.vel, s_xpoint, source.vel)
source.pos = s_xpoint + source.vel * rewind
target.pos = t_xpoint + target.vel * rewind
end
end
end
vec2 = { vec2 = {
x = 0, x = 0,
y = 0, y = 0,
mag = function(self) angle = function(self)
return atan2(y, x);
end,
length = function(self)
return sqrt(self.x^2+self.y^2) return sqrt(self.x^2+self.y^2)
end, end,
@@ -69,24 +17,33 @@ vec2 = {
return self.x * v.x + self.y * v.y return self.x * v.x + self.y * v.y
end, end,
normal = function(self) normal = function(self)
local mag = self:mag() return vec2:new(self.x/#self, self.y/#self)
return vec2:new(self.x/mag, self.y/mag)
end, end,
bounce = function(self, n, b) bounce = function(self, n)
local b = b or 1
local n = n:normal() local n = n:normal()
return (n * -2 * self:dot(n) + self) * b return n * -2 * self:dot(n) + self
end end,
} }
vec2.__eq = function(v1,v2) return v1.x == v2.x and v1.y == v2.y end vec2.__eq = function(v1,v2) return v1.x == v2.x and v1.y == v2.y end
vec2.__add = function(v1,v2) return vec2:new(v1.x+v2.x,v1.y+v2.y) end vec2.__add = function(v1,v2) return vec2:new(v1.x+v2.x,v1.y+v2.y) end
vec2.__sub = function(v1,v2) return vec2:new(v1.x-v2.x,v1.y-v2.y) end vec2.__sub = function(v1,v2) return vec2:new(v1.x-v2.x,v1.y-v2.y) end
vec2.__mul = function(v,s) return vec2:new(v.x*s,v.y*s) end vec2.__mul = function(o1,o2)
local s,v
if(tonum(o1)) then
s = o1
v = o2
else
s = o2
v = o1
end
return vec2:new(v.x*s,v.y*s)
end
vec2.__div = function(v,s) return vec2:new(v.x/s,v.y/s) end vec2.__div = function(v,s) return vec2:new(v.x/s,v.y/s) end
vec2.__unm = function(v) return vec2:new(-v.x,-v.y) end vec2.__unm = function(v) return vec2:new(-v.x,-v.y) end
vec2.__len = function(v) return v:length() end
vec2.__tostring = function(v) return "{x: "..v.x..",y: "..v.y.."}" end vec2.__tostring = function(v) return "{x: "..v.x..",y: "..v.y.."}" end
vec2.__index = vec2 vec2.__index = vec2
@@ -102,26 +59,36 @@ end
function vec2:print_debug(v) function vec2:print_debug(v)
local v = v or vec2:new(0,1) local v = v or vec2:new(0,1)
print("vec: "..vec2.__tostring(self)) print("vec: "..vec2.__tostring(self))
print("mag: "..self:mag()) print("length: "..self:length())
print("dot: "..self:dot(v)) print("dot: "..self:dot(v))
print("normal: "..vec2.__tostring(self:normal())) print("normal: "..vec2.__tostring(self:normal()))
print("bounce: "..vec2.__tostring(self:bounce(v))) print("bounce: "..vec2.__tostring(self:bounce(v)))
print("inv: "..vec2.__tostring(-self)) print("inv: "..vec2.__tostring(-self))
end end
-->8
ball = { ball = {
__count = 0, __count = 0,
id = nil, id = nil,
pos = vec2:new(0,0), pos = vec2:new(0,0),
last_pos = vec2:new(0,0),
vel = vec2:new(0,0), vel = vec2:new(0,0),
rad = ball_radius, rad = 1,
col = 7, col = 7,
draw = function(self) mass = function(self)
return self.rad
end,
draw = function(self, shadow)
if (shadow == true) then
circfill(self.last_pos.x, self.last_pos.y, self.rad, self.col + 8)
end
circfill(self.pos.x, self.pos.y, self.rad, self.col) circfill(self.pos.x, self.pos.y, self.rad, self.col)
end, end,
move = function(self) move = function(self)
self.last_pos = self.pos
self.pos += self.vel self.pos += self.vel
end, end,
@@ -146,7 +113,7 @@ ball = {
for b in all(balls) do for b in all(balls) do
if (b.id != self.id and (collisions[b] == nil or collisions[b][self] == nil)) then if (b.id != self.id and (collisions[b] == nil or collisions[b][self] == nil)) then
local diff = self.pos - b.pos local diff = self.pos - b.pos
if ((diff):mag() < self.rad + b.rad) then if (#diff < self.rad + b.rad) then
collisions[self] = collisions[self] or {} collisions[self] = collisions[self] or {}
collisions[self][b] = diff collisions[self][b] = diff
end end
@@ -160,16 +127,81 @@ function ball:new(o)
ball.__count += 1 ball.__count += 1
local o = o or {} local o = o or {}
o.id = ball.__count o.id = ball.__count
o.col = o.id % 16 o.col = o.col or o.id % 8
setmetatable(o, self) setmetatable(o, self)
o.__index = self o.__index = self
return o return o
end end
function collision_vel(x1,v1,x2,v2) -->8
pal()
cls()
ball_count = 16
max_radius = 16
function _init()
pal({9,10,11,12,13,2,15,136,137,138,139,140,141,130,143,8},1)
balls = {}
collisions = {}
for i=1,ball_count do
add(balls, ball:new({
pos=vec2:new(rnd(127),rnd(127)),
vel=vec2:new(rnd(2)-1,rnd(2)-1):normal(),
rad = i < max_radius and i or max_radius,
}))
end
end
function _update60()
for b in all(balls) do
b:move()
b:edge_bounce()
end
for b in all(balls) do
b:check_collisions(balls)
end
handle_collisions(balls)
collisions = {}
end
function _draw()
-- cls()
for b in all(balls) do
b:draw(true)
end
end
function handle_collisions()
for source,colls in pairs(collisions) do
for target,diff in pairs(colls) do
local n = diff:normal()
local steps = 0
local rewind = 0
local s_xpoint = source.pos
local t_xpoint = target.pos
repeat
steps += 1
rewind = 0.1 * steps
s_xpoint = source.pos - source.vel * rewind
t_xpoint = target.pos - target.vel * rewind
until rewind > 1 or #(s_xpoint - t_xpoint) >= source.rad + target.rad
local s_newvel = collision_vel(s_xpoint, source.vel, t_xpoint, target.vel, source:mass(), target:mass())
local t_newvel = collision_vel(t_xpoint, target.vel, s_xpoint, source.vel, target:mass(), source:mass())
source.pos = s_xpoint + source.vel * rewind
target.pos = t_xpoint + target.vel * rewind
source.vel = s_newvel
target.vel = t_newvel
end
end
end
function collision_vel(x1,v1,x2,v2,m1,m2)
m1 = m1 or 1
m2 = m2 or 1
local x_diff = x1 - x2 local x_diff = x1 - x2
local v_diff = v1 - v2 local v_diff = v1 - v2
return v1 - x_diff * v_diff:dot(x_diff) / x_diff:mag()^2 return v1 - x_diff * (2 * m2 / (m1 + m2)) * v_diff:dot(x_diff) / (#x_diff)^2
end end
__gfx__ __gfx__
+64
View File
@@ -0,0 +1,64 @@
vec2 = {
x = 0,
y = 0,
angle = function(self)
return atan2(y, x);
end,
length = function(self)
return sqrt(self.x^2+self.y^2)
end,
dot = function(self,v)
return self.x * v.x + self.y * v.y
end,
normal = function(self)
return vec2:new(self.x/#self, self.y/#self)
end,
bounce = function(self, n)
local n = n:normal()
return n * -2 * self:dot(n) + self
end,
}
vec2.__eq = function(v1,v2) return v1.x == v2.x and v1.y == v2.y end
vec2.__add = function(v1,v2) return vec2:new(v1.x+v2.x,v1.y+v2.y) end
vec2.__sub = function(v1,v2) return vec2:new(v1.x-v2.x,v1.y-v2.y) end
vec2.__mul = function(o1,o2)
local s,v
if(tonum(o1)) then
s = o1
v = o2
else
s = o2
v = o1
end
return vec2:new(v.x*s,v.y*s)
end
vec2.__div = function(v,s) return vec2:new(v.x/s,v.y/s) end
vec2.__unm = function(v) return vec2:new(-v.x,-v.y) end
vec2.__len = function(v) return v:length() end
vec2.__tostring = function(v) return "{x: "..v.x..",y: "..v.y.."}" end
vec2.__index = vec2
function vec2:new(x,y)
local x = x or 0
local y = y or 0
local o = {x=x,y=y}
setmetatable(o, self)
o.__index = self
return o
end
function vec2:print_debug(v)
local v = v or vec2:new(0,1)
print("vec: "..vec2.__tostring(self))
print("length: "..self:length())
print("dot: "..self:dot(v))
print("normal: "..vec2.__tostring(self:normal()))
print("bounce: "..vec2.__tostring(self:bounce(v)))
print("inv: "..vec2.__tostring(-self))
end