Skip to content

Instantly share code, notes, and snippets.

@adhishthite
Created March 26, 2025 05:53
Show Gist options
  • Select an option

  • Save adhishthite/4553af43793d340b40bf51c1e65c6bea to your computer and use it in GitHub Desktop.

Select an option

Save adhishthite/4553af43793d340b40bf51c1e65c6bea to your computer and use it in GitHub Desktop.
Dino Game: Using p5.js, developed by Gemini 2.5 Pro!
// 🎮 Game vibe-coded by Adhish Thite <adhish.thite@gmail.com>
// 🤖 Entirely generated using Gemini 2.5 (Experimental) on March 26, 2025
// 🚀 Paste this code into https://editor.p5js.org to play the game and enjoy the vibe!
// ==================================
// == GAME CONFIGURATION ==
// ==================================
const INITIAL_GAME_SPEED = 6;
const GAME_SPEED_INCREMENT = 0.003;
const GRAVITY = 0.7;
const JUMP_FORCE = -16;
const SCORE_DIVISOR = 5; // Divide raw score by this for display
// Spawning
const MIN_OBSTACLE_DISTANCE = 90; // Min horizontal pixels between obstacles
const BASE_OBSTACLE_SPAWN_RATE = 0.015; // Base chance per frame (increases with speed)
const CLOUD_SPAWN_RATE = 0.005;
const GROUND_DETAIL_SPAWN_RATE = 0.1;
const NUM_STARS = 100; // Number of stars in the night sky
// Day/Night Cycle
const CYCLE_SCORE_LENGTH = 1000; // Internal score points for one phase (day or night)
const TRANSITION_SCORE_DURATION = 200; // Internal score points for transition
// Dino Visuals
const DINO_X_POS = 80;
const DINO_WIDTH = 44;
const DINO_HEIGHT = 47;
const DINO_ANIM_SPEED = 5; // Lower is faster animation frame rate
// Colors - Day
const DAY_SKY_COLOR = [135, 206, 250];
const DAY_GROUND_COLOR = [210, 180, 140];
const DAY_SUN_COLOR = [255, 255, 0];
const DAY_TEXT_COLOR = [50];
const DAY_CLOUD_COLOR = [255, 255, 255, 200];
const DAY_DETAIL_COLOR = [160, 120, 90, 150];
// Colors - Night
const NIGHT_SKY_COLOR = [25, 25, 112];
const NIGHT_GROUND_COLOR = [88, 62, 40];
const NIGHT_MOON_COLOR = [240, 240, 240];
const NIGHT_TEXT_COLOR = [230];
const NIGHT_CLOUD_COLOR = [180, 180, 180, 150];
const NIGHT_DETAIL_COLOR = [60, 40, 20, 150];
const STAR_COLOR = [255, 255, 255, 180];
// Other Visuals
const SUN_MOON_SIZE = 50;
// ==================================
// == GLOBAL VARIABLES ==
// ==================================
let dino;
let obstacles = [];
let clouds = [];
let groundDetails = [];
let stars = [];
let score = 0;
let gameSpeed; // Initialized in resetGame
let groundY;
let gameState = 'initial'; // 'initial', 'running', 'paused', 'gameOver'
// Day/Night Cycle State
let isDay;
let isTransitioning;
let transitionProgress;
let targetIsDay;
// Current Dynamic Colors
let currentSkyColor, currentGroundColor, currentSunMoonColor, currentTextColor, currentCloudColor, currentDetailColor;
// Sun/Moon Position
let sunMoonX, sunMoonY;
// Screen Shake
let shakeDuration = 0;
let shakeIntensity = 3;
// ==================================
// == P5.JS FUNCTIONS ==
// ==================================
function setup() {
// Use 90% of window width, fixed height
createCanvas(windowWidth * 0.9, 350);
// Calculate ground level based on canvas height
groundY = height - 50;
// Set text defaults
textAlign(LEFT);
textSize(18);
textFont('monospace');
// Use CORNER mode for easier positioning with top-left coordinates
rectMode(CORNER);
// Initial game setup
resetGame(); // Call resetGame to initialize all states and objects
noLoop(); // Start paused, waiting for user input
}
function draw() {
// --- Update Day/Night Cycle colors if running ---
if (gameState === 'running') {
updateCycle();
}
// --- Apply Screen Shake (if active) ---
if (shakeDuration > 0) {
translate(random(-shakeIntensity, shakeIntensity), random(-shakeIntensity, shakeIntensity));
shakeDuration--;
}
// --- Draw Background ---
background(currentSkyColor);
// --- Draw Stars (if appropriate) ---
// Draw if fully night, or during any transition phase (alpha handles visibility)
if (!isDay || isTransitioning) {
drawStars();
}
// --- Draw Sun/Moon ---
fill(currentSunMoonColor);
noStroke();
ellipse(sunMoonX, sunMoonY, SUN_MOON_SIZE, SUN_MOON_SIZE);
// --- Draw Clouds ---
if (gameState === 'running' || gameState === 'paused' || gameState === 'gameOver') {
trySpawnCloud(); // Continue spawning clouds even if paused/game over for visual effect
}
for (let i = clouds.length - 1; i >= 0; i--) {
// Clouds keep moving slowly even if paused for parallax effect
let cloudSpeed = (gameState === 'running') ? gameSpeed : INITIAL_GAME_SPEED * 0.5; // Slower when paused
clouds[i].move(cloudSpeed);
clouds[i].show(currentCloudColor);
if (clouds[i].isOffscreen()) {
clouds.splice(i, 1);
}
}
// --- Draw Ground ---
fill(currentGroundColor);
noStroke();
rect(0, groundY, width, height - groundY);
// --- Draw Ground Details ---
if (gameState === 'running' || gameState === 'paused' || gameState === 'gameOver') {
trySpawnGroundDetail(); // Keep spawning details
}
for (let i = groundDetails.length - 1; i >= 0; i--) {
// Ground details keep moving even if paused
let detailSpeed = (gameState === 'running') ? gameSpeed : INITIAL_GAME_SPEED;
groundDetails[i].move(detailSpeed);
groundDetails[i].show(currentDetailColor);
if (groundDetails[i].isOffscreen()) {
groundDetails.splice(i, 1);
}
}
// --- Game Logic ---
if (gameState === 'running') {
// Update score & speed
score++;
gameSpeed += GAME_SPEED_INCREMENT;
let currentObstacleSpawnRate = BASE_OBSTACLE_SPAWN_RATE * (gameSpeed / INITIAL_GAME_SPEED);
// Handle Dino
dino.applyGravity();
dino.update();
dino.show();
// Handle Obstacles
trySpawnObstacle(currentObstacleSpawnRate);
for (let i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].move(gameSpeed);
obstacles[i].show();
// Check Collision
if (dino.collides(obstacles[i])) {
gameOver(); // Trigger game over sequence
break; // Stop checking collisions after the first one
}
// Remove offscreen obstacles
if (obstacles[i].isOffscreen()) {
obstacles.splice(i, 1);
}
}
// Display Score
drawScore();
} else {
// Draw Dino/Obstacles in their last position if paused or game over
if (dino) dino.show();
obstacles.forEach(obs => obs.show());
// Handle specific overlays/text for non-running states
if (gameState === 'initial') {
drawOverlayMessage("Press SPACE or Click/Tap to Start");
} else if (gameState === 'paused') {
drawOverlayMessage("PAUSED", "Press P to Resume");
drawScore(); // Still show score when paused
} else if (gameState === 'gameOver') {
drawOverlayMessage("GAME OVER", `Final Score: ${floor(score / SCORE_DIVISOR)}\nPress SPACE or Click/Tap to Restart`);
}
}
}
// ==================================
// == INPUT HANDLING & STATE ==
// ==================================
function keyPressed() {
// --- Pause/Resume ---
if ((key === 'p' || key === 'P') && (gameState === 'running' || gameState === 'paused')) {
togglePause();
return false; // Prevent default behavior
}
// --- Start / Jump / Restart ---
if (key === ' ') {
handlePrimaryAction();
return false; // Prevent space bar scrolling page
}
}
function mousePressed() {
handlePrimaryAction();
}
// Handle touch events for mobile
function touchStarted() {
handlePrimaryAction();
return false; // Prevent touch causing clicks/scrolling etc.
}
function handlePrimaryAction() {
if (gameState === 'initial') {
startGame();
} else if (gameState === 'running') {
if (dino.isOnGround()) {
dino.jump();
// Optional: Add jump sound effect here
}
} else if (gameState === 'gameOver') {
resetGame();
startGame(); // Directly start after reset
}
// No action if paused (except unpausing with 'P')
}
function togglePause() {
if (gameState === 'running') {
gameState = 'paused';
noLoop(); // Stop draw loop updates (except for manual redraws if needed)
redraw(); // Redraw once to show pause message
} else if (gameState === 'paused') {
gameState = 'running';
loop(); // Resume draw loop
}
}
function startGame() {
if(gameState === 'initial' || gameState === 'gameOver') {
gameState = 'running';
loop(); // Start the draw loop if it wasn't running
}
}
function gameOver() {
gameState = 'gameOver';
shakeDuration = 15; // Activate screen shake for 15 frames
// Optional: Play game over sound
noLoop(); // Stop updates
redraw(); // Draw the final game over screen
}
// ==================================
// == GAME RESET ==
// ==================================
function resetGame() {
// Reset scores and speed
score = 0;
gameSpeed = INITIAL_GAME_SPEED;
// Clear dynamic objects
obstacles = [];
clouds = [];
groundDetails = [];
// Reset Day/Night cycle to initial Day state
isDay = true;
targetIsDay = true;
isTransitioning = false;
transitionProgress = 0;
currentSkyColor = color(...DAY_SKY_COLOR);
currentGroundColor = color(...DAY_GROUND_COLOR);
currentSunMoonColor = color(...DAY_SUN_COLOR);
currentTextColor = color(...DAY_TEXT_COLOR);
currentCloudColor = color(...DAY_CLOUD_COLOR);
currentDetailColor = color(...DAY_DETAIL_COLOR);
// Sun/Moon position
sunMoonX = width - 80;
sunMoonY = 70;
// Create/Reset Dino
dino = new Dino(DINO_X_POS, groundY - DINO_HEIGHT, DINO_WIDTH, DINO_HEIGHT);
// Initial spawn for visual elements
stars = []; generateStars(NUM_STARS);
for (let i = 0; i < 5; i++) { trySpawnCloud(); }
for (let i = 0; i < 10; i++) { trySpawnGroundDetail(); }
// Reset shake effect
shakeDuration = 0;
// Set initial state (will wait for user input)
gameState = 'initial';
// Ensure p5 loop is stopped until user starts
noLoop();
// Draw the initial screen once
redraw();
}
// ==================================
// == DRAWING HELPER FUNCTIONS ==
// ==================================
function drawScore() {
fill(currentTextColor);
noStroke();
textSize(18);
textAlign(LEFT);
text(`Score: ${floor(score / SCORE_DIVISOR)}`, 20, 30);
}
function drawOverlayMessage(line1, line2 = "") {
// Semi-transparent overlay - adjust alpha based on day/night
let overlayAlpha = isDay ? 100 : 80;
fill(0, 0, 0, overlayAlpha);
rect(0,0,width, height);
// Draw text lines
fill(currentTextColor);
stroke(isDay ? 0 : 255); // Contrast stroke
strokeWeight(1);
textAlign(CENTER, CENTER);
textSize(30); // Larger primary message
text(line1, width / 2, height / 2 - (line2 ? 20 : 0)); // Adjust position if two lines
if (line2) {
textSize(16); // Smaller secondary message
noStroke();
text(line2, width / 2, height / 2 + 20);
}
textAlign(LEFT); // Reset alignment
}
function drawStars() {
push();
let baseAlpha = STAR_COLOR[3] || 255; // Get alpha from definition or default to 255
let currentAlpha = baseAlpha;
if (isTransitioning) {
// Fade stars based on transition direction and progress
currentAlpha = targetIsDay ? baseAlpha * (1 - transitionProgress) : baseAlpha * transitionProgress;
} else if (isDay) {
currentAlpha = 0; // Explicitly no stars during full day
}
// Only draw if alpha is significant
if (currentAlpha > 5) {
fill(STAR_COLOR[0], STAR_COLOR[1], STAR_COLOR[2], currentAlpha);
noStroke();
for (let star of stars) {
ellipse(star.x, star.y, star.size, star.size);
}
}
pop();
}
// ==================================
// == DAY/NIGHT CYCLE LOGIC ==
// ==================================
function updateCycle() {
let phase = score % (CYCLE_SCORE_LENGTH * 2);
targetIsDay = (phase < CYCLE_SCORE_LENGTH);
if (!isTransitioning && targetIsDay !== isDay) {
isTransitioning = true;
transitionProgress = 0;
}
if (isTransitioning) {
// Increment progress - somewhat tied to score speed but capped
// Use a small fixed increment for smoother visual transition regardless of game speed jumps
transitionProgress += (1 / (TRANSITION_SCORE_DURATION * 0.1)); // Adjust denominator for transition speed
transitionProgress = min(transitionProgress, 1); // Clamp progress to 1
let fromSky, toSky, fromGround, toGround, fromSunMoon, toSunMoon, fromText, toText, fromCloud, toCloud, fromDetail, toDetail;
if (targetIsDay) { // To Day
fromSky = color(...NIGHT_SKY_COLOR); toSky = color(...DAY_SKY_COLOR);
fromGround = color(...NIGHT_GROUND_COLOR); toGround = color(...DAY_GROUND_COLOR);
fromSunMoon = color(...NIGHT_MOON_COLOR); toSunMoon = color(...DAY_SUN_COLOR);
fromText = color(...NIGHT_TEXT_COLOR); toText = color(...DAY_TEXT_COLOR);
fromCloud = color(...NIGHT_CLOUD_COLOR); toCloud = color(...DAY_CLOUD_COLOR);
fromDetail = color(...NIGHT_DETAIL_COLOR); toDetail = color(...DAY_DETAIL_COLOR);
} else { // To Night
fromSky = color(...DAY_SKY_COLOR); toSky = color(...NIGHT_SKY_COLOR);
fromGround = color(...DAY_GROUND_COLOR); toGround = color(...NIGHT_GROUND_COLOR);
fromSunMoon = color(...DAY_SUN_COLOR); toSunMoon = color(...NIGHT_MOON_COLOR);
fromText = color(...DAY_TEXT_COLOR); toText = color(...NIGHT_TEXT_COLOR);
fromCloud = color(...DAY_CLOUD_COLOR); toCloud = color(...NIGHT_CLOUD_COLOR);
fromDetail = color(...DAY_DETAIL_COLOR); toDetail = color(...NIGHT_DETAIL_COLOR);
}
// Lerp all colors
currentSkyColor = lerpColor(fromSky, toSky, transitionProgress);
currentGroundColor = lerpColor(fromGround, toGround, transitionProgress);
currentSunMoonColor = lerpColor(fromSunMoon, toSunMoon, transitionProgress);
currentTextColor = lerpColor(fromText, toText, transitionProgress);
currentCloudColor = lerpColor(fromCloud, toCloud, transitionProgress);
currentDetailColor = lerpColor(fromDetail, toDetail, transitionProgress);
// Check if transition ended
if (transitionProgress === 1) {
isTransitioning = false;
isDay = targetIsDay;
// Snap to final colors to avoid floating point inaccuracies
currentSkyColor = isDay ? color(...DAY_SKY_COLOR) : color(...NIGHT_SKY_COLOR);
currentGroundColor = isDay ? color(...DAY_GROUND_COLOR) : color(...NIGHT_GROUND_COLOR);
currentSunMoonColor = isDay ? color(...DAY_SUN_COLOR) : color(...NIGHT_MOON_COLOR);
currentTextColor = isDay ? color(...DAY_TEXT_COLOR) : color(...NIGHT_TEXT_COLOR);
currentCloudColor = isDay ? color(...DAY_CLOUD_COLOR) : color(...NIGHT_CLOUD_COLOR);
currentDetailColor = isDay ? color(...DAY_DETAIL_COLOR) : color(...NIGHT_DETAIL_COLOR);
}
}
// If not transitioning, colors are already correct (set at end of last transition or in reset)
}
function generateStars(numStars) {
stars = [];
for (let i = 0; i < numStars; i++) {
stars.push({
x: random(width),
y: random(groundY * 0.7), // Keep stars above ground level
size: random(1, 2.5) // Smaller size variation
});
}
}
// ==================================
// == SPAWNING FUNCTIONS ==
// ==================================
// (These remain largely the same, but ensure they only run when appropriate)
function trySpawnObstacle(spawnRate) {
// Only spawn if running
if (gameState !== 'running') return;
if (random(1) < spawnRate) {
let lastObstacle = obstacles[obstacles.length - 1];
// Dynamic required distance based on game speed slightly
let speedFactor = map(gameSpeed, INITIAL_GAME_SPEED, INITIAL_GAME_SPEED * 3, 1, 1.5, true); // Increase spacing slightly at higher speeds
let requiredDist = (MIN_OBSTACLE_DISTANCE + random(0, width * 0.3)) * speedFactor + (lastObstacle ? lastObstacle.w : 0);
if (!lastObstacle || (width - lastObstacle.x > requiredDist)) {
obstacles.push(new Obstacle());
}
}
}
function trySpawnCloud() {
// Clouds can spawn visually even if paused/game over
if (random(1) < CLOUD_SPAWN_RATE) {
let lastCloud = clouds[clouds.length - 1];
if (!lastCloud || (width - lastCloud.x > 150 + random(0, 100))) { // Slightly more random cloud spacing
clouds.push(new Cloud());
}
}
}
function trySpawnGroundDetail() {
// Details can spawn visually even if paused/game over
if (random(1) < GROUND_DETAIL_SPAWN_RATE) {
let lastDetail = groundDetails[groundDetails.length - 1];
if (!lastDetail || (width - lastDetail.x > 5 + random(0,25))) { // Slightly more random detail spacing
groundDetails.push(new GroundDetail());
}
}
}
// ==================================
// == GAME CLASSES ==
// ==================================
// (Classes remain largely the same, ensure methods use constants)
class Dino {
constructor(x, y, w, h) {
this.baseY = y; this.x = x; this.y = y; this.w = w; this.h = h;
this.velocityY = 0; this.legToggle = false; this.animFrameCounter = 0;
}
jump() { this.velocityY = JUMP_FORCE; }
applyGravity() {
this.velocityY += GRAVITY; this.y += this.velocityY;
if (this.y > this.baseY) { this.y = this.baseY; this.velocityY = 0; }
}
isOnGround() { return this.y === this.baseY; }
update() {
if (this.isOnGround()) {
this.animFrameCounter++;
if (this.animFrameCounter >= DINO_ANIM_SPEED) { this.legToggle = !this.legToggle; this.animFrameCounter = 0; }
} else { this.legToggle = false; } // Keep legs consistent mid-air
}
collides(obstacle) { // Simple AABB collision
let dinoHitbox = { x: this.x + 5, y: this.y, w: this.w - 15, h: this.h - 5 };
let obsHitbox = obstacle.getHitbox(); // Use a method to get obstacle hitbox
return ( dinoHitbox.x < obsHitbox.x + obsHitbox.w && dinoHitbox.x + dinoHitbox.w > obsHitbox.x && dinoHitbox.y < obsHitbox.y + obsHitbox.h && dinoHitbox.y + dinoHitbox.h > obsHitbox.y );
}
reset() { this.y = this.baseY; this.velocityY = 0; this.legToggle = false; this.animFrameCounter = 0;}
show() { // Pixelated dino drawing
push(); translate(this.x, this.y); fill(80); noStroke();
rect(10, 0, 24, 30); rect(0, 8, 10, 12); rect(28, -7, 16, 18); rect(24, 5, 8, 8);
fill(255); rect(36, -2, 3, 3); // Eye
fill(80);
if (this.legToggle && this.isOnGround()) { rect(12, 30, 8, 12); rect(24, 30, 8, 8); } // Running legs
else { rect(12, 30, 8, 17); rect(24, 30, 8, 17); } // Standing/Jumping legs
pop();
}
}
class Obstacle {
constructor() { // Tree obstacle
this.type = random(1) > 0.6 ? 'tall' : 'bushy';
if (this.type === 'tall') { this.trunkW = random(15, 25); this.trunkH = random(40, 80); this.foliageH = random(20, 40); this.foliageW = this.trunkW * random(1.8, 2.5); }
else { this.trunkW = random(25, 40); this.trunkH = random(20, 40); this.foliageH = random(30, 50); this.foliageW = this.trunkW * random(1.2, 1.8); }
this.h = this.trunkH + this.foliageH / 2; this.w = this.foliageW;
this.x = width; this.y = groundY - this.trunkH; // Place trunk base on ground
}
move(speed) { this.x -= speed; }
isOffscreen() { return this.x < -this.foliageW; } // Check against foliage width
getHitbox() { // Provide hitbox relative to current position
let xOffset = 0, yOffset = 0, boxW = 0, boxH = 0;
yOffset = (this.type === 'tall' ? -this.foliageH / 1.5 : -this.foliageH / 2);
boxW = this.trunkW; boxH = this.trunkH + this.foliageH;
if (this.foliageW > this.trunkW) { boxW = this.foliageW * 0.8; xOffset = -(this.foliageW - this.trunkW) / 2;}
return { x: this.x + xOffset, y: this.y + yOffset, w: boxW, h: boxH };
}
show() { // Draws the tree
push(); translate(this.x, this.y); noStroke();
fill(139, 69, 19); rect(0, 0, this.trunkW, this.trunkH); // Trunk
fill(34, 139, 34); // Foliage
if (this.type === 'tall') { ellipse(this.trunkW / 2, -this.foliageH / 2, this.foliageW, this.foliageH); }
else { ellipse(this.trunkW / 2, -this.foliageH / 3, this.foliageW, this.foliageH * 0.8); ellipse(this.trunkW / 2, 0, this.foliageW * 0.8, this.foliageH * 0.6); }
pop();
}
}
class Cloud {
constructor() { this.w = random(50, 100); this.h = random(20, 40); this.x = width + random(0, width * 0.5); this.y = random(height * 0.1, height * 0.4); this.speedMultiplier = random(0.2, 0.6); }
move(baseSpeed) { this.x -= baseSpeed * this.speedMultiplier; } // Slower parallax speed
isOffscreen() { return this.x < -this.w; }
show(cloudCol) { // Draws cloud using overlapping ellipses
fill(cloudCol); noStroke();
ellipse(this.x + this.w / 2, this.y + this.h / 2, this.w, this.h);
ellipse(this.x + this.w * 0.2, this.y + this.h * 0.7, this.w * 0.6, this.h * 0.8);
ellipse(this.x + this.w * 0.8, this.y + this.h * 0.8, this.w * 0.7, this.h * 0.6);
}
}
class GroundDetail {
constructor() { this.x = width + random(0, 50); this.y = groundY + random(5, 15); this.w = random(10, 30); this.h = random(1, 3); }
move(speed) { this.x -= speed; }
isOffscreen() { return this.x < -this.w; }
show(detailCol) { // Draws small ground detail rectangle
fill(detailCol); noStroke(); rect(this.x, this.y, this.w, this.h);
}
}
// ==================================
// == WINDOW RESIZE ==
// ==================================
function windowResized() {
print("Window resized. Resetting game."); // Log to console
resizeCanvas(windowWidth * 0.9, 350);
groundY = height - 50;
resetGame(); // Force reset to correctly scale/position elements relative to new size
// Note: This means resizing during gameplay restarts the game.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment