xxxxxxxxxx
519
// ~ D E C L A R E D V A R I A B L E S ~ //
let help = "Press f (possibly twice) to toggle fullscreen";
let fenceImg;
let horseSpriteSheet;
let manSpriteSheet;
let rockImg;
let standingFrames = [];
let runningFrames = [];
let currentFrame = 0;
let frameWidth, frameHeight;
let animationSpeed = 8;
let scaleFactor = 3;
let horseX;
let horseY;
let horseSpeed = 3.5;
let isHorseVisible = true;
let isRunning = false;
let startButton;
let isBackgroundMoving = false;
let movingBackground;
let man;
let isManVisible = false;
let fenceScaleFactor;
let fenceX;
let fenceY;
let fenceSpeed = 2;
let isFenceLeaving = false;
let rocks = []; // Array to hold all rock objects
let totalRocksGenerated = 0; // Track total rocks generated
let canGenerateRock = true; // Cooldown flag for rock generation
let rockTimestamp = 0; // Timestamp of the last rock generation
let gameState = "playing";
let serialInput = ""; // Variable to store serial data
let birdSpriteSheet;
let birds = []; // Array to hold all bird objects
let totalBirdsGenerated = 0; // Track total birds generated
let canGenerateBird = true; // Cooldown flag for bird generation
let sensorActive = false; // Flag to track ultrasonic sensor
let diceGame;
let dice;
let flippedRunningFrames = []; // To store flipped horse frames
let flippedHorseX; // X position of the flipped horse in gameWon state
let flippedHorseY; // Y position of the flipped horse in gameWon state
let horseStopX = 400;
let flippedStandingFrame;
// ~ I M A G E S ~ //
function preload() {
horseSpriteSheet = loadImage("Images/horse.png");
manSpriteSheet = loadImage("Images/man.png");
bgImg = loadImage("Images/background.png");
fenceImg = loadImage("Images/fence.png");
rockImg = loadImage("Images/rock.png");
birdSpriteSheet = loadImage("Images/bird.png");
}
function setup() {
createCanvas(windowWidth, windowHeight);
print(help);
// Initialize moving background
movingBackground = new MovingBackground(bgImg, 2, width, height);
// Calculate the width and height of each sprite
frameWidth = horseSpriteSheet.width / 7;
frameHeight = horseSpriteSheet.height / 3;
// Extract the frames for the standing animation
for (let i = 0; i < 7; i++) {
let x = i * frameWidth;
let y = frameHeight;
standingFrames.push(horseSpriteSheet.get(x, y, frameWidth, frameHeight));
}
// Extract the frames for the running animation
for (let i = 0; i < 7; i++) {
let x = i * frameWidth;
let y = 0;
runningFrames.push(horseSpriteSheet.get(x, y, frameWidth, frameHeight));
}
// Create flipped running frames
for (let i = 0; i < runningFrames.length; i++) {
flippedRunningFrames.push(flipImage(runningFrames[i]));
}
horseX = 1200;
horseY = 700 - frameHeight * scaleFactor;
flippedHorseX = -frameWidth * scaleFactor; // Start off-screen for the gameWon state
flippedHorseY = horseY;
flippedStandingFrame = flipImage(standingFrames[0]);
startButton = createButton("Start");
startButton.position(width / 2 - 50, height / 2);
startButton.size(100, 50);
startButton.mousePressed(startRunning);
// Initialize Man object
man = new Man(manSpriteSheet, 1400, height - 260, width, height);
// Initialize fence properties
fenceScaleFactor = scaleFactor * 0.8;
fenceX = 1525 - frameWidth * fenceScaleFactor;
fenceY = 700 - frameHeight * fenceScaleFactor;
dice = new Dice();
// Setup serial communication
setUpSerial();
}
function draw() {
if (gameState === "diceGame") {
dice.update();
dice.draw();
if (!dice.rolling) {
if (!dice.rollStoppedTime) {
// Record the time when the rolling stopped
dice.rollStoppedTime = millis();
}
// Wait for 4 seconds after displaying the roll result
if (millis() - dice.rollStoppedTime > 4000) {
dice.rollStoppedTime = null; // Reset the timer for the next roll
if (dice.rollValue === 5 || dice.rollValue === 6) {
isBackgroundMoving = false; // Stop the background
man.setStanding(); // Switch the man to standing mode
sendCommandToArduino("stop_motor"); // Send command to stop the motor
gameState = "gameWon"; // Switch to gameWon state
} else {
gameState = "playing"; // Return to playing state for other rolls
}
}
}
return;
}
if (gameState === "gameWon") {
drawGameWonScreen();
return;
}
if (gameState === "gameOver") {
drawGameOverScreen();
return;
}
background(0); // Clear background
// Only move the background if `isBackgroundMoving` is true
if (isBackgroundMoving) {
movingBackground.update(); // Update the background position
movingBackground.draw(); // Draw the moving background
if (!isFenceLeaving) {
isFenceLeaving = true; // Start moving the fence
}
} else {
image(bgImg, 0, 0, width, height); // Draw static background
}
if (isManVisible) {
// Check if the man has reached the stopX position
if (man.manX === man.stopX && !isBackgroundMoving) {
isBackgroundMoving = true; // Start the background scrolling
}
}
if (isHorseVisible) {
if (isRunning) {
image(
runningFrames[currentFrame],
horseX,
horseY,
frameWidth * scaleFactor,
frameHeight * scaleFactor
);
horseX -= horseSpeed;
if (horseX + frameWidth * scaleFactor < 0) {
isHorseVisible = false;
isManVisible = true;
sendCommandToArduino("start_motor");
}
} else {
image(
standingFrames[currentFrame],
horseX,
horseY,
frameWidth * scaleFactor,
frameHeight * scaleFactor
);
}
if (frameCount % animationSpeed === 0) {
currentFrame = (currentFrame + 1) % 7;
}
}
// Update and draw the fence
if (!isFenceLeaving) {
image(
fenceImg,
fenceX,
fenceY,
frameWidth * fenceScaleFactor,
frameHeight * fenceScaleFactor
);
} else {
fenceX += fenceSpeed; // Move fence to the right
if (fenceX > width) {
// Once the fence leaves the screen, stop drawing it
isFenceLeaving = false;
} else {
image(
fenceImg,
fenceX,
fenceY,
frameWidth * fenceScaleFactor,
frameHeight * fenceScaleFactor
);
}
}
// Update and draw all rocks (before the man is drawn, to appear behind)
for (let i = rocks.length - 1; i >= 0; i--) {
let rock = rocks[i];
rock.update();
rock.draw();
if (rock.x > width) {
rocks.splice(i, 1); // Remove rock if it leaves the canvas
}
}
// Update and draw all birds
for (let i = birds.length - 1; i >= 0; i--) {
let bird = birds[i];
bird.update();
bird.draw();
// Check if the bird is at the specified position and the sensor is active
if (
bird.x >= width / 2 - 2 &&
bird.x <= width / 2 + 2 &&
sensorActive
) {
console.log("Bird triggered sensor-based game over");
gameState = "gameOver"; // Trigger game-over state
break; // Exit the loop to avoid multiple triggers
}
if (bird.isOutOfCanvas()) {
birds.splice(i, 1); // Remove bird if it leaves the canvas
}
}
if (isManVisible) {
man.draw(); // Draw the man after the rocks, to appear in front
}
if (!serialActive && !isHorseVisible) {
text("Press Space Bar to select Serial Port", 20, 30);
} else {
text("Connected", 20, 30);
}
// Check for game over condition
let currentTime = millis();
if (
currentTime - rockTimestamp > 4800 && // After the valid jump window
totalRocksGenerated > 0 && // A rock has been generated
!isSafe // Player didn't jump during the valid time window
) {
gameState = "gameOver";
}
}
let isSafe = false; // Track if the player successfully jumped
function drawGameOverScreen() {
background(0); // Black background
textAlign(CENTER, CENTER); // Center align text
textSize(64); // Large font size
fill(255, 0, 0); // Red color for "Game Over"
text("Horse Escaped", width / 2, height / 2); // Display "Game Over" message
textSize(32);
fill(255); // White color for additional message
text("Press F5 to Restart", width / 2, height / 2 + 100); // Restart message
}
function startRunning() {
isRunning = true;
startButton.remove();
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
horseX = 1200;
horseY = 700 - frameHeight * scaleFactor;
if (!isRunning) {
startButton.position(width / 2 - 50, height / 2);
}
movingBackground.resize(width, height);
}
function keyTyped() {
if (key === "f") {
toggleFullscreen();
}
}
function keyPressed() {
if (gameState === "diceGame") {
dice.update();
return; // Do not process other keys during dice game
}
if (key === " ") {
console.log("spacebar key pressed");
setUpSerial();
}
if (key.toLowerCase() === "b" && isBackgroundMoving) {
if (canGenerateBird && totalBirdsGenerated < 10) {
console.log("B key pressed: Generating bird");
birds.push(new Bird(birdSpriteSheet, -50, height / 2, 4));
totalBirdsGenerated++;
canGenerateBird = false;
// Set a timeout to re-enable bird generation after 10 seconds
setTimeout(() => {
canGenerateBird = true;
}, 10000);
} else if (!canGenerateBird) {
console.log("Bird generation is on cooldown.");
} else {
console.log("Maximum number of birds generated.");
}
}
if (key.toLowerCase() === "j") {
console.log("Jump key pressed"); // Log the key press
// Check if jump is pressed between 4.2 and 4.8 seconds after rock generation
let currentTime = millis();
if (currentTime - rockTimestamp >= 4200 && currentTime - rockTimestamp <= 4800) {
console.log("safe");
isSafe = true; // Mark as safe
}
man.jump(); // Trigger jump
}
if (key.toLowerCase() === "r" && isBackgroundMoving) {
if (canGenerateRock && totalRocksGenerated < 10) { // Limit the total number of rocks to 10 with cooldown
console.log("R key pressed: Generating rock");
rocks.push(new Rock(-50, 585, 2));
totalRocksGenerated++;
canGenerateRock = false; // Start cooldown
rockTimestamp = millis(); // Record the time of rock generation
isSafe = false; // Reset the safe status
// Set a timeout to re-enable rock generation after 10 seconds
setTimeout(() => {
canGenerateRock = true;
}, 10000);
} else if (!canGenerateRock) {
console.log("Rock generation is on cooldown.");
} else {
console.log("Maximum number of rocks generated.");
}
}
if (key.toLowerCase() === "d" && isBackgroundMoving) {
dice.startRolling();
gameState = "diceGame";
return;
}
}
function readSerial(data) {
console.log(`Received from Arduino: ${data}`);
serialInput = data.trim();
if (serialInput === "jump") {
man.jump(); // Trigger jump regardless of timing
// Check if the jump happens within the safe window
let currentTime = millis();
if (currentTime - rockTimestamp >= 4200 && currentTime - rockTimestamp <= 4800) {
console.log("Button triggered jump (safe)");
isSafe = true; // Mark as safe if within the correct time window
} else {
console.log("Button jump outside the safe window");
}
}
if (serialInput === "rock") {
// Perform the same logic as the "R" key press
if (isBackgroundMoving && canGenerateRock && totalRocksGenerated < 10) {
console.log("Button triggered rock generation");
rocks.push(new Rock(-50, 585, 2));
totalRocksGenerated++;
canGenerateRock = false; // Start cooldown
rockTimestamp = millis(); // Record the time of rock generation
isSafe = false; // Reset the safe status
// Set a timeout to re-enable rock generation after 10 seconds
setTimeout(() => {
canGenerateRock = true;
}, 10000);
} else if (!canGenerateRock) {
console.log("Rock generation is on cooldown.");
} else {
console.log("Maximum number of rocks generated.");
}
}
if (serialInput === "sensor_detect") {
sensorActive = true; // Sensor is detecting something
console.log("(detecting)");
} else {
sensorActive = false; // Reset sensor flag if no detection
}
if (serialInput === "bird") {
// Perform the same logic as the "B" key press
if (isBackgroundMoving && canGenerateBird && totalBirdsGenerated < 10) {
console.log("Button triggered bird generation");
birds.push(new Bird(birdSpriteSheet, -50, height / 2, 4));
totalBirdsGenerated++;
canGenerateBird = false; // Start cooldown
// Set a timeout to re-enable bird generation after 10 seconds
setTimeout(() => {
canGenerateBird = true;
}, 10000);
} else if (!canGenerateBird) {
console.log("Bird generation is on cooldown.");
} else {
console.log("Maximum number of birds generated.");
}
}
serialInput = ""; // Clear serial input after processing
}
async function sendCommandToArduino(command) {
if (serialActive && writer) {
await writer.write(`${command}\n`);
console.log(`Sent to Arduino: ${command}`);
}
}
function toggleFullscreen() {
let fs = fullscreen();
fullscreen(!fs);
}
function flipImage(img) {
let flipped = createGraphics(img.width, img.height);
flipped.scale(-1, 1); // Flip horizontally
flipped.image(img, -img.width, 0); // Draw the flipped image at negative x
return flipped.get(); // Return the flipped image
}
function drawGameWonScreen() {
background(bgImg); // Static background
man.drawStanding(); // Draw the man's standing frame
if (flippedHorseX < horseStopX) {
// Animate the flipped horse running
image(
flippedRunningFrames[currentFrame],
flippedHorseX,
flippedHorseY,
frameWidth * scaleFactor,
frameHeight * scaleFactor
);
// Update flipped horse position
flippedHorseX += horseSpeed;
// Update animation frame
if (frameCount % animationSpeed === 0) {
currentFrame = (currentFrame + 1) % flippedRunningFrames.length;
}
} else {
// Draw the flipped standing frame when horse reaches stop position
image(
flippedStandingFrame,
flippedHorseX,
flippedHorseY,
frameWidth * scaleFactor,
frameHeight * scaleFactor
);
}
}