Upload files to "/"
This commit is contained in:
parent
ec3ec011fc
commit
b0a527843b
30
index.php
Normal file
30
index.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!---------------------------------- index.php ---------------------------------->
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
$conn = new mysqli("localhost","root","","breakout_db");
|
||||||
|
if($conn->connect_error){ die("DB Error"); }
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Breakout Game</h1>
|
||||||
|
<div class="menu">
|
||||||
|
<a href="login.php">Login / Register</a>
|
||||||
|
<a href="leaderboard.php">Leaderboard</a>
|
||||||
|
</div>
|
||||||
|
<canvas id="game" width="480" height="320"></canvas>
|
||||||
|
<p>Score: <span id="score">0</span></p>
|
||||||
|
<p>
|
||||||
|
<select id="diff">
|
||||||
|
<option value="easy">Easy</option>
|
||||||
|
<option value="medium">Medium</option>
|
||||||
|
<option value="hard">Hard</option>
|
||||||
|
</select>
|
||||||
|
<button onclick="startGame()">Start</button>
|
||||||
|
</p>
|
||||||
|
<script src="game.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
leaderboard.php
Normal file
19
leaderboard.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
$conn = new mysqli("localhost","root","","breakout_db");
|
||||||
|
$lb = $conn->query("SELECT username, highscore FROM users ORDER BY highscore DESC LIMIT 10");
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Leaderboard</h2>
|
||||||
|
<table>
|
||||||
|
<?php while($r = $lb->fetch_assoc()){ ?>
|
||||||
|
<tr><td><?= $r['username'] ?></td><td><?= $r['highscore'] ?></td></tr>
|
||||||
|
<?php } ?>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
49
login.php
Normal file
49
login.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
$conn = new mysqli("localhost","root","","breakout_db");
|
||||||
|
if($conn->connect_error){ die("DB Error"); }
|
||||||
|
|
||||||
|
// Register
|
||||||
|
if(isset($_POST['register'])){
|
||||||
|
$u = $_POST['user'];
|
||||||
|
$p = password_hash($_POST['pass'], PASSWORD_DEFAULT);
|
||||||
|
$conn->query("INSERT INTO users(username,password,highscore) VALUES('$u','$p',0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login
|
||||||
|
if(isset($_POST['login'])){
|
||||||
|
$u = $_POST['user'];
|
||||||
|
$p = $_POST['pass'];
|
||||||
|
$res = $conn->query("SELECT * FROM users WHERE username='$u'");
|
||||||
|
if($row = $res->fetch_assoc()){
|
||||||
|
if(password_verify($p, $row['password'])){
|
||||||
|
$_SESSION['user'] = $u;
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="login-box">
|
||||||
|
<h2>Login</h2>
|
||||||
|
<form method="POST">
|
||||||
|
<input name="user" placeholder="Username" required>
|
||||||
|
<input name="pass" type="password" placeholder="Password" required>
|
||||||
|
<button name="login">Login</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Register</h2>
|
||||||
|
<form method="POST">
|
||||||
|
<input name="user" placeholder="Username" required>
|
||||||
|
<input name="pass" type="password" placeholder="Password" required>
|
||||||
|
<button name="register">Register</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
7
save.php
Normal file
7
save.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
$conn = new mysqli("localhost","root","","breakout_db");
|
||||||
|
$u = $_SESSION['user'];
|
||||||
|
$s = intval($_GET['score']);
|
||||||
|
$conn->query("UPDATE users SET highscore = GREATEST(highscore, $s) WHERE username='$u'");
|
||||||
|
?>
|
||||||
241
script.js
241
script.js
@ -1,189 +1,84 @@
|
|||||||
const canvas = document.getElementById('gameCanvas');
|
let canvas = document.getElementById("game");
|
||||||
const ctx = canvas.getContext('2d');
|
let ctx = canvas.getContext("2d");
|
||||||
const uiLayer = document.getElementById('ui-layer');
|
|
||||||
const finalScoreSpan = document.getElementById('final-score');
|
|
||||||
const inGameScore = document.getElementById('ingame-score');
|
|
||||||
const highScoreDisplay = document.getElementById('high-score-display');
|
|
||||||
|
|
||||||
// Setup UI Flexbox
|
|
||||||
uiLayer.style.display = 'none';
|
|
||||||
|
|
||||||
let score = 0;
|
let score = 0;
|
||||||
let gameRunning = true;
|
let running = false;
|
||||||
let speed = 2;
|
let ball, paddle, bricks, rows, cols, speed;
|
||||||
let blockHeight = 35;
|
let hitSound = new Audio("hit.wav");
|
||||||
let initialWidth = 200;
|
let loseSound = new Audio("lose.wav");
|
||||||
let blocks = [];
|
|
||||||
let currentBlock = {};
|
|
||||||
let direction = 1;
|
|
||||||
let hue = 0;
|
|
||||||
|
|
||||||
function initGame() {
|
function startGame(){
|
||||||
score = 0;
|
score = 0;
|
||||||
blocks = [];
|
document.getElementById("score").textContent = score;
|
||||||
speed = 3;
|
running = true;
|
||||||
hue = Math.random() * 360;
|
|
||||||
|
|
||||||
inGameScore.innerText = score;
|
let diff = document.getElementById("diff").value;
|
||||||
|
if(diff === "easy"){ rows = 3; cols = 5; speed = 3; }
|
||||||
|
if(diff === "medium"){ rows = 4; cols = 7; speed = 4; }
|
||||||
|
if(diff === "hard"){ rows = 6; cols = 9; speed = 5; }
|
||||||
|
|
||||||
// Base block
|
paddle = { x:200, w:80, h:10 };
|
||||||
blocks.push({
|
ball = { x:240, y:200, dx:speed, dy:-speed, r:6 };
|
||||||
x: (canvas.width - initialWidth) / 2,
|
bricks = [];
|
||||||
y: canvas.height - 100,
|
|
||||||
width: initialWidth,
|
for(let r=0;r<rows;r++){
|
||||||
color: `hsl(${hue}, 100%, 50%)`
|
for(let c=0;c<cols;c++){
|
||||||
|
bricks.push({ x: c*50+30, y: r*20+30, w:40, h:15, hit:false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loop(){
|
||||||
|
if(!running) return;
|
||||||
|
ctx.clearRect(0,0,480,320);
|
||||||
|
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.fillRect(paddle.x, 300, paddle.w, paddle.h);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI*2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ball.x += ball.dx;
|
||||||
|
ball.y += ball.dy;
|
||||||
|
|
||||||
|
if(ball.x < ball.r || ball.x > 480-ball.r) ball.dx *= -1;
|
||||||
|
if(ball.y < ball.r) ball.dy *= -1;
|
||||||
|
|
||||||
|
if(ball.y > 294 && ball.x > paddle.x && ball.x < paddle.x+paddle.w){
|
||||||
|
ball.dy *= -1;
|
||||||
|
hitSound.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
bricks.forEach(b =>{
|
||||||
|
if(!b.hit && ball.x > b.x && ball.x < b.x+b.w && ball.y > b.y && ball.y < b.y+b.h){
|
||||||
|
b.hit = true;
|
||||||
|
ball.dy *= -1;
|
||||||
|
score += 10;
|
||||||
|
document.getElementById("score").textContent = score;
|
||||||
|
hitSound.play();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
spawnBlock();
|
bricks.forEach(b =>{
|
||||||
|
if(!b.hit){ ctx.fillRect(b.x,b.y,b.w,b.h); }
|
||||||
// Perubahan: Jangan panggil draw() di sini jika ini reset,
|
|
||||||
// biarkan resetGame yang mengontrol loop
|
|
||||||
if(gameRunning) draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
function spawnBlock() {
|
|
||||||
const prevBlock = blocks[blocks.length - 1];
|
|
||||||
hue += 10;
|
|
||||||
|
|
||||||
currentBlock = {
|
|
||||||
x: 0,
|
|
||||||
y: prevBlock.y - blockHeight,
|
|
||||||
width: prevBlock.width,
|
|
||||||
color: `hsl(${hue}, 100%, 50%)`
|
|
||||||
};
|
|
||||||
|
|
||||||
if (currentBlock.y < 150) {
|
|
||||||
blocks.forEach(b => b.y += blockHeight);
|
|
||||||
currentBlock.y += blockHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBackground() {
|
|
||||||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
|
|
||||||
ctx.lineWidth = 1;
|
|
||||||
for (let i = 0; i < canvas.width; i += 40) {
|
|
||||||
ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height); ctx.stroke();
|
|
||||||
}
|
|
||||||
for (let i = 0; i < canvas.height; i += 40) {
|
|
||||||
ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(canvas.width, i); ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function draw() {
|
|
||||||
if (!gameRunning) return;
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
drawBackground();
|
|
||||||
|
|
||||||
currentBlock.x += speed * direction;
|
|
||||||
if (currentBlock.x + currentBlock.width > canvas.width || currentBlock.x < 0) {
|
|
||||||
direction *= -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.shadowBlur = 20;
|
|
||||||
blocks.forEach(b => {
|
|
||||||
ctx.fillStyle = b.color;
|
|
||||||
ctx.shadowColor = b.color;
|
|
||||||
ctx.fillRect(b.x, b.y, b.width, blockHeight);
|
|
||||||
ctx.fillStyle = 'rgba(255,255,255,0.2)';
|
|
||||||
ctx.fillRect(b.x, b.y, b.width, 5);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.fillStyle = currentBlock.color;
|
if(ball.y > 330){
|
||||||
ctx.shadowColor = currentBlock.color;
|
loseSound.play();
|
||||||
ctx.fillRect(currentBlock.x, currentBlock.y, currentBlock.width, blockHeight);
|
running = false;
|
||||||
ctx.fillStyle = 'rgba(255,255,255,0.4)';
|
saveScore();
|
||||||
ctx.fillRect(currentBlock.x, currentBlock.y, currentBlock.width, 5);
|
alert("Game Over");
|
||||||
ctx.shadowBlur = 0;
|
|
||||||
|
|
||||||
requestAnimationFrame(draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
function placeBlock() {
|
|
||||||
if (!gameRunning) return;
|
|
||||||
|
|
||||||
const prevBlock = blocks[blocks.length - 1];
|
|
||||||
let overlap = currentBlock.width - Math.abs(currentBlock.x - prevBlock.x);
|
|
||||||
|
|
||||||
if (overlap <= 0) {
|
|
||||||
gameOver();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentBlock.x < prevBlock.x) {
|
requestAnimationFrame(loop);
|
||||||
currentBlock.width = overlap;
|
|
||||||
currentBlock.x = prevBlock.x;
|
|
||||||
} else {
|
|
||||||
currentBlock.width = overlap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks.push(currentBlock);
|
window.addEventListener("mousemove", e =>{
|
||||||
score++;
|
paddle.x = e.clientX - canvas.offsetLeft - paddle.w/2;
|
||||||
inGameScore.innerText = score;
|
|
||||||
speed += 0.15;
|
|
||||||
spawnBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
function gameOver() {
|
|
||||||
gameRunning = false;
|
|
||||||
uiLayer.style.display = 'flex';
|
|
||||||
finalScoreSpan.innerText = score;
|
|
||||||
saveScore(score);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- PERBAIKAN UTAMA ADA DI SINI ---
|
|
||||||
function resetGame() {
|
|
||||||
uiLayer.style.display = 'none';
|
|
||||||
|
|
||||||
// Matikan gameRunning sementara agar klik tombol tidak terbaca sebagai 'placeBlock'
|
|
||||||
gameRunning = false;
|
|
||||||
|
|
||||||
initGame();
|
|
||||||
|
|
||||||
// Beri jeda 300ms. Setelah tombol dilepas, baru game aktif.
|
|
||||||
setTimeout(() => {
|
|
||||||
gameRunning = true;
|
|
||||||
draw(); // Mulai loop animasi
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input Handler yang lebih aman
|
|
||||||
document.body.onkeydown = (e) => {
|
|
||||||
if (e.code === 'Space') {
|
|
||||||
if (gameRunning) {
|
|
||||||
placeBlock();
|
|
||||||
} else if (uiLayer.style.display === 'flex') {
|
|
||||||
// Cegah spasi merestart terlalu cepat
|
|
||||||
resetGame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.body.onclick = (e) => {
|
|
||||||
// PENTING: Jika yang diklik adalah tombol atau bagian dari UI Layer, JANGAN jalankan game
|
|
||||||
if (e.target.closest('#ui-layer') || e.target.closest('.btn')) return;
|
|
||||||
|
|
||||||
if (gameRunning) placeBlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
function saveScore(newScore) {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('score', newScore);
|
|
||||||
fetch('save_score.php', { method: 'POST', body: formData })
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => fetchHighScore());
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchHighScore() {
|
|
||||||
fetch('save_score.php?action=get')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
highScoreDisplay.innerText = data.high_score || "0";
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Start pertama kali
|
function saveScore(){
|
||||||
initGame();
|
fetch("save.php?score="+score);
|
||||||
// Paksa draw pertama kali
|
}
|
||||||
gameRunning = true;
|
|
||||||
draw();
|
|
||||||
Loading…
x
Reference in New Issue
Block a user