const canvas = document.getElementById("canvas1");
const ctx = canvas.getContext("2d");
let gameOver = false;
let velocity = 10;
let angle = 45;
let inertia = 0;
let accelerationY = 9.8;
let justBegan = false;
let projectileRadius = 10;
let projectileX = 0;
let projectileY = canvas.height - projectileRadius;

let speedScale = 0.5;
let angleScale = 0.1;

let randomHeight = Math.random() * (200 - 50) + 50;
let barHeight = randomHeight;
let barX = (Math.random() * (canvas.width - 100)) + 100;
const tolerance = 5;

let points = 0;
let won = false;
let isAnimating = false; // Flag to track animation state
let isLaunched = false; // Flag to track if the ball has been launched
let animationId;
let tries = 0; // Counter for number of tries

window.onload = function () {
  ctx.beginPath();
  ctx.arc(projectileX, projectileY, projectileRadius, 0, Math.PI * 2);
  ctx.fillStyle = "red";
  ctx.fill();
  ctx.closePath();

  ctx.fillStyle = "white";
  ctx.fillRect(barX - 100, barHeight, 100, 25);
};

function resetAllVariables() {
  updateVelocityLabel();
  updateAngleLabel();
  inertia = 0;
  accelerationY = 0.2;
  gameOver = false;
  projectileRadius = 10;
  projectileX = projectileRadius + 5;
  projectileY = canvas.height - projectileRadius;

  speedScale = 0.5;
  angleScale = 0.1;
}

function updateVelocityLabel() {
  velocity = document.getElementById("velocity").value;
  document.getElementById("velocityLabel").innerHTML = "Velocity: " + velocity;
}

function updateAngleLabel() {
  angle = document.getElementById("angle").value;
  document.getElementById("angleLabel").innerHTML = "Angle: " + angle;
}

document
  .getElementById("velocity")
  .addEventListener("input", updateVelocityLabel);
document.getElementById("angle").addEventListener("input", updateAngleLabel);

function getUserGuess() {
  console.log(velocity);
  console.log(angle);
  projectileX = projectileRadius + 5;
  resetAllVariables();
  if (!isAnimating && !isLaunched && velocity > 0) {
    cancelAnimationFrame(animationId); // Cancel any ongoing animation
    isAnimating = true; // Set the isAnimating flag
    tries++; // Increment the number of tries
    drawProjectile(projectileX);
  }
  justBegan = true;
}

function drawProjectile(x) {
  const angleInRadians = (angle * Math.PI) / 180;

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  inertia += accelerationY;
  projectileY += inertia;

  ctx.beginPath();
  ctx.arc(x, projectileY, projectileRadius, 0, Math.PI * 2);
  ctx.fillStyle = "red";
  ctx.fill();
  ctx.closePath();

  ctx.fillStyle = "white";
  ctx.fillRect(barX - 100, barHeight, 100, 25);

  const velocityX = velocity * speedScale * Math.cos(angleInRadians);
  const velocityY = velocity * speedScale * Math.sin(angleInRadians);

  x += velocityX;
  projectileY -= velocityY;

  if (projectileY + projectileRadius >= canvas.height) {
    projectileY = canvas.height - projectileRadius;
    inertia = 0;
    velocity = 0;
    isAnimating = false; // Reset the isAnimating flag
  }
  if (
    x + projectileRadius >= barX - 100 &&
    x - projectileRadius <= barX &&
    projectileY >= barHeight &&
    projectileY <= barHeight + 25
  ) {
    won = true;
    document.getElementById("finishLine").innerHTML = "You Won! :)";
    gameOver = true;
    velocityX = 0;
    velocityY = 0;
  } else if (projectileY + projectileRadius == canvas.height) {
    document.getElementById("finishLine").innerHTML = "You Lost :(";
    gameOver = true;
    won = false;
  }
  justBegan = false;
  if (x + projectileRadius <= canvas.width && !gameOver) {
    animationId = requestAnimationFrame(() => drawProjectile(x));
  } else {
    if (!won) {
      document.getElementById("finishLine").innerHTML = "You Lost :(";
    }
    isAnimating = false; // Reset the isAnimating flag
    isLaunched = false; // Reset the isLaunched flag
    document.getElementById("tries").innerHTML = "Tries: " + tries; // Display the number of tries
  }
}

Let's break it down step-by-step:

  1. Setting up the Canvas and Context:

    • The code starts by selecting the canvas element with the ID "canvas1" and getting its 2D rendering context using getContext("2d").
    • It also initializes various variables such as gameOver, velocity, angle, inertia, accelerationY, justBegan, projectileRadius, projectileX, and projectileY for managing the game state and projectile movement.
  2. Randomizing the Bar:

    • The code generates a random height for a bar within a specific range using Math.random().
    • It assigns the random height to barHeight and sets a random X position for the bar using Math.random().
  3. Drawing Initial Objects:

    • When the window finishes loading (window.onload), the code draws the initial state of the game on the canvas.
    • It uses ctx.beginPath() to start a new drawing path, ctx.arc() to draw the projectile as a red circle at (projectileX, projectileY) with the specified radius, and ctx.fillStyle to set the fill color.
    • The code then uses ctx.fill() to fill the projectile shape and ctx.closePath() to close the drawing path.
    • Additionally, it draws a white rectangle (the bar) using ctx.fillRect().
  4. Updating Velocity and Angle:

    • The code defines two functions, updateVelocityLabel() and updateAngleLabel(), which are triggered when the values of input elements with IDs "velocity" and "angle" change, respectively.
    • These functions retrieve the updated values from the input elements and display them as labels on the webpage.
  5. Handling User Interaction:

    • The code sets up event listeners for the "input" event on the velocity and angle input elements.
    • When the user changes the values, the corresponding functions (updateVelocityLabel() and updateAngleLabel()) are called to update the labels accordingly.
  6. Launching the Projectile:

    • The getUserGuess() function is called when the user takes an action (e.g., clicks a button).
    • It resets various variables and checks conditions to determine if a new projectile should be launched.
    • If the animation is not currently running, the ball has not been launched yet (isLaunched is false), and the velocity is greater than 0, a new animation is initiated by calling drawProjectile(projectileX).
  7. Drawing the Projectile and Animating:

    • The drawProjectile(x) function is responsible for drawing the projectile and animating its movement.
    • It starts by calculating the angle in radians and clearing the entire canvas using ctx.clearRect().
    • The function updates the position of the projectile by modifying the projectileY and projectileX variables based on the current velocity and angle.
    • It then draws the projectile and the bar on the canvas.
    • If the projectile reaches the bottom of the canvas, it stops and sets the isAnimating flag to false.
    • If the projectile collides with the bar, the game is won and relevant variables are updated.
    • If the projectile falls off the canvas, the game is lost.
    • The function recursively calls itself using requestAnimationFrame() to create a smooth animation loop as long as the projectile is within the canvas boundaries and the game is not over.
  8. Resetting the Game and Displaying Results:

    • After the projectile finishes its animation loop, the game's result is displayed based on whether the

    player won or lost.

    • The isAnimating and isLaunched flags are reset.
    • The number of tries is displayed.
    • Finally, the resetAllVariables() function resets various game variables.

Web developers can incorporate the main concepts from this code into their own projects by understanding and utilizing the following concepts:

  • Interacting with the Canvas element and its rendering context (2D or 3D).
  • Drawing shapes and objects on the canvas using methods like arc() and fillRect().
  • Managing game state and variables for tracking objects and their properties.
  • Handling user input events and updating relevant variables or triggering actions accordingly.
  • Implementing animations using recursion and requestAnimationFrame().
  • Displaying game results and resetting game state when needed.

Developers can modify and extend this code to create various types of games or interactive applications that involve canvas-based animations and user interactions.

Certainly! The code provided incorporates some basic physics concepts that can be useful in implementing physics simulations or interactive visualizations in web projects. Here's how a web developer could use the physics concepts from the code in their own projects:

  1. Projectile Motion:

    • The code simulates the motion of a projectile in a 2D space. It calculates the projectile's position based on its velocity and angle, taking into account factors like gravity and inertia.
    • Web developers can use this concept to create simulations involving projectile motion, such as cannonball trajectories, parabolic motion, or even physics-based games.
    • By understanding how the drawProjectile() function updates the projectile's position and incorporates velocity, angle, and gravitational acceleration, developers can adapt these calculations to suit their specific needs.
  2. Collision Detection:

    • The code includes collision detection logic to determine if the projectile collides with the bar object. It checks whether the projectile's position overlaps with the bar's position.
    • Developers can apply this concept to detect collisions between objects in their own projects, such as games or simulations that require collision detection between game characters, obstacles, or other elements.
    • By examining the conditional statements that check for collision conditions in the drawProjectile() function, developers can learn how to implement their own collision detection algorithms based on their specific requirements.
  3. Physics Variables and Formulas:

    • The code uses variables like velocity, angle, inertia, and accelerationY to represent physical quantities. These variables are updated and used to calculate the projectile's motion.
    • Developers can adapt these variables and formulas to create their own physics simulations or visualizations. They can incorporate additional variables to represent other physical properties, such as mass or air resistance, and use appropriate equations to simulate realistic behavior.
    • By studying how the code applies these variables and uses them in calculations, developers can gain insights into how to incorporate physics concepts into their own projects.
  4. Real-Time Animation and Rendering:

    • The code uses the HTML5 Canvas and the requestAnimationFrame() function to create smooth animations and render the updated positions of the objects in real-time.
    • Developers can learn how to create visually appealing and interactive animations by understanding the principles of real-time rendering and animation loops.
    • By analyzing how the drawProjectile() function is called recursively using requestAnimationFrame(), developers can grasp the concept of frame-based animation and apply it to their own projects.

By studying and adapting the physics concepts and implementations from the provided code, web developers can enhance their projects with realistic motion, collision detection, and interactive simulations. They can create educational tools, visualizations, games, or other interactive experiences that involve physics-based interactions and behaviors.

Hacks

  1. Projectile Motion Visualization:

    • Challenge: Modify the code to visualize the projectile motion of the object launched with a given velocity and angle. Implement realistic motion by considering acceleration, inertia, and collision detection.
    • This challenge requires understanding and applying the principles of physics, such as calculating projectile trajectory, accounting for gravitational acceleration, and handling collision detection accurately.
  2. Dynamic Environment with Multiple Obstacles:

    • Challenge: Extend the code to create a dynamic environment with multiple obstacles and barriers. Allow the projectile to interact with the obstacles, such as bouncing off or being affected by their presence.
    • This challenge requires implementing collision detection and response between the projectile and multiple obstacles. It involves designing obstacle placement, managing their properties, and handling the projectile's behavior accordingly.
  3. 2D Platformer Game:

    • Challenge: Utilize the code to build a 2D platformer game where the projectile acts as a player character. Implement platform physics, jumping mechanics, and level design with obstacles, platforms, and collectibles.
    • This challenge combines the principles of projectile motion with platformer game mechanics. It involves designing platform layouts, handling player input for movement and jumping, and creating engaging level designs.
  4. Extra Credit Challenge: Interactive Target Shooting Game:

    • Challenge: Transform the code into an interactive target shooting game. Allow the user to control the launch parameters and aim to hit moving targets. Implement scoring mechanics based on accuracy and speed.
    • This challenge involves incorporating user input to control the projectile's velocity and angle. It requires designing and animating moving targets, calculating the hit accuracy, and providing feedback to the player.