2023-01-13 20:05:19 +00:00
|
|
|
--Character controller. Renders point particle.
|
|
|
|
local love = love
|
2023-01-13 21:34:01 +00:00
|
|
|
local oldBuffer = love.graphics.newCanvas()
|
|
|
|
local newBuffer = love.graphics.newCanvas()
|
2023-01-14 13:48:52 +00:00
|
|
|
local oldState, curState, newState
|
|
|
|
|
2023-01-14 03:46:39 +00:00
|
|
|
local FRICTION = 0.01
|
|
|
|
local MAXSPEED = 5.0
|
2023-01-14 13:48:52 +00:00
|
|
|
local ddx, ddy = 0.0, 0.0
|
|
|
|
|
|
|
|
local function State( )
|
|
|
|
return { t = 0, x = 0, y = 0, dx = 0, dy = 0 }
|
|
|
|
end
|
2023-01-13 20:05:19 +00:00
|
|
|
|
2023-01-14 13:48:52 +00:00
|
|
|
local function Current() return curState end
|
|
|
|
local function Next() return newState end
|
|
|
|
|
|
|
|
local function Integrate( step )
|
|
|
|
newState.t = love.timer.getTime()
|
|
|
|
newState.dx = (1.0 - FRICTION) * curState.dx + FRICTION * ddx
|
|
|
|
newState.dy = (1.0 - FRICTION) * curState.dy + FRICTION * ddy
|
|
|
|
newState.x = curState.x + newState.dx * step * MAXSPEED
|
|
|
|
newState.y = curState.y + newState.dy * step * MAXSPEED
|
|
|
|
end
|
2023-01-13 20:05:19 +00:00
|
|
|
|
2023-01-14 12:43:54 +00:00
|
|
|
local function OnImpact( impact )
|
2023-01-14 13:48:52 +00:00
|
|
|
--Adjust current trajectory according to collision.
|
2023-01-14 14:54:32 +00:00
|
|
|
if not impact.dt then return end
|
2023-01-14 18:47:46 +00:00
|
|
|
|
|
|
|
local x, y = impact.r * math.cos( impact.th ), impact.r * math.sin( impact.th )
|
|
|
|
local vx, vy = newState.dx, newState.dy --Velocity of particle going into collision.
|
|
|
|
local unx, uny = math.cos( impact.normal ), math.sin( impact.normal ) --Outward-facing normal of wave.
|
|
|
|
local uvx, uvy --Unit vector velocity of particle.
|
|
|
|
local speed = math.sqrt( vx * vx + vy * vy )
|
|
|
|
if speed < 0.01 then
|
|
|
|
--If the marble is motionless, there is no angular velocity wrt 0,
|
|
|
|
--so the wave is headed directly inward.
|
|
|
|
--We handle the collision as if the marble is headed directly outward.
|
|
|
|
uvx, uvy = unx, uny
|
|
|
|
vx, vy = uvx, uvy
|
|
|
|
else
|
|
|
|
uvx, uvy = vx / speed , vy / speed
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--Get signed angle between normal and incoming velocity (both unit vectors)
|
|
|
|
local dot = unx * uvy - uny * uvx
|
|
|
|
|
|
|
|
--Fudge factor: apply an impulse inward so that you don't stick or slide on the wave.
|
|
|
|
local inward = ( dot > 0 ) and dot or -dot
|
|
|
|
inward = inward * inward * inward
|
|
|
|
|
|
|
|
--Calculate the rotation matrix:
|
|
|
|
--counterclockwise rotation by 2 * pi - 2 * arccos( n dot v )
|
|
|
|
local c, s = 1 - 2 * dot * dot, - 2 * dot * math.sqrt( 1 - dot * dot )
|
|
|
|
--Apply:
|
|
|
|
local vxout, vyout =
|
|
|
|
inward * (- math.cos(impact.th) ) - (1.0 - inward) * ( vx * c - vy * s ),
|
|
|
|
inward * (- math.sin(impact.th) ) - (1.0 - inward) * ( x * s + vy * c )
|
|
|
|
|
|
|
|
curState.x, curState.y = x, y
|
|
|
|
curState.dx, curState.dy = 0.5 * vxout, 0.5 * vyout
|
|
|
|
|
|
|
|
debugRenderImpact = { xi = x, yi = y, xf = x - vx, yf = y - vy,
|
|
|
|
xn = 0.2 * unx + x, yn = 0.2 * uny + y,
|
|
|
|
vxout = x + vxout, vyout = y + vyout}
|
|
|
|
|
|
|
|
return Integrate( math.max( impact.dt , 1 / 60 ) ) --Hmm! Maybe this should be a fixed step instead for stability's sake.
|
2023-01-14 12:43:54 +00:00
|
|
|
end
|
|
|
|
|
2023-01-13 20:05:19 +00:00
|
|
|
local function Update()
|
2023-01-14 13:48:52 +00:00
|
|
|
--Roll the log.
|
|
|
|
for k, v in pairs( oldState ) do
|
|
|
|
oldState[k] = curState[k]
|
|
|
|
curState[k] = newState[k]
|
|
|
|
end
|
2023-01-14 12:43:54 +00:00
|
|
|
end
|
|
|
|
|
2023-01-13 20:05:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
local function OnKey()
|
|
|
|
ddx = (love.keyboard.isScancodeDown( "d" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "a" ) and 1.0 or 0.0)
|
2023-01-14 03:46:39 +00:00
|
|
|
ddy = (love.keyboard.isScancodeDown( "w" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "s" ) and 1.0 or 0.0)
|
2023-01-13 20:05:19 +00:00
|
|
|
|
|
|
|
local n = math.sqrt( ddx * ddx + ddy * ddy )
|
|
|
|
if n < 0.001 then return end
|
|
|
|
ddx, ddy = ddx / n, ddy / n
|
|
|
|
end
|
|
|
|
|
|
|
|
local function Draw()
|
2023-01-14 13:59:20 +00:00
|
|
|
|
|
|
|
--Extrapolate forward for slightly smoother rendering.
|
|
|
|
local dt = love.timer.getTime() - curState.t
|
|
|
|
Integrate( dt + 1 / 60.0 )
|
2023-01-14 03:46:39 +00:00
|
|
|
|
2023-01-14 13:48:52 +00:00
|
|
|
local xp, yp = transform:transformPoint( oldState.x, oldState.y )
|
|
|
|
local xc, yc = transform:transformPoint( curState.x, curState.y )
|
|
|
|
local xn, yn = transform:transformPoint( newState.x, newState.y )
|
2023-01-14 03:46:39 +00:00
|
|
|
|
2023-01-13 21:34:01 +00:00
|
|
|
love.graphics.setCanvas( newBuffer )
|
2023-01-14 03:46:39 +00:00
|
|
|
love.graphics.setColor( 1.0, 1.0, 1.0 , 0.99 ) -- White.
|
2023-01-13 21:34:01 +00:00
|
|
|
love.graphics.draw( oldBuffer ) --Time-dependent fade: overlay canvas over itself.
|
|
|
|
|
|
|
|
--Render latest segment in trail.
|
2023-01-14 03:46:39 +00:00
|
|
|
love.graphics.setColor( 245 / 255, 169 / 255, 184 / 255, 1.0 ) --Trans pink.
|
|
|
|
--love.graphics.line( xi, yi, xf, yf )
|
2023-01-14 13:48:52 +00:00
|
|
|
--Segment stuff:
|
|
|
|
love.graphics.setLineJoin( "bevel" )
|
|
|
|
love.graphics.setLineStyle( "smooth" )
|
|
|
|
love.graphics.setLineWidth( 8.0 )
|
2023-01-14 13:59:20 +00:00
|
|
|
love.graphics.line( xp, yp, xc, yc, xn, yn)
|
|
|
|
love.graphics.circle( "fill", xn, yn, 4.0 )
|
2023-01-13 21:34:01 +00:00
|
|
|
|
|
|
|
love.graphics.setCanvas( oldBuffer )
|
|
|
|
love.graphics.clear( 1.0, 1.0, 1.0, 0.0 )
|
|
|
|
|
|
|
|
--Render circle directly to screen.
|
|
|
|
love.graphics.setCanvas()
|
|
|
|
love.graphics.draw( newBuffer )
|
2023-01-14 03:46:39 +00:00
|
|
|
love.graphics.setColor( 1, 1, 1, 1.0 ) --White.
|
2023-01-14 14:54:32 +00:00
|
|
|
love.graphics.setLineWidth( 1.0 )
|
|
|
|
love.graphics.circle( "line", xn, yn, 4 )
|
2023-01-13 21:34:01 +00:00
|
|
|
|
|
|
|
oldBuffer, newBuffer = newBuffer, oldBuffer
|
|
|
|
|
2023-01-13 20:05:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local function Impact( impact )
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2023-01-13 21:34:01 +00:00
|
|
|
--Window resize.
|
|
|
|
local function Resize()
|
2023-01-14 03:46:39 +00:00
|
|
|
newBuffer = love.graphics.newCanvas()
|
|
|
|
--TODO: render oldBuffer to new newBuffer, but scaled down.
|
|
|
|
oldBuffer = love.graphics.newCanvas()
|
2023-01-13 21:34:01 +00:00
|
|
|
|
2023-01-14 03:46:39 +00:00
|
|
|
|
2023-01-13 21:34:01 +00:00
|
|
|
end
|
|
|
|
|
2023-01-14 13:48:52 +00:00
|
|
|
local function Reset()
|
|
|
|
oldState, curState, newState = State(), State(), State()
|
|
|
|
Resize()
|
2023-01-13 20:05:19 +00:00
|
|
|
end
|
|
|
|
|
2023-01-13 21:34:01 +00:00
|
|
|
|
|
|
|
|
2023-01-13 20:05:19 +00:00
|
|
|
Reset()
|
|
|
|
|
|
|
|
return {
|
2023-01-14 12:43:54 +00:00
|
|
|
Integrate = Integrate,
|
|
|
|
OnImpact = OnImpact,
|
2023-01-13 20:05:19 +00:00
|
|
|
Update = Update,
|
|
|
|
OnKey = OnKey,
|
|
|
|
Draw = Draw,
|
|
|
|
Impact = Impact,
|
|
|
|
Reset = Reset,
|
2023-01-14 03:46:39 +00:00
|
|
|
Resize = Resize,
|
2023-01-14 13:48:52 +00:00
|
|
|
Current = Current,
|
|
|
|
Next = Next,
|
2023-01-13 20:05:19 +00:00
|
|
|
}
|