Goose Game
4 months ago
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Goose Game</title>
<style>
body {
margin: 0;
overflow: hidden;
}
canvas {
display: block;
background-color: #75643f;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let x = canvas.width / 2;
let y = canvas.height / 2;
let radius = 30;
let targetRadius = 30;
const speed = 3;
const scaleSpeed = 1;
const minRadius = 30;
const maxRadius = 300;
let gooseHeadCollider = { x: 0, y: 0, r: 0 };
const keys = {
w: false,
a: false,
s: false,
d: false,
};
const palette = {
'1': 'tan',
'2': 'white',
'3': 'green'
};
const breadSprite = [
"011111111111110",
"112222222111111",
"122222222211111",
"122222222211111",
"012222222111110",
"001222221111100",
"001222221111100",
"001222221111100",
"001222221111100",
"001222221111100",
"001111111111100"
];
const vegetableSprite = [
"0000333330000",
"0033333333300",
"0333333333330",
"3333303033333",
"3333003003333",
"0303303033030",
"0000333330000",
"0000033300000",
"0000033300000",
"0000033300000",
"0000033300000"
];
const breads = [];
const vegetables = [];
function getBiasedY(canvasHeight, exponent = 2, reverse = false) {
const r = Math.random();
const biased = reverse ? 1 - Math.pow(r, exponent) : Math.pow(r, exponent);
return biased * canvasHeight;
}
function spawnBread() {
breads.push({
x: canvas.width + 10,
y: getBiasedY(canvas.height, 2, true),
speed: 1,
scale: 2
});
}
function spawnVegetable() {
vegetables.push({
x: canvas.width + 10,
y: getBiasedY(canvas.height, 2, false),
speed: 2,
scale: 2
});
}
setInterval(spawnBread, 500);
setInterval(spawnVegetable, 500);
document.addEventListener('keydown', (e) => {
if (e.key in keys) keys[e.key] = true;
});
document.addEventListener('keyup', (e) => {
if (e.key in keys) keys[e.key] = false;
});
function update() {
// Move breads and update their colliders first
for (const bread of breads) {
bread.x -= bread.speed;
const spriteWidth = breadSprite[0].length * bread.scale;
const spriteHeight = breadSprite.length * bread.scale;
bread.collider = {
x: bread.x + spriteWidth * 0.1,
y: bread.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
for (const veg of vegetables) {
veg.x -= veg.speed;
const spriteWidth = vegetableSprite[0].length * veg.scale;
const spriteHeight = vegetableSprite.length * veg.scale;
veg.collider = {
x: veg.x + spriteWidth * 0.1,
y: veg.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
// Now check for collisions and remove sprites
for (let i = breads.length - 1; i >= 0; i--) {
const bread = breads[i];
const dx = gooseHeadCollider.x - (bread.collider.x + bread.collider.width / 2);
const dy = gooseHeadCollider.y - (bread.collider.y + bread.collider.height / 2);
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < gooseHeadCollider.r + Math.min(bread.collider.width, bread.collider.height) / 2) {
breads.splice(i, 1);
targetRadius += 10; // Increase target radius
}
}
for (let i = vegetables.length - 1; i >= 0; i--) {
const veg = vegetables[i];
const dx = gooseHeadCollider.x - (veg.collider.x + veg.collider.width / 2);
const dy = gooseHeadCollider.y - (veg.collider.y + veg.collider.height / 2);
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < gooseHeadCollider.r + Math.min(veg.collider.width, veg.collider.height) / 2) {
vegetables.splice(i, 1);
targetRadius = Math.max(minRadius, targetRadius - 30);
}
}
// Movement controls
if (keys.w) y -= speed;
if (keys.s) y += speed;
if (keys.a) x -= speed;
if (keys.d) x += speed;
// Smoothly interpolate radius toward targetRadius
radius += (targetRadius - radius) * 0.1;
radius = Math.max(minRadius, Math.min(maxRadius, radius));
x = Math.max(radius, Math.min(canvas.width - radius, x));
y = Math.max(radius, Math.min(canvas.height - radius, y));
for (const bread of breads) {
bread.x -= bread.speed;
// Bread box collider (slightly smaller than sprite)
const spriteWidth = breadSprite[0].length * bread.scale;
const spriteHeight = breadSprite.length * bread.scale;
bread.collider = {
x: bread.x + spriteWidth * 0.1,
y: bread.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
for (const veg of vegetables) {
veg.x -= veg.speed;
// Vegetable box collider (slightly smaller than sprite)
const spriteWidth = vegetableSprite[0].length * veg.scale;
const spriteHeight = vegetableSprite.length * veg.scale;
veg.collider = {
x: veg.x + spriteWidth * 0.1,
y: veg.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
}
function drawGoose() {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
const angle = -Math.PI / 2;
const headDistance = radius + 16;
const headX = x + Math.cos(angle) * headDistance;
const headY = y + Math.sin(angle) * headDistance;
const neckWidth = 10;
const neckHeight = 20;
ctx.save();
ctx.translate(headX, headY);
ctx.rotate(angle + Math.PI / 2);
ctx.fillStyle = 'white';
ctx.fillRect(-neckWidth / 2, 0, neckWidth, neckHeight);
ctx.restore();
const headRadius = 12;
ctx.beginPath();
ctx.arc(headX, headY, headRadius, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
const localBeakPoints = [
{ x: -6, y: 25 },
{ x: -6, y: 10 },
{ x: 6, y: 11 }
];
const rotatedPoints = localBeakPoints.map(p => {
const rotatedX = p.x * Math.cos(angle) - p.y * Math.sin(angle);
const rotatedY = p.x * Math.sin(angle) + p.y * Math.cos(angle);
return {
x: headX + rotatedX,
y: headY + rotatedY
};
});
ctx.beginPath();
ctx.moveTo(rotatedPoints[0].x, rotatedPoints[0].y);
ctx.lineTo(rotatedPoints[1].x, rotatedPoints[1].y);
ctx.lineTo(rotatedPoints[2].x, rotatedPoints[2].y);
ctx.closePath();
ctx.fillStyle = 'orange';
ctx.fill();
ctx.beginPath();
ctx.arc(headX + 3, headY - 3, 2, 0, Math.PI * 2);
ctx.fillStyle = 'black';
ctx.fill();
drawPixelBow(ctx, headX - 12, headY + 16, 2);
// Head collider for use in logic or debugging
gooseHeadCollider = {
x: headX,
y: headY,
r: headRadius
};
}
function drawPixelBow(ctx, startX, startY, scale = 2) {
const bowPixels = [
"0111011101110",
"1111111111111",
"1111111111111",
"0110110110110",
"0000110110000",
"0001110111000",
"0011100011100",
"0001100011000"
];
ctx.fillStyle = '#57aeff';
for (let row = 0; row < bowPixels.length; row++) {
for (let col = 0; col < bowPixels[row].length; col++) {
if (bowPixels[row][col] === '1') {
ctx.fillRect(startX + col * scale, startY + row * scale, scale, scale);
}
}
}
}
function drawPolkaDots(ctx, canvasWidth, canvasHeight, spacing = 40, dotRadius = 4) {
ctx.fillStyle = 'white';
for (let row = 0, y = 0; y < canvasHeight; row++, y += spacing) {
const isOddRow = row % 2 === 1;
const xOffset = isOddRow ? spacing / 2 : 0;
for (let x = 0; x < canvasWidth; x += spacing) {
ctx.beginPath();
ctx.arc(x + xOffset, y, dotRadius, 0, Math.PI * 2);
ctx.fill();
}
}
}
function drawSprite(ctx, pixelData, startX, startY, scale = 2, palette = {}) {
for (let row = 0; row < pixelData.length; row++) {
for (let col = 0; col < pixelData[row].length; col++) {
const code = pixelData[row][col];
const color = palette[code];
if (color) {
ctx.fillStyle = color;
ctx.fillRect(startX + col * scale, startY + row * scale, scale, scale);
}
}
}
}
function draw() {
ctx.fillStyle = '#94bce0';
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPolkaDots(ctx, canvas.width, canvas.height, 49, 2);
drawGoose();
for (const bread of breads) {
drawSprite(ctx, breadSprite, bread.x, bread.y, bread.scale, palette);
}
for (const veg of vegetables) {
drawSprite(ctx, vegetableSprite, veg.x, veg.y, veg.scale, palette);
}
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
gameLoop();
</script>
</body>
</html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Goose Game</title>
<style>
body {
margin: 0;
overflow: hidden;
}
canvas {
display: block;
background-color: #75643f;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let x = canvas.width / 2;
let y = canvas.height / 2;
let radius = 30;
let targetRadius = 30;
const speed = 3;
const scaleSpeed = 1;
const minRadius = 30;
const maxRadius = 300;
let gooseHeadCollider = { x: 0, y: 0, r: 0 };
const keys = {
w: false,
a: false,
s: false,
d: false,
};
const palette = {
'1': 'tan',
'2': 'white',
'3': 'green'
};
const breadSprite = [
"011111111111110",
"112222222111111",
"122222222211111",
"122222222211111",
"012222222111110",
"001222221111100",
"001222221111100",
"001222221111100",
"001222221111100",
"001222221111100",
"001111111111100"
];
const vegetableSprite = [
"0000333330000",
"0033333333300",
"0333333333330",
"3333303033333",
"3333003003333",
"0303303033030",
"0000333330000",
"0000033300000",
"0000033300000",
"0000033300000",
"0000033300000"
];
const breads = [];
const vegetables = [];
function getBiasedY(canvasHeight, exponent = 2, reverse = false) {
const r = Math.random();
const biased = reverse ? 1 - Math.pow(r, exponent) : Math.pow(r, exponent);
return biased * canvasHeight;
}
function spawnBread() {
breads.push({
x: canvas.width + 10,
y: getBiasedY(canvas.height, 2, true),
speed: 1,
scale: 2
});
}
function spawnVegetable() {
vegetables.push({
x: canvas.width + 10,
y: getBiasedY(canvas.height, 2, false),
speed: 2,
scale: 2
});
}
setInterval(spawnBread, 500);
setInterval(spawnVegetable, 500);
document.addEventListener('keydown', (e) => {
if (e.key in keys) keys[e.key] = true;
});
document.addEventListener('keyup', (e) => {
if (e.key in keys) keys[e.key] = false;
});
function update() {
// Move breads and update their colliders first
for (const bread of breads) {
bread.x -= bread.speed;
const spriteWidth = breadSprite[0].length * bread.scale;
const spriteHeight = breadSprite.length * bread.scale;
bread.collider = {
x: bread.x + spriteWidth * 0.1,
y: bread.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
for (const veg of vegetables) {
veg.x -= veg.speed;
const spriteWidth = vegetableSprite[0].length * veg.scale;
const spriteHeight = vegetableSprite.length * veg.scale;
veg.collider = {
x: veg.x + spriteWidth * 0.1,
y: veg.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
// Now check for collisions and remove sprites
for (let i = breads.length - 1; i >= 0; i--) {
const bread = breads[i];
const dx = gooseHeadCollider.x - (bread.collider.x + bread.collider.width / 2);
const dy = gooseHeadCollider.y - (bread.collider.y + bread.collider.height / 2);
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < gooseHeadCollider.r + Math.min(bread.collider.width, bread.collider.height) / 2) {
breads.splice(i, 1);
targetRadius += 10; // Increase target radius
}
}
for (let i = vegetables.length - 1; i >= 0; i--) {
const veg = vegetables[i];
const dx = gooseHeadCollider.x - (veg.collider.x + veg.collider.width / 2);
const dy = gooseHeadCollider.y - (veg.collider.y + veg.collider.height / 2);
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < gooseHeadCollider.r + Math.min(veg.collider.width, veg.collider.height) / 2) {
vegetables.splice(i, 1);
targetRadius = Math.max(minRadius, targetRadius - 30);
}
}
// Movement controls
if (keys.w) y -= speed;
if (keys.s) y += speed;
if (keys.a) x -= speed;
if (keys.d) x += speed;
// Smoothly interpolate radius toward targetRadius
radius += (targetRadius - radius) * 0.1;
radius = Math.max(minRadius, Math.min(maxRadius, radius));
x = Math.max(radius, Math.min(canvas.width - radius, x));
y = Math.max(radius, Math.min(canvas.height - radius, y));
for (const bread of breads) {
bread.x -= bread.speed;
// Bread box collider (slightly smaller than sprite)
const spriteWidth = breadSprite[0].length * bread.scale;
const spriteHeight = breadSprite.length * bread.scale;
bread.collider = {
x: bread.x + spriteWidth * 0.1,
y: bread.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
for (const veg of vegetables) {
veg.x -= veg.speed;
// Vegetable box collider (slightly smaller than sprite)
const spriteWidth = vegetableSprite[0].length * veg.scale;
const spriteHeight = vegetableSprite.length * veg.scale;
veg.collider = {
x: veg.x + spriteWidth * 0.1,
y: veg.y + spriteHeight * 0.1,
width: spriteWidth * 0.8,
height: spriteHeight * 0.8
};
}
}
function drawGoose() {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
const angle = -Math.PI / 2;
const headDistance = radius + 16;
const headX = x + Math.cos(angle) * headDistance;
const headY = y + Math.sin(angle) * headDistance;
const neckWidth = 10;
const neckHeight = 20;
ctx.save();
ctx.translate(headX, headY);
ctx.rotate(angle + Math.PI / 2);
ctx.fillStyle = 'white';
ctx.fillRect(-neckWidth / 2, 0, neckWidth, neckHeight);
ctx.restore();
const headRadius = 12;
ctx.beginPath();
ctx.arc(headX, headY, headRadius, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
const localBeakPoints = [
{ x: -6, y: 25 },
{ x: -6, y: 10 },
{ x: 6, y: 11 }
];
const rotatedPoints = localBeakPoints.map(p => {
const rotatedX = p.x * Math.cos(angle) - p.y * Math.sin(angle);
const rotatedY = p.x * Math.sin(angle) + p.y * Math.cos(angle);
return {
x: headX + rotatedX,
y: headY + rotatedY
};
});
ctx.beginPath();
ctx.moveTo(rotatedPoints[0].x, rotatedPoints[0].y);
ctx.lineTo(rotatedPoints[1].x, rotatedPoints[1].y);
ctx.lineTo(rotatedPoints[2].x, rotatedPoints[2].y);
ctx.closePath();
ctx.fillStyle = 'orange';
ctx.fill();
ctx.beginPath();
ctx.arc(headX + 3, headY - 3, 2, 0, Math.PI * 2);
ctx.fillStyle = 'black';
ctx.fill();
drawPixelBow(ctx, headX - 12, headY + 16, 2);
// Head collider for use in logic or debugging
gooseHeadCollider = {
x: headX,
y: headY,
r: headRadius
};
}
function drawPixelBow(ctx, startX, startY, scale = 2) {
const bowPixels = [
"0111011101110",
"1111111111111",
"1111111111111",
"0110110110110",
"0000110110000",
"0001110111000",
"0011100011100",
"0001100011000"
];
ctx.fillStyle = '#57aeff';
for (let row = 0; row < bowPixels.length; row++) {
for (let col = 0; col < bowPixels[row].length; col++) {
if (bowPixels[row][col] === '1') {
ctx.fillRect(startX + col * scale, startY + row * scale, scale, scale);
}
}
}
}
function drawPolkaDots(ctx, canvasWidth, canvasHeight, spacing = 40, dotRadius = 4) {
ctx.fillStyle = 'white';
for (let row = 0, y = 0; y < canvasHeight; row++, y += spacing) {
const isOddRow = row % 2 === 1;
const xOffset = isOddRow ? spacing / 2 : 0;
for (let x = 0; x < canvasWidth; x += spacing) {
ctx.beginPath();
ctx.arc(x + xOffset, y, dotRadius, 0, Math.PI * 2);
ctx.fill();
}
}
}
function drawSprite(ctx, pixelData, startX, startY, scale = 2, palette = {}) {
for (let row = 0; row < pixelData.length; row++) {
for (let col = 0; col < pixelData[row].length; col++) {
const code = pixelData[row][col];
const color = palette[code];
if (color) {
ctx.fillStyle = color;
ctx.fillRect(startX + col * scale, startY + row * scale, scale, scale);
}
}
}
}
function draw() {
ctx.fillStyle = '#94bce0';
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPolkaDots(ctx, canvas.width, canvas.height, 49, 2);
drawGoose();
for (const bread of breads) {
drawSprite(ctx, breadSprite, bread.x, bread.y, bread.scale, palette);
}
for (const veg of vegetables) {
drawSprite(ctx, vegetableSprite, veg.x, veg.y, veg.scale, palette);
}
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
gameLoop();
</script>
</body>
</html>
Otto Stubclaw
~~shadowstubclaw~
Really been on the HTML grind haven’t you?
Gandere
~gandere
OP
If by that you mean browbeating a LLM until it makes something that works, yes. I have.
Nuclear
~dickers69
Is this the entire code for goose game?
Gandere
~gandere
OP
That's the whole thing. I wouldn't give you something you couldn't play.
GeltyDrake
~geltydrake
Amazing! <3
cheeky.squeaker
~cheeky.squeaker
This was a fun little experience! Although I would admit, after playing for a bit, I changed the code that determines the spawn rate for the bread, and just watched the chaos happen.
Gandere
~gandere
OP
I encourage everyone to screw around with the code like this.
Evan H.
~mickeymouseaddict
I made my goose so big you can't see his head. Very nice!
YES
~yesitis
I love the detail of broccoli being the evil substance that reduces your figure.
D322MW
~d322mw
That's so freakin awesome, and I had so much fun! X3 Can there be a part where the goose can go off-screen while still being fed? :3 Really wanna see the goose get extremely fat! LOL
hoodah
~hoodah
atari port when?
Gandere
~gandere
OP
It's called Fast Food.
Vuhsar
~vuhsar
I've become too fat I can't reach food anymore ARRGHJH!
Gandere
~gandere
OP
It's deeply symbolic of real life struggles, you see.
Oi-Its-Joi
~oi-its-joi
What do I input this into? The last programming language I used was Scratch.
Gandere
~gandere
OP
It's HTML. It runs in a web browser.
Dusk
~duskthekobold
How does one get this to work in a web browser?
Gandere
~gandere
OP
Seek information in your search engine of choice related to .HTML files.
FA+