Last active
May 23, 2022 14:58
-
-
Save samfromcadott/579e12a77027a360a5fe61e5a7a2bbbc to your computer and use it in GitHub Desktop.
Ever-changing maze, based on Legend 64 devlog
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en" dir="ltr"> | |
<head> | |
<meta charset="utf-8"> | |
<title></title> | |
</head> | |
<style media="screen"> | |
canvas { | |
background-size: 100px 100px; | |
background-image: | |
linear-gradient(to right, aquamarine 1px, transparent 1px), | |
linear-gradient(to bottom, aquamarine 1px, transparent 1px); | |
border-right: 1px solid aquamarine; | |
border-bottom: 1px solid aquamarine; | |
} | |
</style> | |
<body> | |
<canvas id="canvas" width="700" height="700"></canvas> | |
<script type="text/javascript"> | |
const TILE_SIZE = 100 | |
const MAP_SIZE = 7 | |
var canvas = document.getElementById('canvas'); | |
var ctx = canvas.getContext('2d'); | |
// Math | |
function rand_int(min, max) { | |
min = Math.ceil(min); | |
max = Math.floor(max); | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
function dot(a, b) { | |
return a.x * b.x + a.y * b.y | |
} | |
function dist(a, b) { | |
return Math.sqrt( Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2) ) | |
} | |
function shuffle(n) { | |
array = [...Array(n).keys()] | |
var i = n, r | |
while (i != 0) { | |
r = Math.floor(Math.random() * i) | |
i-- | |
[array[i], array[r]] = [array[r], array[i]] | |
} | |
return array | |
} | |
function Vector(x, y) { | |
this.x = x | |
this.y = y | |
this.add = function(other) { | |
this.x += other.x | |
this.y += other.y | |
} | |
this.sub = function(other) { | |
this.x -= other.x | |
this.y -= other.y | |
} | |
} | |
// Tiles | |
const tile_types = [ | |
// {up:false, down:false, left:false, right:false}, // No openings | |
{up:false, down:false, left:false, right:true }, // Dead end | |
{up:false, down:false, left:true , right:false}, // Dead end | |
{up:false, down:false, left:true , right:true }, | |
{up:false, down:true , left:false, right:false}, // Dead end | |
{up:false, down:true , left:false, right:true }, | |
{up:false, down:true , left:true , right:false}, | |
{up:false, down:true , left:true , right:true }, | |
{up:true , down:false, left:false, right:false}, // Dead end | |
{up:true , down:false, left:false, right:true }, | |
{up:true , down:false, left:true , right:false}, | |
{up:true , down:false, left:true , right:true }, | |
{up:true , down:true , left:false, right:false}, | |
{up:true , down:true , left:false, right:true }, | |
{up:true , down:true , left:true , right:false}, | |
{up:true , down:true , left:true , right:true } | |
] | |
function Tile(x, y, openings) { | |
this.x = x | |
this.y = y | |
this.up = openings.up | |
this.down = openings.down | |
this.left = openings.left | |
this.right = openings.right | |
this.draw = function() { | |
const HALL_WIDTH = TILE_SIZE/3 | |
ctx.fillStyle = 'black' | |
ctx.fillRect( // Draw center | |
this.x*TILE_SIZE+HALL_WIDTH, | |
this.y*TILE_SIZE+HALL_WIDTH, | |
HALL_WIDTH, | |
HALL_WIDTH | |
); | |
if (this.up) | |
ctx.fillRect( | |
this.x*TILE_SIZE+HALL_WIDTH, | |
this.y*TILE_SIZE, | |
HALL_WIDTH, | |
HALL_WIDTH+1 | |
); | |
if (this.down) | |
ctx.fillRect( | |
this.x*TILE_SIZE+HALL_WIDTH, | |
this.y*TILE_SIZE+HALL_WIDTH*2-1, | |
HALL_WIDTH, | |
HALL_WIDTH+1 | |
); | |
if (this.left) | |
ctx.fillRect( | |
this.x*TILE_SIZE, | |
this.y*TILE_SIZE+HALL_WIDTH, | |
HALL_WIDTH+1, | |
HALL_WIDTH | |
); | |
if (this.right) | |
ctx.fillRect( | |
this.x*TILE_SIZE+HALL_WIDTH*2-1, | |
this.y*TILE_SIZE+HALL_WIDTH, | |
HALL_WIDTH+1, | |
HALL_WIDTH | |
); | |
} | |
this.position = function () { | |
var x = this.x * TILE_SIZE + TILE_SIZE/2 | |
var y = this.y * TILE_SIZE + TILE_SIZE/2 | |
return new Vector(x, y) | |
} | |
this.can_add = function () { | |
// Check that for each side the connected tile fits | |
// Up | |
if (this.y > 0 && tilemap[this.x][this.y-1] instanceof Tile && this.up != tilemap[this.x][this.y-1].down) | |
return false | |
// Down | |
if (this.y < MAP_SIZE-1 && tilemap[this.x][this.y+1] instanceof Tile && this.down != tilemap[this.x][this.y+1].up) | |
return false | |
// Left | |
if (this.x > 0 && tilemap[this.x-1][this.y] instanceof Tile && this.left != tilemap[this.x-1][this.y].right) | |
return false | |
// Right | |
if (this.x < MAP_SIZE-1 && tilemap[this.x+1][this.y] instanceof Tile && this.right != tilemap[this.x+1][this.y].left) | |
return false | |
// Check if an opening would leave tilemap | |
if (this.up && this.y == 0) // Up | |
return false | |
if (this.down && this.y == MAP_SIZE-1) // Down | |
return false | |
if (this.left && this.x == 0) // Left | |
return false | |
if (this.right && this.x == MAP_SIZE-1) // Right | |
return false | |
if (this.x < MAP_SIZE && this.y < MAP_SIZE && this.x >= 0 && this.y >= 0) | |
return true | |
} | |
} | |
// Tilemap | |
var tilemap = Array.from(Array(MAP_SIZE), () => new Array(MAP_SIZE)) | |
tilemap.draw = function() { | |
for (var i = 0; i < tilemap.length; i++) | |
for (var j = 0; j < tilemap[0].length; j++) { | |
if (tilemap[i][j] instanceof Tile) | |
tilemap[i][j].draw() | |
} | |
} | |
tilemap.add_tile = function (x, y) { | |
var tiles = shuffle(tile_types.length) | |
for (var n of tiles) { | |
var new_tile = new Tile( x, y, tile_types[n] ) | |
if (new_tile.can_add()) { | |
tilemap[x][y] = new_tile | |
break | |
} | |
} | |
} | |
tilemap.update_maze = function () { | |
const CHANGE_DIST = 125 | |
for (var i = 0; i < tilemap.length; i++) | |
for (var j = 0; j < tilemap[0].length; j++) { | |
// Move on to the next tile if this one is empty | |
if ( !(tilemap[i][j] instanceof Tile) ) | |
continue | |
var tile = tilemap[i][j] | |
var dir = new Vector(tile.position().x-player.position.x, tile.position().y-player.position.y) // Vector from player to tile center | |
var facing = dot(player.velocity, dir) | |
var d = dist(player.position, tile.position()) | |
// Player is moving away from tile | |
if ( facing < 0 && d > CHANGE_DIST ) | |
delete tilemap[i][j] | |
// Player is moving toward the tile | |
if ( facing > 0 && d < CHANGE_DIST ) { | |
// Up | |
if (tile.up && tile.y > 0 && tilemap[i][j-1] === undefined) | |
tilemap.add_tile(i, j-1) | |
// Down | |
if (tile.down && tile.y < MAP_SIZE-1 && tilemap[i][j+1] === undefined) | |
tilemap.add_tile(i, j+1) | |
// Left | |
if (tile.left && tile.x > 0 && tilemap[i-1][j] === undefined) | |
tilemap.add_tile(i-1, j) | |
// Right | |
if (tile.right && tile.x < MAP_SIZE-1 && tilemap[i+1][j] === undefined) | |
tilemap.add_tile(i+1, j) | |
} | |
} | |
} | |
// Player | |
var player = { | |
position: new Vector(MAP_SIZE*TILE_SIZE/2, MAP_SIZE*TILE_SIZE/2), | |
velocity: new Vector(0, 0), | |
width: 10, | |
speed: 5, | |
draw: function () { | |
ctx.fillStyle = 'orange' | |
ctx.fillRect(this.position.x-this.width/2, this.position.y-this.width/2, this.width, this.width) | |
} | |
} | |
// Input | |
var map = {} | |
onkeydown = onkeyup = function(e) { | |
e = e || event | |
map[e.code] = e.type == 'keydown' | |
player.velocity = new Vector(0, 0) | |
if (map["ArrowUp"]) | |
player.velocity.add( new Vector(0,-player.speed) ) | |
if (map["ArrowDown"]) | |
player.velocity.add( new Vector(0,player.speed) ) | |
if (map["ArrowLeft"]) | |
player.velocity.add( new Vector(-player.speed,0) ) | |
if (map["ArrowRight"]) | |
player.velocity.add( new Vector(player.speed,0) ) | |
} | |
// Main loop | |
tilemap[3][3] = new Tile(3, 3, {up: true, down: true, left: true, right: true}) | |
tilemap[3][2] = new Tile(3, 2, {up: true, down: true, left: false, right: false}) | |
tilemap[3][4] = new Tile(3, 4, {up: true, down: true, left: false, right: false}) | |
tilemap[2][3] = new Tile(2, 3, {up: false, down: false, left: true, right: true}) | |
tilemap[4][3] = new Tile(4, 3, {up: false, down: false, left: true, right: true}) | |
window.requestAnimationFrame(loop) | |
function loop(timestamp) { | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
tilemap.draw() | |
player.draw() | |
player.position.add(player.velocity) | |
tilemap.update_maze() | |
window.requestAnimationFrame(loop) | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment