Neon Racer
by BlazeKoala99798 lines18.3 KB
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Neon Racer</title>
<style>
* { box-sizing: border-box; }
:root {
--yellow: #ffe45e;
--cyan: #22d3ee;
--bg: #080d19;
}
body {
margin: 0;
min-height: 100vh;
text-align: center;
color: white;
font-family: system-ui, Arial, sans-serif;
background:
radial-gradient(ellipse at top, #17264b, transparent 65%),
var(--bg);
user-select: none;
-webkit-user-select: none;
}
h1 {
margin: 14px 0 4px;
color: var(--yellow);
letter-spacing: 3px;
text-shadow: 0 0 20px #facc1570;
}
.subtitle {
margin: 0 0 12px;
color: #a5b4cc;
font-size: 12px;
letter-spacing: 3px;
}
.hud {
width: min(380px, 94vw);
margin: 12px auto;
display: flex;
justify-content: space-between;
gap: 8px;
}
.stat {
flex: 1;
padding: 10px 6px;
border: 1px solid #ffffff1c;
border-radius: 12px;
background: #ffffff0b;
}
.stat small {
display: block;
color: #9caecb;
font-size: 10px;
letter-spacing: 1px;
}
.stat strong {
display: block;
margin-top: 3px;
font-size: 18px;
color: var(--yellow);
}
#game {
position: relative;
width: min(380px, 94vw);
height: min(62vh, 620px);
min-height: 380px;
margin: auto;
overflow: hidden;
background: #292f3d;
border: 3px solid #536079;
border-radius: 12px;
box-shadow: 0 0 40px #0008, 0 0 20px #22d3ee15;
isolation: isolate;
}
#game::before, #game::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
width: 7px;
background: linear-gradient(#39e98a, #087c54);
z-index: 2;
}
#game::before { left: 0; }
#game::after { right: 0; }
.road-line {
position: absolute;
width: 2px;
background: linear-gradient(to bottom, #ffffff22, #ffffff66, #ffffff22);
top: 0;
bottom: 0;
transform: translateX(-50%);
z-index: 0;
}
.car {
position: absolute;
width: 42px;
height: 66px;
border: 2px solid #ffffffcc;
border-radius: 12px 12px 8px 8px;
box-shadow: 0 5px 12px #0007;
}
.car::before {
content: "";
position: absolute;
top: 12px;
left: 6px;
width: 26px;
height: 20px;
border-radius: 5px 5px 8px 8px;
background: linear-gradient(135deg, #dffaff, #38bdf8);
border: 1px solid #ffffff99;
}
.car::after {
content: "";
position: absolute;
bottom: 5px;
left: 6px;
width: 7px;
height: 5px;
border-radius: 2px;
background: #fff8b5;
box-shadow: 17px 0 #fff8b5;
}
#player {
position: absolute;
bottom: 22px;
background: linear-gradient(90deg, #b91c1c, #ff4c63, #dc2626);
z-index: 1;
will-change: left;
}
.coin-item {
position: absolute;
width: 24px;
height: 24px;
background: var(--yellow);
border-radius: 50%;
border: 2px solid #fff;
box-shadow: 0 0 10px var(--yellow);
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
color: #101323;
}
.enemy {
background: linear-gradient(90deg, #1d4ed8, #38bdf8, #1e40af);
}
.enemy:nth-of-type(odd) {
background: linear-gradient(90deg, #6d28d9, #c084fc, #7c3aed);
}
#message {
position: absolute;
inset: 0;
z-index: 5;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 24px;
background: #080d19f0;
backdrop-filter: blur(7px);
}
#message h2 {
color: var(--yellow);
font-size: 26px;
margin: 0 0 10px;
}
#message p {
color: #d1d9e8;
line-height: 1.5;
font-size: 14px;
margin-bottom: 16px;
}
.menu-buttons {
display: flex;
flex-direction: column;
gap: 10px;
width: 100%;
max-width: 220px;
}
.shop-grid {
display: grid;
grid-template-columns: 1fr;
gap: 8px;
width: 100%;
max-height: 220px;
overflow-y: auto;
margin-bottom: 12px;
padding-right: 4px;
}
.shop-item {
display: flex;
justify-content: space-between;
align-items: center;
background: #ffffff0d;
border: 1px solid #ffffff20;
padding: 8px 12px;
border-radius: 10px;
font-size: 13px;
}
.shop-item button {
padding: 6px 12px;
font-size: 12px;
}
button {
border: 0;
border-radius: 12px;
padding: 12px 20px;
font-size: 15px;
font-weight: 800;
cursor: pointer;
transition: transform .12s, filter .12s;
touch-action: manipulation;
}
button:active {
transform: scale(.94);
filter: brightness(1.2);
}
.primary {
color: #101323;
background: linear-gradient(135deg, #fff176, #facc15);
box-shadow: 0 4px 20px #facc1535;
}
.secondary-btn {
color: white;
background: #26344d;
border: 1px solid #ffffff30;
}
#pause {
color: white;
background: #26344d;
border: 1px solid #ffffff30;
padding: 9px 12px;
}
.controls {
width: min(380px, 94vw);
display: flex;
justify-content: center;
gap: 18px;
margin: 14px auto 8px;
}
.controls button {
flex: 1;
max-width: 160px;
height: 62px;
font-size: 26px;
color: white;
background: linear-gradient(145deg, #263852, #18243a);
border: 1px solid #ffffff25;
touch-action: none;
}
.hint {
color: #8e9bb1;
font-size: 12px;
margin: 8px 0 18px;
}
</style>
</head>
<body>
<h1>🏎️ NEON RACER</h1>
<p class="subtitle">DODGE • SURVIVE • DOMINATE</p>
<div class="hud">
<div class="stat">
<small>SCORE</small>
<strong id="score">0</strong>
</div>
<div class="stat">
<small>COINS 🪙</small>
<strong id="coins">0</strong>
</div>
<div class="stat">
<small>BEST 🏆</small>
<strong id="best">0</strong>
</div>
<button id="pause" aria-label="Pause">Ⅱ</button>
</div>
<div id="game">
<div id="player" class="car"></div>
<div id="message">
<h2>🏁 NEON RACER</h2>
<p>Weiche Autos aus, sammle Coins & schalte Skins frei!</p>
<div class="menu-buttons">
<button id="start" class="primary">▶ SPIEL STARTEN</button>
<button id="shop-btn" class="secondary-btn">🛒 SHOP</button>
</div>
</div>
</div>
<div class="controls">
<button id="left" aria-label="Nach links">◀</button>
<button id="right" aria-label="Nach rechts">▶</button>
</div>
<p class="hint">Tipp: ← → oder A / D zum Steuern</p>
<script>
const game = document.getElementById("game");
const player = document.getElementById("player");
const message = document.getElementById("message");
const scoreText = document.getElementById("score");
const coinsText = document.getElementById("coins");
const bestText = document.getElementById("best");
const pauseBtn = document.getElementById("pause");
let currentLane = 1;
let targetX = 0;
let x = 0;
let score = 0;
let coins = 0;
let totalCoins = 0;
let speed = 250;
let enemies = [];
let itemCoins = [];
let lines = [];
let running = false;
let paused = false;
let lastTime = 0;
let spawnTimer = 0;
let animationId = 0;
let best = 0;
let keys = { left: false, right: false };
// Skins Definition
const skins = [
{ id: 'classic', name: '🔴 Classic Red', price: 0, css: 'linear-gradient(90deg, #b91c1c, #ff4c63, #dc2626)' },
{ id: 'cyan', name: '💎 Cyan Cyber', price: 50, css: 'linear-gradient(90deg, #0e7490, #22d3ee, #0891b2)' },
{ id: 'gold', name: '👑 Royal Gold', price: 150, css: 'linear-gradient(90deg, #ca8a04, #facc15, #eab308)' },
{ id: 'neon', name: '⚡ Toxic Green', price: 300, css: 'linear-gradient(90deg, #15803d, #4ade80, #16a34a)' }
];
let unlockedSkins = ['classic'];
let currentSkin = 'classic';
// Load Data from LocalStorage
try {
best = Number(localStorage.getItem("neonRacerBest")) || 0;
totalCoins = Number(localStorage.getItem("neonRacerCoins")) || 0;
unlockedSkins = JSON.parse(localStorage.getItem("neonRacerSkins")) || ['classic'];
currentSkin = localStorage.getItem("neonRacerCurrentSkin") || 'classic';
} catch {}
bestText.textContent = best;
coinsText.textContent = totalCoins;
updatePlayerSkin();
function updatePlayerSkin() {
const skin = skins.find(s => s.id === currentSkin) || skins[0];
player.style.background = skin.css;
}
// Road Setup
for (let j = 0; j < 2; j++) {
const line = document.createElement("div");
line.className = "road-line";
game.appendChild(line);
lines.push({ el: line, lineIndex: j });
}
function getLaneCenterX(lane) {
const roadWidth = game.clientWidth - 14;
const laneWidth = roadWidth / 3;
return 7 + lane * laneWidth + (laneWidth - player.offsetWidth) / 2;
}
function resetRoad() {
const roadWidth = game.clientWidth - 14;
const laneWidth = roadWidth / 3;
lines.forEach((line) => {
const lineX = 7 + (line.lineIndex + 1) * laneWidth;
line.el.style.left = lineX + "px";
});
}
function movePlayer(direction) {
if (!running || paused) return;
currentLane = Math.max(0, Math.min(2, currentLane + direction));
targetX = getLaneCenterX(currentLane);
}
function spawnEntity() {
const lane = Math.floor(Math.random() * 3);
const left = getLaneCenterX(lane);
if (Math.random() < 0.3) {
const coinEl = document.createElement("div");
coinEl.className = "coin-item";
coinEl.textContent = "🪙";
coinEl.style.left = (left + 9) + "px";
coinEl.style.top = "-30px";
game.appendChild(coinEl);
itemCoins.push({ el: coinEl, y: -30, x: left + 9 });
} else {
const enemy = document.createElement("div");
enemy.className = "car enemy";
enemy.style.left = left + "px";
enemy.style.top = "-75px";
game.appendChild(enemy);
enemies.push({ el: enemy, y: -75, x: left, passed: false });
}
}
function hitTest(a, b) {
return a.left + 6 < b.right - 6 &&
a.right - 6 > b.left + 6 &&
a.top + 7 < b.bottom - 7 &&
a.bottom - 5 > b.top + 5;
}
function updateHud() {
scoreText.textContent = Math.floor(score);
coinsText.textContent = totalCoins;
}
function clearEntities() {
enemies.forEach(e => e.el.remove());
enemies = [];
itemCoins.forEach(c => c.el.remove());
itemCoins = [];
}
function showMainMenu() {
message.replaceChildren();
const h = document.createElement("h2");
h.textContent = "🏁 NEON RACER";
const p = document.createElement("p");
p.innerHTML = `Coins gesamt: <b>${totalCoins} 🪙</b><br>Weiche dem Verkehr aus & sammle Coins!`;
const btnContainer = document.createElement("div");
btnContainer.className = "menu-buttons";
const startBtn = document.createElement("button");
startBtn.className = "primary";
startBtn.textContent = "▶ SPIEL STARTEN";
startBtn.addEventListener("click", startGame);
const shopBtn = document.createElement("button");
shopBtn.className = "secondary-btn";
shopBtn.textContent = "🛒 SHOP";
shopBtn.addEventListener("click", showShop);
btnContainer.append(startBtn, shopBtn);
message.append(h, p, btnContainer);
message.style.display = "flex";
}
function showShop() {
message.replaceChildren();
const h = document.createElement("h2");
h.textContent = "🛒 CAR SHOP";
const p = document.createElement("p");
p.style.margin = "0 0 8px 0";
p.innerHTML = `Guthaben: <b>${totalCoins} 🪙</b>`;
const grid = document.createElement("div");
grid.className = "shop-grid";
skins.forEach(skin => {
const item = document.createElement("div");
item.className = "shop-item";
const isUnlocked = unlockedSkins.includes(skin.id);
const isSelected = currentSkin === skin.id;
let btnText = "";
let btnClass = "primary";
let isDisabled = false;
if (isSelected) {
btnText = "AKTIV";
isDisabled = true;
} else if (isUnlocked) {
btnText = "WÄHLEN";
btnClass = "secondary-btn";
} else {
btnText = `${skin.price} 🪙`;
}
item.innerHTML = `<span>${skin.name}</span>`;
const btn = document.createElement("button");
btn.className = btnClass;
btn.textContent = btnText;
if (isDisabled) btn.style.opacity = "0.6";
btn.addEventListener("click", () => {
if (isSelected) return;
if (isUnlocked) {
currentSkin = skin.id;
updatePlayerSkin();
saveData();
showShop();
} else if (totalCoins >= skin.price) {
totalCoins -= skin.price;
unlockedSkins.push(skin.id);
currentSkin = skin.id;
updatePlayerSkin();
saveData();
showShop();
} else {
alert("Nicht genug Coins!");
}
});
item.appendChild(btn);
grid.appendChild(item);
});
const backBtn = document.createElement("button");
backBtn.className = "secondary-btn";
backBtn.textContent = "⬅ ZURÜCK";
backBtn.style.width = "100%";
backBtn.addEventListener("click", showMainMenu);
message.append(h, p, grid, backBtn);
}
function saveData() {
try {
localStorage.setItem("neonRacerBest", best);
localStorage.setItem("neonRacerCoins", totalCoins);
localStorage.setItem("neonRacerSkins", JSON.stringify(unlockedSkins));
localStorage.setItem("neonRacerCurrentSkin", currentSkin);
} catch {}
}
function endGame() {
running = false;
paused = false;
cancelAnimationFrame(animationId);
pauseBtn.textContent = "Ⅱ";
totalCoins += coins;
const finalScore = Math.floor(score);
if (finalScore > best) best = finalScore;
saveData();
updateHud();
message.replaceChildren();
const h = document.createElement("h2");
h.textContent = "💥 GAME OVER";
const p = document.createElement("p");
p.innerHTML = `Score: ${finalScore} • Rekord: ${best}<br>Erhaltene Coins: <b>+${coins} 🪙</b>`;
const btnContainer = document.createElement("div");
btnContainer.className = "menu-buttons";
const retryBtn = document.createElement("button");
retryBtn.className = "primary";
retryBtn.textContent = "↻ NOCHMAL";
retryBtn.addEventListener("click", startGame);
const menuBtn = document.createElement("button");
menuBtn.className = "secondary-btn";
menuBtn.textContent = "🏠 MENÜ";
menuBtn.addEventListener("click", showMainMenu);
btnContainer.append(retryBtn, menuBtn);
message.append(h, p, btnContainer);
message.style.display = "flex";
}
function startGame() {
cancelAnimationFrame(animationId);
clearEntities();
score = 0;
coins = 0;
speed = 250;
spawnTimer = 0;
running = true;
paused = false;
keys.left = keys.right = false;
currentLane = 1;
x = targetX = getLaneCenterX(currentLane);
player.style.left = x + "px";
resetRoad();
updateHud();
message.style.display = "none";
pauseBtn.textContent = "Ⅱ";
lastTime = performance.now();
animationId = requestAnimationFrame(loop);
}
function togglePause() {
if (!running) return;
paused = !paused;
pauseBtn.textContent = paused ? "▶" : "Ⅱ";
if (paused) {
cancelAnimationFrame(animationId);
message.replaceChildren();
const h = document.createElement("h2");
h.textContent = "⏸ PAUSE";
const btnContainer = document.createElement("div");
btnContainer.className = "menu-buttons";
const resumeBtn = document.createElement("button");
resumeBtn.className = "primary";
resumeBtn.textContent = "▶ WEITER";
resumeBtn.addEventListener("click", () => {
message.style.display = "none";
paused = false;
pauseBtn.textContent = "Ⅱ";
lastTime = performance.now();
animationId = requestAnimationFrame(loop);
});
const menuBtn = document.createElement("button");
menuBtn.className, "secondary-btn";
menuBtn.textContent = "🏠 MENÜ";
menuBtn.addEventListener("click", showMainMenu);
btnContainer.append(resumeBtn, menuBtn);
message.append(h, btnContainer);
message.style.display = "flex";
}
}
let keyCooldown = false;
function loop(time) {
if (!running || paused) return;
const dt = Math.min((time - lastTime) / 1000, 0.04);
lastTime = time;
// Score kontinuierlich basierend auf der aktuellen Geschwindigkeit erhöhen (wie in Subway Surfers)
score += speed * dt * 0.5;
updateHud();
if ((keys.left || keys.right) && !keyCooldown) {
if (keys.left) movePlayer(-1);
if (keys.right) movePlayer(1);
keyCooldown = true;
setTimeout(() => { keyCooldown = false; }, 150);
}
targetX = getLaneCenterX(currentLane);
x += (targetX - x) * Math.min(1, 16 * dt);
player.style.left = x + "px";
spawnTimer += dt;
const spawnInterval = Math.max(0.45, 1.0 - (score * 0.0001));
if (spawnTimer >= spawnInterval) {
spawnEntity();
spawnTimer = 0;
}
const playerRect = player.getBoundingClientRect();
// Gegner aktualisieren & Kollision prüfen
for (let i = enemies.length - 1; i >= 0; i--) {
const enemy = enemies[i];
enemy.y += speed * dt;
enemy.el.style.top = enemy.y + "px";
if (hitTest(playerRect, enemy.el.getBoundingClientRect())) {
endGame();
return;
}
if (enemy.y > game.clientHeight) {
enemy.el.remove();
enemies.splice(i, 1);
if (!enemy.passed) {
speed = Math.min(650, 250 + score * 0.05);
}
}
}
// Coins aktualisieren & Einsammeln prüfen
for (let i = itemCoins.length - 1; i >= 0; i--) {
const c = itemCoins[i];
c.y += speed * dt;
c.el.style.top = c.y + "px";
if (hitTest(playerRect, c.el.getBoundingClientRect())) {
c.el.remove();
itemCoins.splice(i, 1);
coins++;
updateHud();
continue;
}
if (c.y > game.clientHeight) {
c.el.remove();
itemCoins.splice(i, 1);
}
}
animationId = requestAnimationFrame(loop);
}
document.getElementById("start").addEventListener("click", startGame);
document.getElementById("shop-btn").addEventListener("click", showShop);
pauseBtn.addEventListener("click", togglePause);
document.addEventListener("keydown", e => {
const k = e.key.toLowerCase();
if (["arrowleft", "arrowright", " "].includes(k)) e.preventDefault();
if (k === "arrowleft" || k === "a") keys.left = true;
if (k === "arrowright" || k === "d") keys.right = true;
if (k === " " && !e.repeat) {
if (!running && message.style.display !== "none") {
startGame();
} else {
togglePause();
}
}
});
document.addEventListener("keyup", e => {
const k = e.key.toLowerCase();
if (k === "arrowleft" || k === "a") keys.left = false;
if (k === "arrowright" || k === "d") keys.right = false;
});
window.addEventListener("blur", () => {
keys.left = keys.right = false;
keyCooldown = false;
});
function bindControl(id, direction) {
const btn = document.getElementById(id);
btn.addEventListener("pointerdown", e => {
e.preventDefault();
btn.setPointerCapture(e.pointerId);
movePlayer(direction);
});
}
bindControl("left", -1);
bindControl("right", 1);
window.addEventListener("resize", () => {
x = targetX = getLaneCenterX(currentLane);
player.style.left = x + "px";
resetRoad();
});
resetRoad();
showMainMenu();
</script>
</body>
</html>Game Source: Neon Racer
Creator: BlazeKoala99
Libraries: none
Complexity: complex (798 lines, 18.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: neon-racer-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.