Skip to content

Commit

Permalink
Merge pull request #1106 from its-kritika/main
Browse files Browse the repository at this point in the history
New Game, closes #1104
  • Loading branch information
Durgesh4993 authored Aug 7, 2024
2 parents 6bda261 + 6ae1fa6 commit 5b7472b
Show file tree
Hide file tree
Showing 9 changed files with 420 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ ________________________________________________________________________________
| 205 | [Breakout_Game](.SinglePlayer%20-%20Games/Maze_Game) |
| 206 | [Bomber_Game](.SinglePlayer%20-%20Games/Bomber_Game) |
| 207 | [Shape_Clicker_Game](.SinglePlayer%20-%20Games/Shape_Clicker_Game) |
| 208 | [Arkanoid_Game](.SinglePlayer%20-%20Games/Arkanoid_Game) |

</div>

Expand Down
29 changes: 29 additions & 0 deletions SinglePlayer - Games/Arkanoid_Game/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Arkanoid Game
Welcome to the game! This is a classic brick-breaking game inspired by the popular game Arkanoid. The objective is to break all the bricks on the screen using a ball while preventing the ball from falling out of the play area with the help of a paddle.

# Features
- **Brick Types**: The game includes three types of bricks, each with different hit points and corresponding score values:
- **Gold Bricks**: Requires 3 hits to break and gives the 25 points.
- **Silver Bricks**: Requires 2 hits to break and gives 15 points.
- **Red Bricks**: Requires 1 hit to break and gives the 8 points.
- **Dynamic Speed**: The ball speed increases as the score increases, adding more challenge to the game.
- **Scoring System**: Track your current score and lives.
- **Persistent Highest Score**: Highest score is saved locally in your browser.
- **Game Over Popup**: A popup appears with a restart button when the game is over.
- **Easy Controls**: Use arrow keys to move the basket left and right.

# Installation
- Clone the repository
- Navigate to the project directory `cd Arkanoid_Game`

# Usage
- Open index.html to start the game.
- Use the left and right arrow keys to move the paddle.
- Keep an eye on your score and lives at the top of the screen.
- When you lose all your lives, the game will show a "Game Over" popup with a button to restart the game.

# Controls
- **Left Arrow Key**: Move basket to the left.
- **Right Arrow Key**: Move basket to the right.

# Enjoy Playing!🥳
29 changes: 29 additions & 0 deletions SinglePlayer - Games/Arkanoid_Game/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arkanoid Game</title>
<link rel = "stylesheet" href = 'style.css'>
</head>
<body>
<div class = 'flex'>
<p id = 'highest-score'>Highest Score is : </p>
<p id = 'current-score'>Current Score : 0</p>
</div>
<div class = 'game-container'>
<div id = 'game-start'>
<p class = 'name'>Break the Blocks</p>
<p>Ready to Play!!</p>
<button id = 'start'>Start Game</button>
</div>
<canvas id = 'game-board' width = 802 height = 560></canvas>
<div id = 'game-over'>
<h1> Game Over</h1>
<p id = 'score-card'></p>
<button id = 'restart'>Restart Game</button>
</div>
</div>
<script src = 'script.js'></script>
</body>
</html>
248 changes: 248 additions & 0 deletions SinglePlayer - Games/Arkanoid_Game/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
const canvas = document.getElementById('game-board')
const gameOver = document.getElementById('game-over')
const gameStart = document.getElementById('game-start')
const restart = document.getElementById('restart')
const start = document.getElementById('start')
const scoreCard = document.getElementById('score-card')
const Currscore = document.getElementById('current-score')
const highScoreCard = document.getElementById('highest-score')

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

const highestScoreKey = 'arkanoid'
let highestScore = localStorage.getItem(highestScoreKey) ? parseInt(localStorage.getItem(highestScoreKey)) : 0

highScoreCard.textContent += highestScore

//paddle
const paddleWidth = 130
const paddleHeight = 12
const paddleSpeed = 16

const ballRadius = 10
//let gameRunning

//brick
const brickRowCount = 7
const brickColumnCount = 9
const brickWidth = 75
const brickHeight = 20
const brickPadding = 10
const brickOffsetTop = 30
const brickOffsetLeft = 25

let score = 0

const keys = {
left : false,
right : false
}

bricks = []

//When we press the arrow key
document.addEventListener('keydown', e => {
if (e.key === 'ArrowLeft') keys.left = true
if (e.key === 'ArrowRight') keys.right = true
})

//When arrow key is released
document.addEventListener('keyup', e => {
if (e.key === 'ArrowLeft') keys.left = false
if (e.key === 'ArrowRight') keys.right = false
})

function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}

function drawPaddle(){
ctx.beginPath() //It's typically used when you want to start a new shape or line segment without connecting it to the previous shapes or lines drawn on the canvas.
//canvas.height: This is the total height of the canvas. Placing something exactly at canvas.height would put it right at the bottom edge of the canvas, which means it would be off-screen because the y-coordinates start at 0 at the top and increase downward.
//paddleHeight: This is the height of the paddle itself. By subtracting the paddle's height from the canvas height, you ensure that the top edge of the paddle is positioned exactly paddleHeight pixels above the bottom edge of the canvas.
// x-coordinate, y-coordinate, width of paddle, height of paddle
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight)
ctx.fillStyle = '#00FF00'
ctx.fill()
ctx.closePath()
}

function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
if (c === 4){
continue
}
for (let r = 0; r < brickRowCount; r++) {
// Only draw the brick if it has hit points left
if (bricks[c][r].hitPoints > 0) {
//c * (brickWidth + brickPadding) calculates the total horizontal distance from the left edge of the first brick to the left edge of the current brick.
//brickOffsetLeft ensures that the entire row of bricks is offset from the left edge of the canvas by a specific amount
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop
bricks[c][r].x = brickX
bricks[c][r].y = brickY
ctx.beginPath()
ctx.rect(brickX, brickY, brickWidth, brickHeight)
ctx.fillStyle = bricks[c][r].color
ctx.fill()
ctx.closePath()
}
}
}
}

function movePaddle(){
if (keys.left && paddleX > 0){
paddleX -= paddleSpeed
}
if (keys.right && paddleX < canvas.width - paddleWidth){
paddleX += paddleSpeed
}
}

// Collision detection
function collisionDetection() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const brick = bricks[c][r];
if (brick.hitPoints > 0) {
//Checking if ball's x-coordinate is greater than the brick's left edge and less than the brick's right edge
//Similarly for y-coordinate
if (x > brick.x && x < brick.x + brickWidth && y > brick.y && y < brick.y + brickHeight) {
dy = -dy //it reverses the vertical direction of the ball by negating its vertical velocity (dy). This makes the ball bounce off the brick.
brick.hitPoints--
if (brick.hitPoints === 2) {
brick.color = "#bebebe" //silver
} else if (brick.hitPoints === 1) {
brick.color = "red"
}
else {
brick.color = "";
score += (r === 3 || r === 0) ? 25 : (r === 2) ? 15 : 8
}
}
}
}
}
}

function adjustSpeedBasedOnScore() {
const baseSpeed = 3
const speedIncrement = 0.5 // Adjust increment as needed
const additionalSpeed = Math.floor(score / 30) * speedIncrement;

const newSpeed = baseSpeed + additionalSpeed

// Adjust dx and dy proportionally to maintain the direction
const speedRatio = newSpeed / Math.sqrt(dx * dx + dy * dy);
dx *= speedRatio;
dy *= speedRatio;
}

let animationFrameId

function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height)

drawBricks()
drawBall()
drawPaddle()
collisionDetection()
adjustSpeedBasedOnScore()


//Horizontal Collision Detection
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx
}
//Top Collision Detection
if (y + dy < ballRadius) {
dy = -dy
}
//Bottom Collision Detection
else if (y + dy > canvas.height - ballRadius) {
if (x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
//gameRunning = false
scoreCard.textContent = 'Your Score is : ' + score
gameOver.style.display = 'block'
Currscore.style.display = 'none'

if (score > highestScore){
highestScore = score
localStorage.setItem(highestScoreKey, highestScore)
highScoreCard.textContent = 'Highest Score is : ' + highestScore
}
return
}
}
movePaddle()

x += dx
y += dy
Currscore.textContent = 'Current Score : ' + score
animationFrameId = requestAnimationFrame(draw)
}

function resetGame() {
//so that on restarting game speed of ball remains same, it happened bcoz each time resetGame is called, it starts a new game
//loop by calling draw again, which can result in multiple overlapping game loops, causing the ball to move faster.
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}

// Reset ball position
x = canvas.width / 2
y = canvas.height - 30

// Reset ball velocity
const speed = 3
dx = (Math.random() * 2 - 1) * speed
dy = -speed //so that ball always moves upward

//Reset paddle
//By dividing the remaining space of the canvas (canvas.width - paddleWidth) by 2, we find the amount of space needed on the left
//side of the paddle to center it horizontally. This places the paddle in the middle of the canvas
paddleX = (canvas.width - paddleWidth) / 2

// Reset bricks
//it initializes a grid of bricks with different hit points and colors based on their row positions
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = []
for (let r = 0; r < brickRowCount; r++) {
const hitPoints = (r === 3 || r === 0) ? 3 : (r === 2 || r === 4) ? 2 : 1;
let color
if (hitPoints === 3) {
color = '#ffcc00' //gold
} else if (hitPoints === 2) {
color = '#bebebe' //silver
} else {
color = 'red'
}
bricks[c][r] = { x: 0, y: 0, hitPoints, color }
}
}

score = 0
Currscore.style.display = 'block'
// Restart the game loop
draw()
}

start.addEventListener('click', e => {
gameStart.style.display = 'none'
canvas.style.display = 'block'
resetGame()
})

//Re-initializing all variables
restart.addEventListener('click', e => {
gameOver.style.display = 'none'
resetGame()

})
67 changes: 67 additions & 0 deletions SinglePlayer - Games/Arkanoid_Game/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
body{
box-sizing: border-box;
margin: 0;
height: 97vh;
}
.game-container{
display: flex;
justify-content: center;
align-items: center;
position: relative;
height: 90%;
}
#game-board{
background-color: black;
background-size: cover;
display: none;
}
#game-start{
height: 500px;
width: 800px;
backdrop-filter: blur(12px) brightness(78%);
text-align: center;
font-size: 18px;
}
#game-over{
width: 802px;
height: 560px;
display: none;
background-color: rgba(208, 207, 207, 0.838);
position: absolute;
z-index: 3;
text-align: center;
}
h1{
margin-top: 18%;
margin-bottom: 3%;
font-family: Arial, Helvetica, sans-serif;
}
button{
width: 140px;
height: 40px;
font-size: 16px;
cursor: pointer;
}
#score-card{
font-size: 18px;
margin-bottom: 3%;
}
.flex{
display: flex;
}
.flex p{
margin: 12px;
font-size: 20px;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
margin-right: 2.5%;
}
#highest-score{
flex-grow: 1;
}
.name{
font-size: 23px;
font-family: Arial, Helvetica, sans-serif;
margin-top: 15%;
color: rgba(7, 79, 174, 0.91);
font-weight: 600;
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 5b7472b

Please sign in to comment.