Last active
August 11, 2022 02:08
-
-
Save tylersnowden/fb61c4d36b22f453ad533e0fe0d9e87d to your computer and use it in GitHub Desktop.
Pacman v0.1
This file contains 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
<html> | |
<head> | |
<title>Pacman</title> | |
<style> | |
canvas { | |
border: 10px solid blue; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="canvas"></canvas> | |
<script> | |
const UP = 'up'; | |
const DOWN = 'down'; | |
const RIGHT = 'right'; | |
const LEFT = 'left'; | |
var canvas = document.getElementById('canvas'); | |
var ctx = canvas.getContext('2d'); | |
canvas.width = 800; | |
canvas.height = 600; | |
direction = RIGHT; // Starting Direction | |
var stepX = 1; | |
var stepY = 1; | |
var anim_img = null; | |
var pacman = { | |
step: 5.5, | |
x: 200, | |
y: 50, | |
nextX: 300, | |
nextY: 300, | |
width: 50, | |
height: 50, | |
speed: 250, | |
color: 'yellow', | |
mouthOpenValue: 40, | |
mouthPosition: -1, | |
}; | |
var bat = { | |
x: 200, | |
y: 200, | |
width: 48, | |
height: 48 | |
} | |
var keysDown = {}; | |
window.addEventListener('keydown', function (e) { | |
keysDown[e.keyCode] = true; | |
}); | |
window.addEventListener('keyup', function (e) { | |
delete keysDown[e.keyCode]; | |
}); | |
/** | |
* For each frame, check for key presses and move the | |
* pacman if the key is pressed. Also update the direction | |
* of the pacman. | |
* | |
* @param mod Time delta | |
*/ | |
function update(mod) { | |
if (37 in keysDown) { | |
pacman.x -= pacman.speed * mod; | |
if (pacman.x <= 22) | |
pacman.x = 22; | |
direction = LEFT; | |
}; | |
if (38 in keysDown) { | |
pacman.y -= pacman.speed * mod; | |
if (pacman.y <= 22) | |
pacman.y = 22; | |
direction = UP; | |
}; | |
if (39 in keysDown) { | |
pacman.x += pacman.speed * mod; | |
if (pacman.x >= 778) | |
pacman.x = 778; | |
direction = RIGHT; | |
}; | |
if (40 in keysDown) { | |
pacman.y += pacman.speed * mod; | |
if (pacman.y >= 578) | |
pacman.y = 578; | |
direction = DOWN; | |
}; | |
}; | |
/** | |
* Draw the Background of the canvas | |
*/ | |
function drawCanvasBackground() { | |
ctx.fillStyle = '#000'; | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
} | |
/** | |
* Draw pacman taking into consideration the Pacman's | |
* direction and position. | |
*/ | |
function drawPacman() { | |
if (pacman.mouthOpenValue <= 0) { | |
pacman.mouthPosition = 1; | |
} else if (pacman.mouthOpenValue >= 40) { | |
pacman.mouthPosition = -1; | |
} | |
pacman.mouthOpenValue += (1 * pacman.mouthPosition); | |
ctx.beginPath(); | |
if (direction == RIGHT) { | |
pacman.nextX = pacman.x - pacman.step; | |
ctx.arc(pacman.x, pacman.y, 20, (Math.PI / 180) * pacman.mouthOpenValue, (Math.PI / 180) * (360 - pacman.mouthOpenValue), false); | |
}; | |
if (direction == LEFT) { | |
pacman.nextX = pacman.x + pacman.step; | |
ctx.arc(pacman.x, pacman.y, 20, (Math.PI / 180) * (pacman.mouthOpenValue - 180), (Math.PI / 180) * (180 - pacman.mouthOpenValue), false); | |
}; | |
if (direction == UP) { | |
pacman.nextY = pacman.y + pacman.step; | |
ctx.arc(pacman.x, pacman.y, 20, (Math.PI / 180) * (pacman.mouthOpenValue - 90), (Math.PI / 180) * (270 - pacman.mouthOpenValue), false); | |
}; | |
if (direction == DOWN) { | |
pacman.nextY = pacman.y - pacman.step; | |
ctx.arc(pacman.x, pacman.y, 20, (Math.PI / 180) * (pacman.mouthOpenValue - 270), (Math.PI / 180) * (90 - pacman.mouthOpenValue), false); | |
}; | |
ctx.lineTo(pacman.x, pacman.y); | |
ctx.fillStyle = pacman.color; | |
ctx.fill(); | |
} | |
/** | |
* Draw the bat, taking into consideration the position. | |
*/ | |
function drawBat() { | |
ctx.beginPath(); | |
ctx.lineTo(bat.x, bat.y); | |
ctx.arc(bat.x, bat.y, 20, (Math.PI / 180) * (pacman.mouthOpenValue - 210), (Math.PI / 180) * (60 - pacman.mouthOpenValue), false); | |
ctx.fillStyle = "green"; | |
ctx.fill(); | |
if (bat.x < 0 || bat.x > canvas.width - bat.width) { | |
stepX = -stepX; | |
} | |
if (bat.y < 0 || bat.y > canvas.height - bat.height) { | |
stepY = -stepY; | |
} | |
bat.x += stepX; | |
bat.y += stepY; | |
} | |
/** | |
* Draw a wall | |
*/ | |
function drawWall() { | |
ctx.beginPath(); | |
ctx.moveTo(50, 50); | |
ctx.lineTo(50, 200); | |
ctx.strokeStyle = 'blue'; | |
ctx.lineWidth = 10; | |
ctx.stroke(); | |
ctx.beginPath(); | |
ctx.moveTo(45, 200); | |
ctx.lineTo(200, 200); | |
ctx.strokeStyle = 'blue'; | |
ctx.lineWidth = 10; | |
ctx.stroke(); | |
}; | |
/** | |
* Render the canvas | |
*/ | |
function render() { | |
drawCanvasBackground(); | |
drawPacman(); | |
drawBat(); | |
}; | |
/** | |
* Build a hitbox centered around the coordinates. | |
* | |
* @param x Center of object's position | |
* @param y Center of object's position | |
* @param width The Object's hitbox width | |
* @param height The Object's hitbox height | |
* @param paint True if canvas should render the ray | |
* | |
* @returns CanvasRenderingContext2D.ImageData | |
*/ | |
function buildHitBox(x, y, width, height, paint) { | |
var offsetX = (width / 2); | |
var offsetY = (height / 2); | |
var hitbox = ctx.getImageData(x - offsetX, y - offsetY, width, height); | |
if (paint) { | |
ctx.beginPath(); | |
ctx.lineWidth = 2; | |
ctx.strokeStyle = 'red'; | |
ctx.moveTo(x - offsetX, y - offsetY); | |
ctx.lineTo(x - offsetX + width, y - offsetY); | |
ctx.lineTo(x - offsetX + width, y - offsetY + height); | |
ctx.lineTo(x - offsetX, y - offsetY + height); | |
ctx.lineTo(x - offsetX, y - offsetY); | |
ctx.stroke(); | |
} | |
return hitbox; | |
} | |
/** | |
* Casts a ray from the coordinates with a direction. | |
* Returns ImageData to be used in collision detection. | |
* | |
* @param x | |
* @param y | |
* @param objectDirection | |
* @param paint True if canvas should render the ray | |
* | |
* @returns CanvasRenderingContext2D.ImageData | |
*/ | |
function buildRay(x, y, objectDirection, paint) { | |
var rayLength = 3; // Pixels | |
var rayWidth = 1; | |
if (paint) { | |
ctx.beginPath(); | |
ctx.lineWidth = 3; | |
ctx.strokeStyle = 'red'; | |
ctx.moveTo(x, y); | |
} | |
switch (objectDirection) { | |
case UP: | |
var ray = ctx.getImageData(x, y, rayWidth, -rayLength); | |
ctx.lineTo(x, y - rayLength); | |
break; | |
case DOWN: | |
var ray = ctx.getImageData(x, y, rayWidth, rayLength); | |
ctx.lineTo(x, y + rayLength); | |
break; | |
case LEFT: | |
var ray = ctx.getImageData(x, y, -rayLength, rayWidth); | |
ctx.lineTo(x - rayLength, y); | |
break; | |
case RIGHT: | |
default: | |
var ray = ctx.getImageData(x, y, rayLength, rayWidth); | |
ctx.lineTo(x + rayLength, y); | |
break; | |
} | |
if (paint) { | |
ctx.stroke(); | |
} | |
return ray; | |
} | |
/** | |
* Check for pacman collisions using raycasting. | |
*/ | |
function checkCollision() { | |
var hitboxWidth = 40; | |
var hitboxHeight = 40; | |
var hitbox = buildHitBox(pacman.x, pacman.y, hitboxWidth, hitboxHeight, false); | |
var pacmanOffsetX = hitboxWidth / 2; | |
var pacmanOffsetY = hitboxHeight / 2; | |
switch (direction) { | |
case UP: | |
var rayOne = buildRay(pacman.x - pacmanOffsetX, pacman.y - pacmanOffsetY, direction, true); | |
var rayTwo = buildRay(pacman.x + pacmanOffsetX, pacman.y - pacmanOffsetY, direction, true); | |
var rayThree = buildRay(pacman.x, pacman.y - pacmanOffsetY, direction, true); | |
break; | |
case DOWN: | |
var rayOne = buildRay(pacman.x - pacmanOffsetX, pacman.y + pacmanOffsetY, direction, true); | |
var rayTwo = buildRay(pacman.x + pacmanOffsetX, pacman.y + pacmanOffsetY, direction, true); | |
var rayThree = buildRay(pacman.x, pacman.y + pacmanOffsetY, direction, true); | |
break; | |
case LEFT: | |
var rayOne = buildRay(pacman.x - pacmanOffsetX, pacman.y - pacmanOffsetY, direction, true); | |
var rayTwo = buildRay(pacman.x - pacmanOffsetX, pacman.y + pacmanOffsetY, direction, true); | |
var rayThree = buildRay(pacman.x - pacmanOffsetX, pacman.y, direction, true); | |
break; | |
case RIGHT: | |
default: | |
var rayOne = buildRay(pacman.x + pacmanOffsetX, pacman.y - pacmanOffsetY, direction, true); | |
var rayTwo = buildRay(pacman.x + pacmanOffsetX, pacman.y + pacmanOffsetY, direction, true); | |
var rayThree = buildRay(pacman.x + pacmanOffsetX, pacman.y, direction, true); | |
break; | |
} | |
if (checkCollisionInImage(rayOne) || checkCollisionInImage(rayTwo) || checkCollisionInImage(rayThree)) { | |
// If there is a collision, return the pacman to it's previous position before collision detection. | |
if (direction == UP) { | |
pacman.y = pacman.nextY; | |
} else if (direction == DOWN) { | |
pacman.y = pacman.nextY; | |
} else if (direction == LEFT) { | |
pacman.x = pacman.nextX; | |
} else if (direction == RIGHT) { | |
pacman.x = pacman.nextX; | |
}; | |
} | |
}; | |
/** | |
* Checks ImageData for a specified color, if found return True. False otherwise. | |
* | |
* @param ImageData image The area to search for a collision | |
* | |
* @return bool True if a collision is detected. | |
*/ | |
function checkCollisionInImage(image) { | |
// Check every pixel inside the ImageData box | |
for (var i = 0; i < image.data.length; i += 4) { | |
var redValue = image.data[i + 0]; | |
var greenValue = image.data[i + 1]; | |
var blueValue = image.data[i + 2] | |
var is_red = (redValue == 255 && greenValue == 0 && blueValue == 0); | |
var is_green = (redValue == 0 && greenValue == 128 && blueValue == 0); // 'Green' uses a darker green, hence 128. | |
var is_blue = (redValue == 0 && greenValue == 0 && blueValue == 255); | |
var is_yellow = (redValue == 255 && greenValue == 255 && blueValue == 0); | |
if (is_blue || is_green) { | |
// A blue wall or green bat was detected. | |
return true; | |
} | |
}; | |
return false; | |
} | |
/** | |
* Process the frame considering collisions and render the canvas | |
*/ | |
function run() { | |
update((Date.now() - time) / 500); | |
render(); | |
drawWall(); | |
checkCollision(); | |
time = Date.now(); | |
} | |
var time = Date.now(); | |
setInterval(run, 10); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment