Skip to content

Instantly share code, notes, and snippets.

@tylersnowden
Last active August 11, 2022 02:08
Show Gist options
  • Save tylersnowden/fb61c4d36b22f453ad533e0fe0d9e87d to your computer and use it in GitHub Desktop.
Save tylersnowden/fb61c4d36b22f453ad533e0fe0d9e87d to your computer and use it in GitHub Desktop.
Pacman v0.1
<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