Skip to content

Instantly share code, notes, and snippets.

@jan-martinek
Last active September 3, 2022 13:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jan-martinek/a81605d35a585b265e2e0a42bb32966a to your computer and use it in GitHub Desktop.
Save jan-martinek/a81605d35a585b265e2e0a42bb32966a to your computer and use it in GitHub Desktop.
Maze
local function vectorFromAngle(magnitude, angle)
angle = (angle + 90) % 360
local rad = math.rad(angle)
local x = magnitude * math.cos(rad)
local y = magnitude * math.sin(rad)
return v2D.new(x, y)
end
Maze = {}
class("Maze").extends(NobleScene)
local at = playdate.geometry.affineTransform
local fastInt = playdate.geometry.lineSegment.fast_intersection
local sqDist = playdate.geometry.squaredDistanceToPoint
local distance = nil
local distance2 = nil
local rayCount = nil
local forwardRayNumber = nil
local rays = nil
local directions = nil
local directionsNormal = nil
local angles = nil
local radAngle = nil
local forwardDir = nil
local forwardNormalDir = nil
local panelWidth = nil
local panelsOffset = nil
local move = nil
local rotation = nil
local walls = nil
local path = nil
local pathPositions = nil
local pos = nil
local automove = nil
local auto = nil
local frontRay
local leftNormalDir
local leftRay
local rightNormalDir
local rightRay
local function rotateCw(v)
return { x = v.y, y = -v.x }
end
local function rotateCcw(v)
return { x = -v.y, y = v.x }
end
local function rotateLevel(amount)
local t = at.new()
t:rotate(amount, pos.x, pos.y)
for i=1,#walls do
local x1, y1 = t:transformXY(walls[i][1], walls[i][2])
local x2, y2 = t:transformXY(walls[i][3], walls[i][4])
walls[i] = {x1, y1, x2, y2}
end
end
local function moveLevel(amount)
local t = at.new()
t:translate(- forwardNormalDir.x * amount, - forwardNormalDir.y * amount)
for i=1,#walls do
local x1, y1 = t:transformXY(walls[i][1], walls[i][2])
local x2, y2 = t:transformXY(walls[i][3], walls[i][4])
walls[i] = {x1, y1, x2, y2}
end
end
local function getMove(dir)
local countdown = 40
return function()
if countdown == 0 then
move = nil
automove()
else
moveLevel(2.5 / 100)
countdown -= 1
end
end
end
local function getRotation(right)
local countdown = 20
return function()
if countdown == 0 then
rotation = nil
--walls = rotated
automove()
else
if right then
rotateLevel(-180 / 40)
else
rotateLevel(180 / 40)
end
countdown -= 1
end
end
end
local function wallCheck(ray)
for wall=1,#walls do
local w = walls[wall]
if ((w[1] >= 0 and w[2] >= 0) or (w[3] >= 0 and w[4] >= 0)) and fastInt(w[1], w[2], w[3], w[4], ray[1], ray[2], ray[3], ray[4]) then
return true
end
end
end
local function frontCheck()
return wallCheck(frontRay)
end
local function leftCheck()
return wallCheck(leftRay)
end
local function rightCheck()
return wallCheck(rightRay)
end
automove = function()
if not frontCheck() then
move = getMove({ x = forwardNormalDir.x, y = forwardNormalDir.y })
elseif not rightCheck() then
rotation = getRotation(false)
else
rotation = getRotation(true)
end
end
local Move = {}
Move.left = 1
Move.fw = 2
Move.right = 3
Maze.backgroundColor = Graphics.kColorWhite -- This is the background color of this scene.
-- This runs when your scene's object is created, which is the first thing that happens when transitining away from another scene.
function Maze:init()
Maze.super.init(self)
distance = 4
distance2 = distance^2
rayCount = 41
pos = playdate.geometry.point.new(0.5, 0.5)
forwardRayNumber = math.ceil(rayCount / 2)
radAngle = (0.5 * math.pi) / rayCount
rays = {}
for i=1,rayCount do
local angle = -90 + 90 / rayCount * i
local dir = vectorFromAngle(distance, angle)
rays[i] = {pos.x, pos.y, pos.x + dir.x, pos.y + dir.y}
if i == forwardRayNumber then
forwardDir = vectorFromAngle(distance, angle)
forwardNormalDir = vectorFromAngle(1, angle)
end
end
panelWidth = math.ceil(400 / rayCount)
panelsOffset = (rayCount * math.ceil(400 / rayCount) - 400) / 2
frontRay = {pos.x, pos.y, pos.x + forwardNormalDir.x, pos.y + forwardNormalDir.y}
leftNormalDir = rotateCcw(forwardNormalDir)
leftRay = {pos.x, pos.y, pos.x + leftNormalDir.x, pos.y + leftNormalDir.y}
rightNormalDir = rotateCw(forwardNormalDir)
rightRay = {pos.x, pos.y, pos.x + rightNormalDir.x, pos.y + rightNormalDir.y}
auto = true
walls = {
{0, 0, 1, 0}, {1, 0, 2, 0}, {2, 0, 3, 0}, {3, 0, 4, 0},
{1, 1, 2, 1}, {4, 1, 5, 1},
{0, 2, 1, 2}, {2, 2, 3, 2},
{1, 3, 2, 3}, {2, 3, 3, 3}, {4, 3, 5, 3},
{3, 4, 4, 4},
{1, 5, 2, 5}, {2, 5, 3, 5}, {3, 5, 4, 5}, {4, 5, 5, 5},
{0, 0, 0, 1}, {0, 1, 0, 2},
{1, 2, 1, 3}, {1, 3, 1, 4}, {1, 4, 1, 5},
{2, 0, 2, 1}, {2, 1, 2, 2}, {2, 4, 2, 5},
{3, 1, 3, 2}, {3, 3, 3, 4},
{4, 0, 4, 1}, {4, 1, 4, 2},
{5, 1, 5, 2}, {5, 2, 5, 3}, {5, 3, 5, 4}, {5, 4, 5, 5}
-- { 0, 0, 1, 0 }, { 1, 0, 2, 0 },
-- { 0, 0, 0, 1 },
-- { 0, 1, 1, 1 }, { 1, 1, 2, 1 },
-- { 2, 0, 2, 1 },
--{ 0, 0, 1, 0 }, { 1, 0, 2, 0 }, { 2, 0, 3, 0 }, { 3, 0, 4, 0 },
--{ 4, 0, 4, 1 }, { 4, 1, 4, 2 }, { 4, 2, 4, 3 }, { 4, 3, 4, 4 },
--{ 0, 4, 1, 4 }, { 1, 4, 2, 4 }, { 2, 4, 3, 4 }, { 3, 4, 4, 4 },
--{ 0, 0, 0, 1 }, { 0, 1, 0, 2 }, { 0, 2, 0, 3 }, { 0, 3, 0, 4 },
--{ 0, 1, 1, 1 }, { 1, 1, 2, 1 },
--{ 3, 0, 3, 1 }, { 3, 1, 3, 2 },
--{ 1, 2, 2, 2 }, { 2, 2, 3, 2 },
--{ 1, 3, 2, 3 },
--{ 3, 3, 4, 3 },
}
rotateLevel(180 * 0.25)
automove()
end
-- When transitioning from another scene, this runs as soon as this scene needs to be visible (this moment depends on which transition type is used).
function Maze:enter()
Maze.super.enter(self)
end
-- This runs once a transition from another scene is complete.
function Maze:start()
Maze.super.start(self)
end
-- local function collisionDist(x1, y1, x2, y2, x3, y3, x4, y4)
-- local uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))
-- local uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))
-- if uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1 then
-- local x = x1 + (uA * (x2-x1))
-- local y = y1 + (uA * (y2-y1))
-- return (x - x1)^2 + (y - y1)^2
-- end
-- return distance2
-- end
local counter = 0
-- This runs once per frame.
function Maze:update()
Maze.super.update(self)
-- Graphics.setColor(Graphics.kColorBlack)
-- Graphics.fillRect(0, 0, 400, 120)
if move then move() end
if rotation then rotation() end
local collisions = {}
for i=1,rayCount do
for wall=1,#walls do
local w = walls[wall]
if (w[1] > -0.4 and w[2] > -0.4 and w[3] > -0.4 and w[4] > -0.4 and w[1] < distance and w[2] < distance and w[3] < distance and w[4] < distance) then
local intersects, x, y = fastInt(w[1], w[2], w[3], w[4], rays[i][1], rays[i][2], rays[i][3], rays[i][4])
if intersects then
local dist2 = sqDist(pos.x, pos.y, x, y)
if not collisions[i] or collisions[i] > dist2 then
collisions[i] = dist2
end
end
end
end
end
for i=1,rayCount do
local collideDist = collisions[i] and math.sqrt(collisions[i]) or distance
local panel = 1 / (math.cos(radAngle * (i - forwardRayNumber)) * collideDist) * 100
-- Graphics.setColor(Graphics.kColorWhite)
-- Graphics.fillRect(
-- (i - 1) * panelWidth - panelsOffset,
-- 120 - panel / 2,
-- panelWidth,
-- panel)
-- Graphics.setColor(Graphics.kColorBlack)
Graphics.setDitherPattern(math.clamp(panel / 400, 0.001, 0.999))
Graphics.fillRect(
(i - 1) * panelWidth - panelsOffset,
120 - panel / 2,
panelWidth,
panel)
end
end
function playdate.debugDraw()
local s = 20
local collisions = {}
for i=1,rayCount do
for wall=1,#walls do
local w = walls[wall]
if (w[1] > -0.4 and w[2] > -0.4 and w[3] > -0.4 and w[4] > -0.4 and w[1] < distance and w[2] < distance and w[3] < distance and w[4] < distance) then
local intersects, x, y = fastInt(w[1], w[2], w[3], w[4], rays[i][1], rays[i][2], rays[i][3], rays[i][4])
if intersects then
local dist2 = sqDist(pos.x, pos.y, x, y)
if not collisions[i] or collisions[i] > dist2 then
collisions[i] = dist2
end
end
end
if not collisions[i] then
collisions[i] = distance2
end
end
end
Graphics.setDrawOffset(200 - pos.x * s, 120 - pos.y * s)
for i=1,rayCount do
local scale = math.sqrt(collisions[i]) / distance
local x, y = (rays[i][3] - rays[i][1]) * scale, (rays[i][4] - rays[i][2]) * scale
Graphics.drawLine(pos.x * s, pos.y * s, pos.x * s + x * s, pos.y * s + y * s)
end
counter += 1
for i=1,#walls do
if counter > 100 then
local w = walls[i]
if (w[1] > -0.4 and w[2] > -0.4 and w[3] > -0.4 and w[4] > -0.4 and w[1] < distance and w[2] < distance and w[3] < distance and w[4] < distance) then
Graphics.drawLine(walls[i][1] * s, walls[i][2] * s, walls[i][3] * s, walls[i][4] * s)
end
else
Graphics.drawLine(walls[i][1] * s, walls[i][2] * s, walls[i][3] * s, walls[i][4] * s)
end
end
end
-- This runs once per frame, and is meant for drawing code.
function Maze:drawBackground()
Maze.super.drawBackground(self)
end
-- This runs as as soon as a transition to another scene begins.
function Maze:exit()
Maze.super.exit(self)
end
-- This runs once a transition to another scene completes.
function Maze:finish()
Maze.super.finish(self)
end
function Maze:pause()
Maze.super.pause(self)
end
function Maze:resume()
Maze.super.resume(self)
end
-- You can define this here, or within your scene's init() function.
Maze.inputHandler = {
-- A button
--
AButtonDown = function() -- Runs once when button is pressed.
end,
AButtonHold = function() -- Runs every frame while the player is holding button down.
end,
AButtonHeld = function() -- Runs after button is held for 1 second.
end,
AButtonUp = function() -- Runs once when button is released.
end,
-- B button
--
BButtonDown = function()
end,
BButtonHeld = function()
end,
BButtonHold = function()
end,
BButtonUp = function()
end,
-- D-pad left
--
leftButtonDown = function()
--if not rotation then
-- rotation = getRotation(false)
--end
end,
leftButtonHold = function()
end,
leftButtonUp = function()
end,
-- D-pad right
--
rightButtonDown = function()
--if not rotation then
-- rotation = getRotation(true)
--end
end,
rightButtonHold = function()
end,
rightButtonUp = function()
end,
-- D-pad up
--
upButtonDown = function()
--if not move then
-- move = getMove({ x = forwardNormalDir.x, y = forwardNormalDir.y })
--end
end,
upButtonHold = function()
end,
upButtonUp = function()
end,
-- D-pad down
--
downButtonDown = function()
-- if not move then
-- move = getMove({ x = -forwardNormalDir.x, y = -forwardNormalDir.y })
-- end
end,
downButtonHold = function()
end,
downButtonUp = function()
end,
-- Crank
--
cranked = function(change, acceleratedChange) -- Runs when the crank is rotated. See Playdate SDK documentation for details.
end,
crankDocked = function() -- Runs once when when crank is docked.
end,
crankUndocked = function() -- Runs once when when crank is undocked.
end
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment