### 1. **Initial Setup**
```javascript
window.onload = function() {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const restartButton = document.getElementById('restartButton');
canvas.width = 400;
canvas.height = 600;
```
- **`window.onload`**: This event handler ensures that the code runs only after the entire page (including images, scripts, etc.) has fully loaded.
- **`canvas` and `ctx`**: The `canvas` element is used for drawing graphics on the screen, and `ctx` is the 2D rendering context that provides methods and properties for drawing.
- **`canvas.width` and `canvas.height`**: Set the canvas dimensions.
- **`restartButton`**: Reference to the restart button element used to restart the game.
### 2. **Game State Variables**
```javascript
let isGameOver = false;
let gameSpeed = 0.5;
let score = 0;
let gravity = 0.2;
let playerVelocityY = 0;
let playerVelocityX = 0;
```
- **`isGameOver`**: Tracks whether the game has ended.
- **`gameSpeed`**: Determines the speed at which blocks scroll. Starts at `0.5`.
- **`score`**: Tracks the player's score.
- **`gravity`**: The force that pulls the player down, simulating gravity.
- **`playerVelocityY`** and **`playerVelocityX`**: Control the player's vertical and horizontal speed.
### 3. **Player Properties**
```javascript
const playerWidth = 30;
const playerHeight = 30;
const playerSpeed = 2;
const jumpStrength = 4;
const maxSpeed = 5;
let playerX, playerY;
```
- **`playerWidth`** and **`playerHeight`**: Size of the player.
- **`playerSpeed`**: Horizontal speed of the player.
- **`jumpStrength`**: How high the player jumps.
- **`maxSpeed`**: Maximum vertical speed due to gravity.
- **`playerX`** and **`playerY`**: The player's current position on the canvas.
### 4. **Player Images**
```javascript
const playerImageRight = new Image();
playerImageRight.src = 'sprite_sheet_R.png';
const playerImageLeft = new Image();
playerImageLeft.src = 'sprite_sheet_L.png';
let currentPlayerImage = playerImageRight;
```
- **Player Sprites**: Load images for the player's right and left-facing sprites.
- **`currentPlayerImage`**: Tracks which sprite to display (left or right).
### 5. **Blocks Properties**
```javascript
const blocks = [];
const blockWidth = 50;
const blockHeight = 15;
const blockSpacing = 200;
```
- **`blocks`**: An array to store block objects.
- **`blockWidth`** and **`blockHeight`**: Size of each block.
- **`blockSpacing`**: Distance between blocks.
### 6. **Image Load Handling**
```javascript
let imagesLoaded = 0;
playerImageRight.onload = playerImageLeft.onload = function() {
imagesLoaded++;
if (imagesLoaded === 2) {
resetGame();
requestAnimationFrame(gameLoop);
}
};
```
- **`imagesLoaded`**: Tracks if both player images have loaded.
- **`onload`**: Once both images are loaded, it calls `resetGame()` to start the game.
### 7. **Reset Game**
```javascript
function resetGame() {
isGameOver = false;
gameSpeed = 0.5;
score = 0;
playerX = canvas.width / 2 - playerWidth / 2;
playerY = canvas.height / 2 - playerHeight / 2;
playerVelocityY = 0;
playerVelocityX = 0;
blocks.length = 0;
generateInitialBlocks();
restartButton.style.display = 'none';
}
```
- Resets game variables, positions the player at the center, and generates the initial blocks.
- Hides the restart button when the game restarts.
### 8. **Generating Blocks**
```javascript
function generateInitialBlocks() {
for (let i = 0; i < 5; i++) {
generateBlock(canvas.height - i * blockSpacing);
}
}
function generateBlock() {
if (blocks.length === 0 || blocks[blocks.length - 1].y > blockSpacing) {
const block = {
x: Math.random() * (canvas.width - blockWidth),
y: -blockHeight,
width: blockWidth,
height: blockHeight,
color: 'blue',
hit: false,
missed: false
};
blocks.push(block);
}
}
```
- **`generateInitialBlocks`**: Creates the initial set of blocks.
- **`generateBlock`**: Generates new blocks at random x-positions and adds them to the `blocks` array.
### 9. **Player Controls**
```javascript
document.addEventListener('keydown', function(event) {
if (event.key === 'ArrowLeft') {
playerVelocityX = -playerSpeed;
currentPlayerImage = playerImageLeft;
} else if (event.key === 'ArrowRight') {
playerVelocityX = playerSpeed;
currentPlayerImage = playerImageRight;
} else if (event.key === 'ArrowUp' || event.key === ' ') {
if (!jumpRequested) {
jump();
jumpRequested = true;
}
}
});
document.addEventListener('keyup', function(event) {
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
playerVelocityX = 0;
} else if (event.key === 'ArrowUp' || event.key === ' ') {
jumpRequested = false;
}
});
```
- **Keydown** and **Keyup Events**: Handle player movement and jumping.
- Arrow keys or spacebar make the player move left, right, or jump.
- **`jumpRequested`**: Ensures the player jumps only once per key press.
### 10. **Touch Controls**
```javascript
let isTouchingLeft = false;
let isTouchingRight = false;
let jumpRequestedTouch = false;
canvas.addEventListener('touchstart', function(e) {
e.preventDefault();
const touch = e.touches[0];
if (touch.clientX < canvas.width / 2) {
isTouchingLeft = true;
} else {
isTouchingRight = true;
}
if (!jumpRequestedTouch) {
jump();
jumpRequestedTouch = true;
}
});
canvas.addEventListener('touchend', function(e) {
isTouchingLeft = false;
isTouchingRight = false;
playerVelocityX = 0;
jumpRequestedTouch = false;
});
```
- Adds support for touch controls on mobile devices.
- Left and right touches control horizontal movement, while tapping makes the player jump.
### 11. **Game Mechanics**
```javascript
function jump() {
playerVelocityY = -jumpStrength;
}
function checkBlockCollision() {
blocks.forEach(block => {
if (
playerX + playerWidth > block.x &&
playerX < block.x + blockWidth &&
playerY + playerHeight > block.y &&
playerY < block.y + blockHeight
) {
playerVelocityY = -jumpStrength;
playerY = block.y - playerHeight;
block.color = 'green';
block.hit = true;
score += 1;
if (gameSpeed < maxSpeed) {
gameSpeed += 0.01;
}
}
});
}
function checkGameOver() {
if (playerY > canvas.height) {
isGameOver = true;
}
blocks.forEach(block => {
if (block.y > canvas.height && !block.hit) {
block.missed = true;
isGameOver = true;
}
});
}
```
- **`jump()`**: Makes the player jump by setting a negative vertical velocity.
- **`checkBlockCollision()`**: Checks if the player collides with any block, allowing the player to jump off it. Increases the score and speed with each block hit.
- **`checkGameOver()`**: Ends the game if the player falls off the screen or misses a block.
### 12. **Game Loop**
```javascript
function gameLoop(timestamp) {
if (isGameOver) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
ctx.font = '30px Arial';
ctx.fillText('Game Over', canvas.width / 2 - 80, canvas.height / 2);
ctx.fillText('Score: ' + score, canvas.width / 2 - 50, canvas.height / 2 + 40);
restartButton.style.display = 'block';
return;
}
updateControls();
playerVelocityY += gravity;
if (playerVelocityY > maxSpeed) playerVelocityY = maxSpeed;
playerY += playerVelocityY;
playerX += playerVelocityX;
if (playerX < 0) playerX = 0;
if (playerX + playerWidth > canvas.width) playerX = canvas.width - playerWidth;
if (playerY < 0) playerY = 0;
blocks.forEach(block => {
block.y += gameSpeed;
});
if (timestamp - lastBlockGenerationTime > blockGenerationInterval) {
generateBlock();
lastBlockGenerationTime = timestamp;
}
if (blocks.length > 0 && blocks[blocks.length - 1].y > canvas.height) {
blocks.shift();
}
checkGameOver();
checkBlockCollision();
ctx.clearRect(0, 0, canvas.width, canvas.height);
blocks.forEach(block => {
ctx.fillStyle = block.color;
ctx.fillRect(block.x, block.y, block.width, block.height);
});
ctx.drawImage(currentPlayerImage, playerX, playerY, playerWidth, playerHeight);
requestAnimationFrame(gameLoop);
}
```
- **Game Loop**:
- Updates the player's position based on velocity and gravity.
- Moves blocks down the screen.
- Checks for collisions and game-over conditions.
- Clears the canvas and redraws everything (blocks and player).
- Calls `requestAnimationFrame` to keep the loop going.
### 13. **Restart Button**
```javascript
restartButton.addEventListener('click', function() {
resetGame();
requestAnimationFrame(gameLoop);
});
};
```
- **Restart Button**: When clicked, the game is reset and restarted.
### Summary of Game Flow
1. **Initialization**: Set up game elements, load images, and wait for player input.
2. **Game Loop**:
- Update player position and velocities.
- Scroll blocks and check for collisions.
- Increase score and speed if the player hits blocks.
- Draw the player and blocks on the canvas.
- End the game if conditions are met (e.g., player falls off the screen).
3. **Player Controls**: Allow player movement through keyboard and touch events.
4. **Restart**: Allow restarting the game after it ends.
### File Structure
```
/project-folder
│
├── index.html
├── style.css
├── script.js
│
└── assets
├── sprites
│ ├── sprite_sheet_R.png
│ └── sprite_sheet_L.png
│
└── other-assets
└── (if you have any other assets like sound effects or additional images)
```
### Explanation of the Structure
- **`index.html`**: The main HTML file that sets up the structure of the webpage.
- **`style.css`**: The CSS file that styles the elements on the webpage, including the canvas and buttons.
- **`script.js`**: The JavaScript file containing all the game logic.
- **`/assets/sprites`**: A folder containing the player sprites (`sprite_sheet_R.png` and `sprite_sheet_L.png`).
- **`/assets/other-assets`**: An optional folder for other game assets, like sounds or additional images.
### Sample File Content
#### 1. `index.html`
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas Game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="gameCanvas"></canvas>
<button id="restartButton">Restart</button>
<script src="script.js"></script>
</body>
</html>
```
- **Canvas**: The game is drawn on this canvas element.
- **Restart Button**: Used to restart the game.
- **CSS and JS**: Linked the stylesheet (`style.css`) and JavaScript (`script.js`).
#### 2. `style.css`
```css
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}
canvas {
border: 2px solid #000;
background-color: #e0e0e0;
}
#restartButton {
display: none; /* Initially hidden */
margin-top: 20px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
```
- **Body Styling**: Centers the canvas on the screen.
- **Canvas Styling**: Adds a border to the canvas.
- **Restart Button**: Initially hidden until the game ends.
#### 3. `script.js`
(The game code you provided goes here.)
#### 4. Sprites (`sprite_sheet_R.png` and `sprite_sheet_L.png`)
- These are image files for the player sprite.
- Place these in the `/assets/sprites` folder.
### Summary of Usage
1. **`index.html`**: Sets up the basic structure and links to `style.css` and `script.js`.
2. **`style.css`**: Handles the layout and appearance of the game canvas and button.
3. **`script.js`**: Contains the game logic.
4. **Sprites**: Images used for player animations, stored in the `assets/sprites` folder.
This file structure organizes the game files in a clear and logical manner, separating HTML, CSS, JavaScript, and assets.