Drawaria.online Bird Game

Adds a detailed parallax bird game to drawaria.online

当前为 2025-02-04 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Drawaria.online Bird Game
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Adds a detailed parallax bird game to drawaria.online
// @author       YouTubeDrawaria
// @match        https://drawaria.online/*
// @grant        none
// @license      MIT
// @icon         https://drawaria.online/avatar/cache/86e33830-86ea-11ec-8553-bff27824cf71.jpg
// ==/UserScript==

(function() {
    'use strict';

    // Create a container for the game
    const gameContainer = document.createElement('div');
    gameContainer.id = 'gameContainer';
    gameContainer.style.position = 'fixed';
    gameContainer.style.top = '0';
    gameContainer.style.left = '0';
    gameContainer.style.width = '100vw';
    gameContainer.style.height = '100vh';
    gameContainer.style.zIndex = '1000';
    gameContainer.style.pointerEvents = 'auto';
    document.body.appendChild(gameContainer);

    // Create the canvas element
    const canvas = document.createElement('canvas');
    canvas.id = 'gameCanvas';
    canvas.width = 800;
    canvas.height = 600;
    gameContainer.appendChild(canvas);

    // Create the game over overlay
    const overlay = document.createElement('div');
    overlay.id = 'gameOverOverlay';
    overlay.style.position = 'absolute';
    overlay.style.top = '0';
    overlay.style.left = '0';
    overlay.style.width = '100%';
    overlay.style.height = '100%';
    overlay.style.background = 'rgba(0, 0, 0, 0.5)';
    overlay.style.color = '#fff';
    overlay.style.display = 'flex';
    overlay.style.flexDirection = 'column';
    overlay.style.justifyContent = 'center';
    overlay.style.alignItems = 'center';
    overlay.style.fontSize = '2rem';
    overlay.style.zIndex = '10';
    overlay.style.visibility = 'hidden';
    gameContainer.appendChild(overlay);

    const gameOverText = document.createElement('div');
    gameOverText.innerText = 'Game Over';
    overlay.appendChild(gameOverText);

    const restartBtn = document.createElement('button');
    restartBtn.id = 'restartBtn';
    restartBtn.innerText = 'Restart';
    restartBtn.style.marginTop = '20px';
    restartBtn.style.padding = '10px 20px';
    restartBtn.style.fontSize = '1rem';
    restartBtn.style.cursor = 'pointer';
    restartBtn.style.border = 'none';
    restartBtn.style.borderRadius = '5px';
    restartBtn.style.backgroundColor = '#ff5050';
    restartBtn.style.color = '#fff';
    overlay.appendChild(restartBtn);

    const ctx = canvas.getContext('2d');

    // Game Variables
    let bird, obstacles, frameCount, gameOver;
    let gravity, jumpForce, obstacleSpeed, obstacleGap, obstacleWidth, obstacleInterval;

    // Parallax background variables
    let mountainPoints = [];
    let hillsPoints = [];
    let mountainOffset = 0;
    let hillsOffset = 0;
    let clouds = [];

    // Speeds for parallax layers (relative multipliers)
    const mountainSpeedMultiplier = 0.2;
    const hillsSpeedMultiplier = 0.5;
    const cloudSpeedMultiplier = 0.3;

    // Utility to generate terrain points for mountains/hills
    function generateTerrain(totalWidth, step, base, variation) {
      const points = [];
      for (let x = -step; x <= totalWidth; x += step) {
        const y = base + Math.random() * variation - variation / 2;
        points.push({ x, y });
      }
      return points;
    }

    // Initialize clouds array
    function initClouds() {
      clouds = [];
      const cloudCount = 8;
      for (let i = 0; i < cloudCount; i++) {
        clouds.push({
          x: Math.random() * canvas.width,
          y: Math.random() * canvas.height * 0.3,
          scale: Math.random() * 0.5 + 0.5
        });
      }
    }

    function init() {
      // Bird properties
      bird = {
        x: 100,
        y: canvas.height / 2,
        width: 30,
        height: 30,
        velocity: 0
      };

      obstacles = [];
      frameCount = 0;
      gameOver = false;
      gravity = 0.5;
      jumpForce = -9;
      obstacleSpeed = 3;
      obstacleGap = 150;
      obstacleWidth = 60;
      obstacleInterval = 90; // frames between obstacles

      // Initialize parallax backgrounds
      mountainPoints = generateTerrain(canvas.width * 2, 50, canvas.height * 0.4, 80);
      hillsPoints = generateTerrain(canvas.width * 2, 50, canvas.height * 0.75, 40);
      mountainOffset = 0;
      hillsOffset = 0;
      initClouds();

      overlay.style.visibility = 'hidden';
    }

    function resetGame() {
      init();
      loop();
    }

    // Event listeners for controls
    document.addEventListener('keydown', function(e) {
      if (e.code === 'Space') {
        e.preventDefault();
        if (gameOver) {
          resetGame();
        } else {
          bird.velocity = jumpForce;
        }
      }
    });

    canvas.addEventListener('mousedown', function() {
      if (!gameOver) {
        bird.velocity = jumpForce;
      }
    });

    restartBtn.addEventListener('click', resetGame);

    // Spawn an obstacle (pipe pair)
    function spawnObstacle() {
      const minGapY = 50;
      const maxGapY = canvas.height - obstacleGap - 50;
      const gapY = Math.floor(Math.random() * (maxGapY - minGapY + 1)) + minGapY;
      obstacles.push({ x: canvas.width, gapY });
    }

    // Update game objects
    function update() {
      // Update bird
      bird.velocity += gravity;
      bird.y += bird.velocity;

      // Spawn obstacles at intervals
      if (frameCount % obstacleInterval === 0) {
        spawnObstacle();
      }

      // Update obstacles positions
      obstacles.forEach(obstacle => {
        obstacle.x -= obstacleSpeed;
      });
      obstacles = obstacles.filter(obstacle => obstacle.x + obstacleWidth > 0);

      // Check collision with top/bottom
      if (bird.y < 0 || bird.y + bird.height > canvas.height) {
        gameOver = true;
      }

      // Collision with obstacles
      obstacles.forEach(obstacle => {
        if (
          bird.x + bird.width > obstacle.x &&
          bird.x < obstacle.x + obstacleWidth
        ) {
          if (bird.y < obstacle.gapY || bird.y + bird.height > obstacle.gapY + obstacleGap) {
            gameOver = true;
          }
        }
      });

      // Update parallax backgrounds
      mountainOffset = (mountainOffset + obstacleSpeed * mountainSpeedMultiplier) % canvas.width;
      hillsOffset = (hillsOffset + obstacleSpeed * hillsSpeedMultiplier) % canvas.width;
      clouds.forEach(cloud => {
        cloud.x -= obstacleSpeed * cloudSpeedMultiplier;
        if (cloud.x + 100 * cloud.scale < 0) { // if cloud moves off the left edge
          cloud.x = canvas.width + Math.random() * canvas.width;
          cloud.y = Math.random() * canvas.height * 0.3;
        }
      });

      frameCount++;
    }

    // Draw sky background gradient on canvas
    function drawSky() {
      const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
      gradient.addColorStop(0, '#87CEEB');
      gradient.addColorStop(1, '#fff');
      ctx.fillStyle = gradient;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // Draw clouds with soft edges
    function drawCloud(x, y, scale) {
      ctx.save();
      ctx.translate(x, y);
      ctx.scale(scale, scale);
      ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
      ctx.beginPath();
      ctx.arc(0, 0, 20, Math.PI * 0.5, Math.PI * 1.5);
      ctx.arc(25, -10, 25, Math.PI * 1, Math.PI * 1.85);
      ctx.arc(55, 0, 20, Math.PI * 1.37, Math.PI * 1.91);
      ctx.arc(40, 10, 25, Math.PI * 1.5, Math.PI * 0.5);
      ctx.arc(15, 10, 20, Math.PI * 1.15, Math.PI * 1.9);
      ctx.closePath();
      ctx.fill();
      ctx.restore();
    }

    // Draw all clouds
    function drawClouds() {
      clouds.forEach(cloud => {
        drawCloud(cloud.x, cloud.y, cloud.scale);
      });
    }

    // Draw mountain range based on pre-generated points
    function drawMountains(offset) {
      ctx.save();
      ctx.fillStyle = '#708090'; // Slate gray for distant mountains
      ctx.beginPath();
      ctx.moveTo(-offset, canvas.height);
      mountainPoints.forEach(pt => {
        ctx.lineTo(pt.x - offset, pt.y);
      });
      ctx.lineTo(canvas.width + 100, canvas.height);
      ctx.closePath();
      ctx.fill();
      ctx.restore();
    }

    // Draw hills in the foreground
    function drawHills(offset) {
      ctx.save();
      ctx.fillStyle = '#228B22'; // Forest green for hills
      ctx.beginPath();
      ctx.moveTo(-offset, canvas.height);
      hillsPoints.forEach(pt => {
        ctx.lineTo(pt.x - offset, pt.y);
      });
      ctx.lineTo(canvas.width + 100, canvas.height);
      ctx.closePath();
      ctx.fill();
      ctx.restore();
    }

    // Draw obstacles (pipes)
    function drawObstacles() {
      ctx.fillStyle = '#006400'; // Dark green for pipes
      obstacles.forEach(obstacle => {
        // Upper pipe
        ctx.fillRect(obstacle.x, 0, obstacleWidth, obstacle.gapY);
        // Lower pipe
        ctx.fillRect(obstacle.x, obstacle.gapY + obstacleGap, obstacleWidth, canvas.height - obstacle.gapY - obstacleGap);
      });
    }

    // Draw the bird with slight rotation based on velocity
    function drawBird() {
      ctx.save();
      // Determine rotation angle (capped for visual effect)
      const angle = Math.min(Math.max(bird.velocity / 10, -0.5), 1);
      ctx.translate(bird.x + bird.width / 2, bird.y + bird.height / 2);
      ctx.rotate(angle);

      // Draw bird body (a yellow circle with a red beak)
      ctx.fillStyle = '#FFD700';
      ctx.beginPath();
      ctx.arc(0, 0, bird.width / 2, 0, Math.PI * 2);
      ctx.fill();

      // Draw eye
      ctx.fillStyle = '#000';
      ctx.beginPath();
      ctx.arc(-5, -5, 3, 0, Math.PI * 2);
      ctx.fill();

      // Draw beak
      ctx.fillStyle = '#FF4500';
      ctx.beginPath();
      ctx.moveTo(bird.width / 2, 0);
      ctx.lineTo(bird.width / 2 + 10, -5);
      ctx.lineTo(bird.width / 2 + 10, 5);
      ctx.closePath();
      ctx.fill();
      ctx.restore();
    }

    function draw() {
      // Draw sky background gradient
      drawSky();
      // Draw parallax layers in order
      drawClouds();
      drawMountains(mountainOffset);
      drawHills(hillsOffset);
      // Draw obstacles and bird
      drawObstacles();
      drawBird();
    }

    function loop() {
      if (gameOver) {
        overlay.style.visibility = 'visible';
        return;
      }
      update();
      draw();
      requestAnimationFrame(loop);
    }

    // Start the game
    init();
    loop();
})();