Skip to content

Instantly share code, notes, and snippets.

@ofou
Created March 28, 2025 17:56
Show Gist options
  • Save ofou/58efcff32571c6e7831b0e54a4325da3 to your computer and use it in GitHub Desktop.
Save ofou/58efcff32571c6e7831b0e54a4325da3 to your computer and use it in GitHub Desktop.
Dinosaur game vibecoding
// Pixelated Dinosaur Runner - Endless Runner Game
// Controls: Press SPACE, UP ARROW, or CLICK to jump
// Game variables
let dino;
let obstacles = [];
let grounds = [];
let clouds = [];
let score = 0;
let gameSpeed = 5;
let gravity = 0.6;
let isGameOver = false;
let backgroundLayers = [];
let highScore = 0;
// Pixel art for dinosaur
let dinoPixelArt = [
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 0, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
];
// Pixel art for cactus obstacles
let cactusPixelArt = [
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 1, 0, 1, 1, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
];
// Dinosaur class
class Dinosaur {
constructor() {
this.x = 50;
this.y = height - 100;
this.velocityY = 0;
this.width = 50;
this.height = 50;
this.isJumping = false;
this.runFrame = 0;
this.runFrameCount = 0;
this.runFrameSpeed = 5;
}
jump() {
if (!this.isJumping) {
this.velocityY = -15;
this.isJumping = true;
}
}
update() {
// Apply gravity
this.velocityY += gravity;
this.y += this.velocityY;
// Ground collision
if (this.y > height - 100) {
this.y = height - 100;
this.velocityY = 0;
this.isJumping = false;
}
// Animation frame
if (!this.isJumping) {
this.runFrameCount++;
if (this.runFrameCount > this.runFrameSpeed) {
this.runFrame = (this.runFrame + 1) % 2; // Alternate between 0 and 1
this.runFrameCount = 0;
}
}
}
draw() {
push();
translate(this.x, this.y);
noStroke();
// Draw the pixelated dinosaur
let pixelSize = this.width / dinoPixelArt[0].length;
for (let y = 0; y < dinoPixelArt.length; y++) {
for (let x = 0; x < dinoPixelArt[y].length; x++) {
if (dinoPixelArt[y][x] === 1) {
fill(50, 150, 50); // Green dinosaur
rect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
}
}
}
// Draw legs based on animation frame (if not jumping)
if (!this.isJumping) {
if (this.runFrame === 0) {
// Left leg up
fill(50, 150, 50);
rect(3 * pixelSize, 9 * pixelSize, pixelSize, pixelSize);
rect(6 * pixelSize, 9 * pixelSize, pixelSize, pixelSize);
rect(6 * pixelSize, 10 * pixelSize, pixelSize, pixelSize);
} else {
// Right leg up
fill(50, 150, 50);
rect(3 * pixelSize, 9 * pixelSize, pixelSize, pixelSize);
rect(3 * pixelSize, 10 * pixelSize, pixelSize, pixelSize);
rect(6 * pixelSize, 9 * pixelSize, pixelSize, pixelSize);
}
} else {
// Both legs down for jumping
fill(50, 150, 50);
rect(3 * pixelSize, 9 * pixelSize, pixelSize, pixelSize);
rect(6 * pixelSize, 9 * pixelSize, pixelSize, pixelSize);
}
pop();
}
getHitbox() {
// Return a slightly smaller hitbox than the actual sprite for better gameplay
return {
x: this.x + 10,
y: this.y + 10,
width: this.width - 20,
height: this.height - 20
};
}
}
// Obstacle class
class Obstacle {
constructor() {
this.width = 30;
this.height = 50;
this.x = width;
this.y = height - 100;
this.speed = gameSpeed;
this.type = floor(random(3)); // 0: small, 1: medium, 2: large
// Adjust size based on type
if (this.type === 0) {
this.height = 30;
} else if (this.type === 2) {
this.height = 70;
this.width = 40;
}
}
update() {
this.x -= this.speed;
}
draw() {
push();
translate(this.x, this.y - this.height);
noStroke();
// Draw the pixelated cactus
let pixelSize = this.width / cactusPixelArt[0].length;
for (let y = 0; y < cactusPixelArt.length; y++) {
for (let x = 0; x < cactusPixelArt[y].length; x++) {
if (cactusPixelArt[y][x] === 1) {
fill(30, 120, 30); // Darker green cactus
rect(x * pixelSize, y * pixelSize, pixelSize * (this.type + 1), pixelSize * (this.type + 1));
}
}
}
pop();
}
getHitbox() {
return {
x: this.x,
y: this.y - this.height,
width: this.width,
height: this.height
};
}
isOffScreen() {
return this.x < -this.width;
}
}
// Ground class for scrolling ground
class Ground {
constructor(x) {
this.x = x;
this.y = height - 50;
this.width = width;
this.speed = gameSpeed;
}
update() {
this.x -= this.speed;
if (this.x < -this.width) {
this.x = width;
}
}
draw() {
noStroke();
fill(120, 100, 40);
rect(this.x, this.y, this.width, 50);
// Draw pixelated ground details
fill(150, 120, 50);
for (let i = 0; i < this.width; i += 20) {
rect(this.x + i, this.y - 2, 10, 2);
}
}
}
// Cloud class for background
class Cloud {
constructor() {
this.x = width;
this.y = random(50, 150);
this.width = random(60, 120);
this.height = random(20, 40);
this.speed = gameSpeed * 0.3;
}
update() {
this.x -= this.speed;
}
draw() {
noStroke();
fill(250);
// Draw pixelated cloud
let pixelSize = 5;
for (let y = 0; y < this.height / pixelSize; y++) {
for (let x = 0; x < this.width / pixelSize; x++) {
// Create a cloud-like pattern
if (random() > 0.3 &&
(y > 0 || x > 0) &&
(y < this.height / pixelSize - 1 || x < this.width / pixelSize - 1)) {
rect(this.x + x * pixelSize, this.y + y * pixelSize, pixelSize, pixelSize);
}
}
}
}
isOffScreen() {
return this.x < -this.width;
}
}
// Background layer class for parallax effect
class BackgroundLayer {
constructor(color, speed, y, height) {
this.color = color;
this.speed = speed;
this.y = y;
this.height = height;
this.mountains = [];
// Generate a mountain range
let mountainX = 0;
while (mountainX < width + 500) {
let mountainHeight = random(30, 80);
let mountainWidth = random(50, 150);
this.mountains.push({
x: mountainX,
width: mountainWidth,
height: mountainHeight
});
mountainX += mountainWidth - random(20, 40); // Overlap mountains a bit
}
}
update() {
// Move mountains
for (let mountain of this.mountains) {
mountain.x -= this.speed;
if (mountain.x < -mountain.width) {
// Reset mountain to the right side
mountain.x = width;
mountain.height = random(30, 80);
mountain.width = random(50, 150);
}
}
}
draw() {
noStroke();
fill(this.color);
rect(0, this.y, width, this.height);
// Draw pixelated mountains
for (let mountain of this.mountains) {
this.drawPixelatedMountain(mountain.x, this.y, mountain.width, mountain.height);
}
}
drawPixelatedMountain(x, y, mountainWidth, mountainHeight) {
let pixelSize = 5;
fill(80, 60, 30);
// Create a triangular mountain shape with pixels
for (let i = 0; i < mountainWidth / pixelSize; i++) {
let h = sin((i / (mountainWidth / pixelSize)) * PI) * mountainHeight;
for (let j = 0; j < h / pixelSize; j++) {
rect(x + i * pixelSize, y - j * pixelSize, pixelSize, pixelSize);
}
}
// Add snow caps
fill(240);
for (let i = 0; i < mountainWidth / pixelSize; i++) {
let h = sin((i / (mountainWidth / pixelSize)) * PI) * mountainHeight;
let snowHeight = random(h * 0.2, h * 0.4);
for (let j = 0; j < snowHeight / pixelSize; j++) {
if (random() > 0.3) { // Add some randomness to snow
rect(x + i * pixelSize, y - h + j * pixelSize, pixelSize, pixelSize);
}
}
}
}
}
// Setup function for p5.js
function setup() {
createCanvas(800, 400);
pixelDensity(1); // Set pixel density for better pixel art rendering
// Create the dinosaur
dino = new Dinosaur();
// Create initial ground pieces
grounds.push(new Ground(0));
grounds.push(new Ground(width));
// Create initial clouds
for (let i = 0; i < 3; i++) {
clouds.push(new Cloud());
clouds[i].x = random(width);
}
// Create background layers for parallax effect
let skyGradient = color(135, 206, 235); // Sky blue
backgroundLayers.push(new BackgroundLayer(skyGradient, gameSpeed * 0.1, 0, height - 50));
let midgroundColor = color(110, 180, 200);
backgroundLayers.push(new BackgroundLayer(midgroundColor, gameSpeed * 0.2, height - 200, 150));
}
// Draw function for p5.js
function draw() {
background(100, 150, 255);
if (!isGameOver) {
// Update game
updateGame();
}
// Draw everything
drawGame();
// Draw UI elements
drawUI();
}
function updateGame() {
// Update dinosaur
dino.update();
// Update background layers
for (let layer of backgroundLayers) {
layer.update();
}
// Update grounds
for (let ground of grounds) {
ground.update();
}
// Update obstacles
for (let i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].update();
// Check for collision
if (checkCollision(dino.getHitbox(), obstacles[i].getHitbox())) {
gameOver();
}
// Remove off-screen obstacles
if (obstacles[i].isOffScreen()) {
obstacles.splice(i, 1);
}
}
// Update clouds
for (let i = clouds.length - 1; i >= 0; i--) {
clouds[i].update();
if (clouds[i].isOffScreen()) {
clouds.splice(i, 1);
}
}
// Add new obstacles randomly
if (frameCount % 60 === 0 && random() > 0.3) {
obstacles.push(new Obstacle());
}
// Add new clouds randomly
if (frameCount % 100 === 0 && random() > 0.5) {
clouds.push(new Cloud());
}
// Update score
if (frameCount % 5 === 0) {
score++;
// Every 100 points
if (score % 100 === 0) {
gameSpeed += 0.25; // Increase game speed every 100 points
}
}
}
function drawGame() {
// Draw background layers
for (let layer of backgroundLayers) {
layer.draw();
}
// Draw clouds
for (let cloud of clouds) {
cloud.draw();
}
// Draw grounds
for (let ground of grounds) {
ground.draw();
}
// Draw obstacles
for (let obstacle of obstacles) {
obstacle.draw();
}
// Draw dinosaur
dino.draw();
}
function drawUI() {
// Draw score
fill(0);
textSize(20);
textAlign(RIGHT);
text(`Score: ${score}`, width - 20, 30);
// Draw high score
textAlign(LEFT);
text(`HI: ${highScore}`, 20, 30);
// Draw instructions on screen
if (isGameOver) {
textAlign(CENTER);
fill(255, 0, 0);
textSize(40);
text("GAME OVER", width / 2, height / 2 - 20);
fill(0);
textSize(20);
text("Press SPACE or CLICK to restart", width / 2, height / 2 + 20);
} else if (frameCount < 180) {
// Show instructions at the beginning
textAlign(CENTER);
fill(0);
textSize(20);
text("Press SPACE, UP ARROW, or CLICK to jump", width / 2, height / 2);
textSize(16);
text("Avoid obstacles and survive as long as possible", width / 2, height / 2 + 30);
}
}
function keyPressed() {
if (key === ' ' || keyCode === UP_ARROW) {
if (isGameOver) {
resetGame();
} else {
dino.jump();
}
}
}
function mousePressed() {
if (isGameOver) {
resetGame();
} else {
dino.jump();
}
}
function checkCollision(rect1, rect2) {
return rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y;
}
function gameOver() {
isGameOver = true;
// Update high score
if (score > highScore) {
highScore = score;
}
}
function resetGame() {
dino = new Dinosaur();
obstacles = [];
score = 0;
gameSpeed = 5;
isGameOver = false;
// Reset grounds
grounds = [];
grounds.push(new Ground(0));
grounds.push(new Ground(width));
// Reset clouds
clouds = [];
for (let i = 0; i < 3; i++) {
clouds.push(new Cloud());
clouds[i].x = random(width);
}
}
let dino;
let obstacles = [];
let score = 0;
let highScore = 0; // Added high score
let gameOver = false;
let gravity;
let jumpForce;
let groundY;
let gameSpeed = 5;
let minObstacleDist = 50;
let nextObstacleFrame;
// --- Pixel Art Data ---
const PIXEL_SCALE = 4;
let C_TRANS, C_GREEN, C_DGREEN, C_WHITE, C_LGRAY, C_DGRAY, C_BROWN, C_SKY, C_GROUND, C_PTERO;
// Dinosaur Frame 1
const dinoPixels1 = [
[0,0,1,1,1,1,0,0,0,0],
[0,1,1,1,1,1,1,0,0,0],
[0,1,1,1,1,1,1,0,0,0],
[0,1,1,1,1,0,0,0,0,0],
[1,1,1,1,1,1,1,1,0,0],
[1,1,1,1,1,1,1,1,1,0],
[0,0,1,1,1,1,1,1,1,0],
[0,0,1,1,0,1,1,0,0,0], // Body/Tail
[0,0,1,1,0,1,1,0,0,0],
[0,0,2,2,0,2,2,0,0,0], // Legs 1
[0,0,2,0,0,2,0,0,0,0],
[0,0,2,0,0,2,0,0,0,0]
];
// Dinosaur Frame 2 (Legs swapped)
const dinoPixels2 = [
[0,0,1,1,1,1,0,0,0,0],
[0,1,1,1,1,1,1,0,0,0],
[0,1,1,1,1,1,1,0,0,0],
[0,1,1,1,1,0,0,0,0,0],
[1,1,1,1,1,1,1,1,0,0],
[1,1,1,1,1,1,1,1,1,0],
[0,0,1,1,1,1,1,1,1,0],
[0,0,1,1,0,1,1,0,0,0], // Body/Tail
[0,0,1,1,0,1,1,0,0,0],
[0,0,2,2,0,2,2,0,0,0], // Legs 2
[0,0,0,2,0,0,2,0,0,0],
[0,0,0,2,0,0,2,0,0,0]
];
const dinoFrames = [dinoPixels1, dinoPixels2];
const dinoWidth = dinoPixels1[0].length;
const dinoHeight = dinoPixels1.length;
// Obstacle (Cactus)
const cactusPixels = [
[0,1,1,1,0],
[0,1,1,1,0],
[1,1,1,1,1],
[1,1,1,1,1],
[0,1,1,1,0],
[0,1,1,1,0],
[0,1,1,1,0],
[0,1,1,1,0],
[0,1,1,1,0]
];
const cactusWidth = cactusPixels[0].length;
const cactusHeight = cactusPixels.length;
// Obstacle (Pterodactyl) - 12x8 pixels
const pteroPixels1 = [ // Frame 1 (Wings up)
[0,0,0,0,3,3,3,3,0,0,0,0],
[0,0,0,3,3,3,3,3,3,0,0,0],
[0,0,3,3,3,3,3,3,3,3,0,0],
[0,3,3,3,3,3,3,3,3,3,3,0],
[3,3,3,3,3,3,3,3,3,3,3,3],
[0,0,0,3,3,3,3,3,3,0,0,0],
[0,0,0,0,3,3,3,3,0,0,0,0],
[0,0,0,0,0,3,3,0,0,0,0,0] // Small body part
];
const pteroPixels2 = [ // Frame 2 (Wings down)
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,3,3,3,3,0,0,0,0],
[0,0,0,3,3,3,3,3,3,0,0,0],
[0,0,3,3,3,3,3,3,3,3,0,0],
[0,3,3,3,3,3,3,3,3,3,3,0],
[3,3,3,3,3,3,3,3,3,3,3,3],
[0,3,3,0,3,3,3,3,0,3,3,0], // Body/Legs visible
[0,0,0,0,3,3,3,3,0,0,0,0]
];
const pteroFrames = [pteroPixels1, pteroPixels2];
const pteroWidth = pteroPixels1[0].length;
const pteroHeight = pteroPixels1.length;
// --- Background Elements ---
let clouds = [];
let hills = [];
let groundDetail = [];
function setup() {
createCanvas(windowWidth, 400);
pixelDensity(1);
// Define Colors
C_TRANS = color(0, 0, 0, 0);
C_GREEN = color(50, 180, 50);
C_DGREEN = color(30, 120, 30);
C_WHITE = color(255);
C_LGRAY = color(200);
C_DGRAY = color(100);
C_BROWN = color(139, 69, 19);
C_PTERO = color(180, 160, 190); // Pterodactyl color
C_SKY = color(135, 206, 250);
C_GROUND = color(210, 180, 140);
groundY = height - 50;
gravity = createVector(0, 0.6);
jumpForce = createVector(0, -12);
dino = new Dinosaur();
nextObstacleFrame = floor(random(minObstacleDist * 2, minObstacleDist * 4));
textAlign(CENTER);
textSize(20);
textFont('monospace');
// Load high score from local storage
let storedHighScore = localStorage.getItem('dinoHighScore');
if (storedHighScore) {
highScore = parseInt(storedHighScore);
}
initializeBackground();
}
function initializeBackground() {
clouds = [];
hills = [];
groundDetail = [];
for (let i = 0; i < 5; i++) {
clouds.push({ x: random(width), y: random(50, 150), size: random(40, 80) });
hills.push({ x: random(width), y: groundY - random(20, 60), w: random(100, 300), h: random(40, 80) });
}
for (let i = 0; i < 20; i++) {
groundDetail.push({x: random(width), y: groundY + random(5, height - groundY - 5), size: random(2, 5)});
}
}
function drawBackground() {
background(C_SKY);
// Hills
fill(188, 143, 143);
noStroke();
for (let i = 0; i < hills.length; i++) {
let hill = hills[i];
ellipse(hill.x, hill.y, hill.w, hill.h * 2);
hill.x -= gameSpeed * 0.2;
if (hill.x + hill.w / 2 < 0) {
hill.x = width + hill.w / 2 + random(50, 150);
hill.y = groundY - random(20, 60);
hill.w = random(100, 300);
hill.h = random(40, 80);
}
}
// Clouds
fill(C_WHITE);
noStroke();
for (let i = 0; i < clouds.length; i++) {
let cloud = clouds[i];
ellipse(cloud.x, cloud.y, cloud.size, cloud.size / 2);
ellipse(cloud.x + cloud.size * 0.3, cloud.y + cloud.size * 0.1, cloud.size * 0.8, cloud.size / 2.5);
ellipse(cloud.x - cloud.size * 0.2, cloud.y + cloud.size * 0.15, cloud.size * 0.7, cloud.size / 3);
cloud.x -= gameSpeed * 0.1;
if (cloud.x + cloud.size < 0) {
cloud.x = width + random(cloud.size, cloud.size * 2);
cloud.y = random(50, 150);
cloud.size = random(40, 80);
}
}
// Ground
fill(C_GROUND);
noStroke();
rect(0, groundY, width, height - groundY);
// Ground Detail
fill(C_BROWN);
for(let i = 0; i < groundDetail.length; i++) {
let detail = groundDetail[i];
ellipse(detail.x, detail.y, detail.size, detail.size / 1.5);
detail.x -= gameSpeed;
if (detail.x + detail.size < 0) {
detail.x = width + random(10, 50);
detail.y = groundY + random(5, height - groundY - 5);
detail.size = random(2, 5);
}
}
}
function draw() {
drawBackground();
if (gameOver) {
fill(0, 0, 0, 180);
rect(0, 0, width, height);
fill(C_WHITE);
textSize(32);
text("GAME OVER", width / 2, height / 2 - 40);
textSize(20);
text(`Score: ${score}`, width / 2, height / 2);
text(`High Score: ${highScore}`, width / 2, height / 2 + 30);
text("Press R to Restart", width / 2, height / 2 + 70);
noLoop();
return;
}
// --- Game Logic ---
dino.applyForce(gravity);
dino.update();
dino.show();
// Spawn obstacles
if (frameCount >= nextObstacleFrame) {
// Add chance for pterodactyl after score > 5
if (score > 5 && random() < 0.3) {
obstacles.push(new Pterodactyl());
} else {
obstacles.push(new Cactus());
}
gameSpeed = min(15, gameSpeed + 0.05);
let baseDist = width / gameSpeed * 1.5; // Adjust base distance based on speed
nextObstacleFrame = frameCount + floor(random(baseDist * 0.7, baseDist * 1.1)); // More variation
// Ensure minimum distance scales slightly with speed (less distance at higher speeds)
let dynamicMinDist = minObstacleDist * (1 + (15 - gameSpeed) / 15);
nextObstacleFrame = max(nextObstacleFrame, frameCount + dynamicMinDist);
}
// Update and draw obstacles
for (let i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].update();
obstacles[i].show();
if (dino.hits(obstacles[i])) {
gameOver = true;
// Update high score if needed
if (score > highScore) {
highScore = score;
localStorage.setItem('dinoHighScore', highScore); // Save high score
}
}
// Remove obstacles that are off-screen
if (obstacles[i].isOffscreen()) {
obstacles.splice(i, 1);
if (!gameOver) { // Only increment score if game isn't over
score++;
}
}
}
// --- UI ---
fill(0);
textSize(20);
textAlign(LEFT);
text(`Score: ${score}`, 20, 30);
text(`Hi: ${highScore}`, 20, 55); // Show high score
textAlign(CENTER);
text("Press SPACE to Jump", width / 2, 30);
}
// --- Helper Function to Draw Pixel Art ---
function drawPixelArt(pixelData, x, y, pixelScale, colorMap) {
push();
translate(x, y);
noStroke();
for (let r = 0; r < pixelData.length; r++) {
for (let c = 0; c < pixelData[r].length; c++) {
let colorIndex = pixelData[r][c];
if (colorIndex > 0) {
fill(colorMap[colorIndex]);
rect(c * pixelScale, r * pixelScale, pixelScale, pixelScale);
}
}
}
pop();
}
function keyPressed() {
if (key === ' ' && !gameOver) {
dino.jump();
}
if ((key === 'r' || key === 'R') && gameOver) {
restartGame();
}
}
function restartGame() {
obstacles = [];
score = 0;
gameSpeed = 5;
dino = new Dinosaur();
nextObstacleFrame = frameCount + floor(random(minObstacleDist * 2, minObstacleDist * 4));
initializeBackground(); // Re-randomize background elements
gameOver = false;
loop();
}
// --- Dinosaur Class ---
class Dinosaur {
constructor() {
this.baseWidth = dinoWidth * PIXEL_SCALE;
this.baseHeight = dinoHeight * PIXEL_SCALE;
this.width = this.baseWidth;
this.height = this.baseHeight;
this.pos = createVector(width / 4, groundY - this.height);
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);
this.onGround = true;
this.colorMap = { 1: C_GREEN, 2: C_DGREEN };
this.animFrame = 0;
this.animTimer = 0;
this.animSpeed = 8; // Frames per animation switch
}
applyForce(force) {
this.acc.add(force);
}
jump() {
if (this.onGround) {
this.applyForce(jumpForce);
this.onGround = false;
}
}
update() {
this.vel.add(this.acc);
this.pos.add(this.vel);
this.acc.mult(0);
if (this.pos.y + this.height >= groundY) {
this.pos.y = groundY - this.height;
this.vel.y = 0;
this.onGround = true;
} else {
this.onGround = false;
}
this.pos.y = min(this.pos.y, groundY - this.height);
// Animation
if (this.onGround) {
this.animTimer++;
if (this.animTimer >= this.animSpeed) {
this.animFrame = (this.animFrame + 1) % dinoFrames.length;
this.animTimer = 0;
}
} else {
this.animFrame = 0; // Use default frame when jumping
this.animTimer = 0;
}
}
show() {
drawPixelArt(dinoFrames[this.animFrame], this.pos.x, this.pos.y, PIXEL_SCALE, this.colorMap);
}
hits(obstacle) {
// Use base dimensions for collision, slightly inset
let dinoRight = this.pos.x + this.baseWidth * 0.8;
let dinoLeft = this.pos.x + this.baseWidth * 0.2;
let dinoBottom = this.pos.y + this.baseHeight * 0.9;
let dinoTop = this.pos.y + this.baseHeight * 0.1;
let obsRight = obstacle.pos.x + obstacle.width * 0.8;
let obsLeft = obstacle.pos.x + obstacle.width * 0.2;
let obsBottom = obstacle.pos.y + obstacle.height * 0.9;
let obsTop = obstacle.pos.y + obstacle.height * 0.1;
return (
dinoRight > obsLeft &&
dinoLeft < obsRight &&
dinoBottom > obsTop &&
dinoTop < obsBottom
);
}
}
// --- Base Obstacle Class (Can be extended or used directly) ---
class Obstacle {
constructor(pixelData, colorMap, width, height) {
this.pixelData = pixelData;
this.colorMap = colorMap;
this.width = width * PIXEL_SCALE;
this.height = height * PIXEL_SCALE;
this.pos = createVector(width, 0); // Initial X, Y set by subclass
this.animFrame = 0;
this.animTimer = 0;
this.animSpeed = 10; // Default animation speed
}
update() {
this.pos.x -= gameSpeed;
// Animate if multiple frames exist
if (Array.isArray(this.pixelData[0][0])) { // Check if pixelData is an array of frames
this.animTimer++;
if (this.animTimer >= this.animSpeed) {
this.animFrame = (this.animFrame + 1) % this.pixelData.length;
this.animTimer = 0;
}
}
}
show() {
let frameToShow = Array.isArray(this.pixelData[0][0]) ? this.pixelData[this.animFrame] : this.pixelData;
drawPixelArt(frameToShow, this.pos.x, this.pos.y, PIXEL_SCALE, this.colorMap);
}
isOffscreen() {
return this.pos.x + this.width < 0;
}
}
// --- Cactus Class ---
class Cactus extends Obstacle {
constructor() {
super(cactusPixels, { 1: C_BROWN }, cactusWidth, cactusHeight);
this.pos.y = groundY - this.height; // Place on ground
}
}
// --- Pterodactyl Class ---
class Pterodactyl extends Obstacle {
constructor() {
super(pteroFrames, { 3: C_PTERO }, pteroWidth, pteroHeight);
// Spawn at one of two heights
let spawnHeightChoice = random([1, 2]);
if (spawnHeightChoice === 1) {
this.pos.y = groundY - this.height - dino.height * 0.8; // Head height
} else {
this.pos.y = groundY - this.height * 1.8; // Higher up
}
this.animSpeed = 12; // Flap a bit slower
}
}
function windowResized() {
resizeCanvas(windowWidth, 400);
groundY = height - 50;
if (!gameOver) {
restartGame();
} else {
// Redraw game over screen centered
drawBackground(); // Redraw background
fill(0, 0, 0, 180);
rect(0, 0, width, height);
fill(C_WHITE);
textSize(32);
textAlign(CENTER);
text("GAME OVER", width / 2, height / 2 - 40);
textSize(20);
text(`Score: ${score}`, width / 2, height / 2);
text(`High Score: ${highScore}`, width / 2, height / 2 + 30);
text("Press R to Restart", width / 2, height / 2 + 70);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment