Neon Racer X
by SolarKnight80979 lines25.3 KB
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon Racer X</title>
<style>
:root {
--cyan: #00f5ff;
--pink: #ff2bd6;
--purple: #8b5cf6;
--bg: #070817;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
background: var(--bg);
color: white;
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
padding: 12px;
}
button {
border: 1px solid var(--cyan);
background: #10152b;
color: white;
padding: 11px 15px;
border-radius: 10px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 0 10px #00f5ff33;
}
button:hover { background: #1b2945; }
button:disabled { opacity: .4; cursor: not-allowed; }
#app {
width: 100%;
max-width: 440px;
text-align: center;
}
h1 {
color: var(--cyan);
text-shadow: 0 0 15px var(--cyan);
margin: 4px 0 12px;
font-size: clamp(25px, 7vw, 36px);
}
#topbar {
display: flex;
justify-content: space-between;
gap: 8px;
margin-bottom: 8px;
font-size: 14px;
}
.stat {
flex: 1;
background: #11152a;
padding: 10px 5px;
border: 1px solid #282e52;
border-radius: 10px;
}
#game {
width: 100%;
height: min(66vh, 610px);
min-height: 380px;
max-height: 610px;
position: relative;
overflow: hidden;
background: #090d19;
border: 2px solid var(--cyan);
border-radius: 14px;
box-shadow: 0 0 20px #00f5ff30;
touch-action: pan-y;
}
#road {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
#101424 0%, #101424 2%,
#1a2032 2%, #1a2032 98%,
#101424 98%, #101424 100%);
}
.lane-line {
position: absolute;
top: -100%;
height: 200%;
width: 3px;
background: repeating-linear-gradient(
to bottom, #65708c 0 28px, transparent 28px 55px
);
opacity: .55;
}
#line1 { left: 33.333%; }
#line2 { left: 66.666%; }
.car {
position: absolute;
width: 48px;
height: 76px;
border-radius: 12px 12px 8px 8px;
border: 2px solid white;
box-shadow: 0 0 13px currentColor;
transform: translateX(-50%);
z-index: 4;
}
.car::before {
content: "";
position: absolute;
width: 27px;
height: 25px;
top: 12px;
left: 50%;
transform: translateX(-50%);
background: #b9faff;
border-radius: 7px 7px 3px 3px;
border: 2px solid #e7ffff;
}
.car::after {
content: "";
position: absolute;
bottom: 8px;
left: 8px;
right: 8px;
height: 8px;
border-radius: 3px;
background: #ff365e;
box-shadow: 0 0 8px #ff365e;
}
#player {
bottom: 30px;
left: 50%;
background: #00bcd4;
color: var(--cyan);
}
.enemy {
background: #b72c75;
color: var(--pink);
}
.enemy::before { background: #ff9ddf; }
.pickup {
position: absolute;
z-index: 5;
transform: translate(-50%, -50%);
font-size: 27px;
filter: drop-shadow(0 0 8px currentColor);
}
#effects {
position: absolute;
z-index: 7;
top: 8px;
left: 8px;
right: 8px;
display: flex;
flex-wrap: wrap;
gap: 6px;
justify-content: center;
pointer-events: none;
}
.effect {
padding: 5px 8px;
background: #11182ddd;
border: 1px solid var(--cyan);
border-radius: 8px;
font-size: 12px;
}
#shieldRing {
position: absolute;
width: 64px;
height: 91px;
border: 3px solid #00f5ff;
box-shadow: 0 0 16px #00f5ff;
border-radius: 18px;
bottom: 22px;
left: 50%;
transform: translateX(-50%);
display: none;
z-index: 3;
pointer-events: none;
}
#overlay {
position: absolute;
inset: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
padding: 18px;
background: #050716eF;
}
.panel {
width: 100%;
max-height: 100%;
overflow-y: auto;
background: #10152b;
border: 1px solid var(--cyan);
border-radius: 15px;
padding: 18px;
box-shadow: 0 0 22px #00f5ff22;
}
.panel h2 { color: var(--cyan); margin: 0 0 12px; }
.panel p { line-height: 1.45; }
.hidden { display: none !important; }
.menu-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-top: 12px;
}
.menu-buttons button { width: 100%; }
.wide { grid-column: 1 / -1; }
#controls {
display: flex;
gap: 8px;
margin-top: 9px;
}
#controls button { flex: 1; font-size: 17px; }
#message {
min-height: 20px;
margin: 8px 0 0;
font-size: 13px;
color: #b9c8e8;
}
.upgrade-card {
border: 1px solid #343d62;
border-radius: 10px;
padding: 10px;
margin: 9px 0;
text-align: left;
background: #0a1022;
}
.upgrade-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.upgrade-card p {
color: #b9c8e8;
font-size: 13px;
margin: 7px 0;
}
.level-bar {
display: flex;
gap: 4px;
margin: 8px 0;
}
.level-bar span {
height: 7px;
flex: 1;
background: #272e49;
border-radius: 5px;
}
.level-bar span.on {
background: var(--cyan);
box-shadow: 0 0 7px #00f5ff;
}
.small { font-size: 12px; color: #b9c8e8; }
#footer { margin-top: 8px; color: #9da9c8; font-size: 12px; }
</style>
</head>
<body>
<div id="app">
<h1>NEON RACER X</h1>
<div id="topbar">
<div class="stat">🏆 Score<br><strong id="score">0</strong></div>
<div class="stat">🪙 Münzen<br><strong id="coins">0</strong></div>
<div class="stat">⚡ Bestwert<br><strong id="best">0</strong></div>
</div>
<div id="game">
<div id="road">
<div class="lane-line" id="line1"></div>
<div class="lane-line" id="line2"></div>
</div>
<div id="effects"></div>
<div id="shieldRing"></div>
<div class="car" id="player"></div>
<div id="overlay">
<div class="panel" id="mainMenu">
<h2>🏁 Neon Racer X</h2>
<p>Weiche Hindernissen aus, sammle Münzen und verbessere deine Power-ups mit Upgrades.</p>
<p>← → oder A/D zum Bewegen. Auf dem Handy kannst du wischen oder die Buttons benutzen.</p>
<div class="menu-buttons">
<button class="wide" id="startBtn">▶ Spielen</button>
<button id="upgradeBtn">⬆ Upgrades</button>
<button id="shopBtn">🎨 Skins</button>
<button class="wide" id="questBtn">📅 Tagesaufgaben</button>
</div>
</div>
<div class="panel hidden" id="upgradeMenu">
<h2>⬆ Upgrade-Shop</h2>
<p>Deine Münzen: 🪙 <strong id="upgradeCoins">0</strong></p>
<p class="small">Jede gekaufte Stufe verlängert Schild und Magnet. Die Stufen werden dauerhaft gespeichert.</p>
<div id="upgradeList"></div>
<button class="wide" id="upgradeBack">↩ Zurück</button>
</div>
<div class="panel hidden" id="shopMenu">
<h2>🎨 Skin-Shop</h2>
<p>Münzen: 🪙 <strong id="shopCoins">0</strong></p>
<div id="skinList"></div>
<button class="wide" id="shopBack">↩ Zurück</button>
</div>
<div class="panel hidden" id="questMenu">
<h2>📅 Tagesaufgaben</h2>
<div id="questList"></div>
<button class="wide" id="questBack">↩ Zurück</button>
</div>
<div class="panel hidden" id="pauseMenu">
<h2>⏸ Pause</h2>
<button class="wide" id="resumeBtn">Weiter spielen</button>
<button class="wide" id="pauseHomeBtn" style="margin-top:8px">Hauptmenü</button>
</div>
<div class="panel hidden" id="overMenu">
<h2>💥 Game Over</h2>
<p>Dein Score: <strong id="finalScore">0</strong></p>
<p>Gesammelte Münzen: <strong id="finalCoins">0</strong></p>
<button class="wide" id="againBtn">🔄 Nochmal spielen</button>
<button class="wide" id="overHomeBtn" style="margin-top:8px">Hauptmenü</button>
</div>
</div>
</div>
<div id="controls">
<button id="leftBtn">◀</button>
<button id="pauseBtn">Ⅱ Pause</button>
<button id="rightBtn">▶</button>
</div>
<div id="message">Sammle Münzen für deine Upgrades!</div>
<div id="footer">Schild: 🛡️ · Magnet: 🧲 · Münze: 🪙</div>
</div>
<script>
"use strict";
const $ = id => document.getElementById(id);
const game = $("game");
const player = $("player");
const overlay = $("overlay");
const UPGRADE_PRICES = [200, 500, 1000, 1500, 1500];
const UPGRADE_NAMES = [
"Stufe 1 – Power-up Plus",
"Stufe 2 – Power-up Pro",
"Stufe 3 – Power-up Ultra",
"Stufe 4 – Power-up Elite",
"Stufe 5 – Power-up Max"
];
const SKINS = [
{name:"Neon Cyan", color:"#00bcd4", price:0},
{name:"Hot Pink", color:"#ff2bd6", price:150},
{name:"Lime Flash", color:"#7dff4f", price:300},
{name:"Gold Rush", color:"#ffbf00", price:500}
];
function loadNumber(key, fallback=0) {
try {
const n = Number(localStorage.getItem(key));
return Number.isFinite(n) && n >= 0 ? n : fallback;
} catch { return fallback; }
}
function loadJSON(key, fallback) {
try {
const value = JSON.parse(localStorage.getItem(key));
return value ?? fallback;
} catch { return fallback; }
}
function save(key, value) {
try { localStorage.setItem(key, String(value)); } catch {}
}
let coins = loadNumber("nr_coins", 0);
let best = loadNumber("nr_best", 0);
let upgradeLevel = Math.min(5, Math.floor(loadNumber("nr_upgradeLevel", 0)));
let ownedSkins = loadJSON("nr_ownedSkins", [0]);
let selectedSkin = Math.min(SKINS.length-1, loadNumber("nr_selectedSkin", 0));
if (!Array.isArray(ownedSkins)) ownedSkins = [0];
if (!ownedSkins.includes(0)) ownedSkins.push(0);
let running = false;
let paused = false;
let gameOver = false;
let score = 0;
let runCoins = 0;
let elapsed = 0;
let lastTime = 0;
let spawnTimer = 0;
let coinTimer = 0;
let powerTimer = 0;
let lane = 1;
let enemies = [];
let pickups = [];
let roadOffset = 0;
let shieldTime = 0;
let magnetTime = 0;
let lastQuestSecond = 0;
let animationId = 0;
const daily = {
date: new Date().toDateString(),
coins: 0,
score: 0,
games: 0
};
const savedDaily = loadJSON("nr_daily", null);
if (savedDaily && savedDaily.date === daily.date) {
daily.coins = savedDaily.coins || 0;
daily.score = savedDaily.score || 0;
daily.games = savedDaily.games || 0;
}
function saveDaily() { save("nr_daily", JSON.stringify(daily)); }
const quests = [
{id:"coins", name:"Sammle 30 Münzen", target:30, reward:75},
{id:"score", name:"Erreiche 1000 Punkte", target:1000, reward:100},
{id:"games", name:"Spiele 3 Runden", target:3, reward:60}
];
let claimed = loadJSON("nr_claimed", {});
if (!claimed || typeof claimed !== "object") claimed = {};
function getQuestProgress(q) {
if (q.id === "coins") return daily.coins;
if (q.id === "score") return daily.score;
return daily.games;
}
function saveAll() {
save("nr_coins", coins);
save("nr_best", best);
save("nr_upgradeLevel", upgradeLevel);
save("nr_ownedSkins", JSON.stringify(ownedSkins));
save("nr_selectedSkin", selectedSkin);
save("nr_claimed", JSON.stringify(claimed));
saveDaily();
}
function setMessage(text) { $("message").textContent = text; }
function updateHUD() {
$("score").textContent = Math.floor(score);
$("coins").textContent = coins;
$("best").textContent = best;
}
function setPlayerSkin() {
player.style.background = SKINS[selectedSkin].color;
player.style.color = SKINS[selectedSkin].color;
}
function currentShieldDuration() {
return 7 + upgradeLevel * 3;
}
function currentMagnetDuration() {
return 8 + upgradeLevel * 3;
}
function laneX(index) {
return (index + 0.5) * game.clientWidth / 3;
}
function playerY() {
return game.clientHeight - 30 - player.offsetHeight / 2;
}
function setLane(index) {
lane = Math.max(0, Math.min(2, index));
player.style.left = laneX(lane) + "px";
$("shieldRing").style.left = laneX(lane) + "px";
}
function showPanel(panelId) {
overlay.classList.remove("hidden");
for (const el of overlay.querySelectorAll(".panel")) el.classList.add("hidden");
$(panelId).classList.remove("hidden");
}
function showMainMenu() {
running = false;
paused = false;
gameOver = false;
showPanel("mainMenu");
$("pauseBtn").textContent = "Ⅱ Pause";
setMessage("Sammle Münzen für deine Upgrades!");
cancelAnimationFrame(animationId);
}
function makeElement(className, text) {
const el = document.createElement("div");
el.className = className;
if (text) el.textContent = text;
game.appendChild(el);
return el;
}
/* ---------------- UPGRADE-SHOP ---------------- */
function renderUpgrades() {
$("upgradeCoins").textContent = coins;
const list = $("upgradeList");
list.innerHTML = "";
for (let i = 0; i < 5; i++) {
const level = i + 1;
const bought = upgradeLevel >= level;
const next = upgradeLevel + 1 === level;
const price = UPGRADE_PRICES[i];
const card = document.createElement("div");
card.className = "upgrade-card";
const head = document.createElement("div");
head.className = "upgrade-head";
const title = document.createElement("strong");
title.textContent = UPGRADE_NAMES[i];
const status = document.createElement("span");
status.className = "small";
status.textContent = bought ? "✓ Gekauft" : (next ? price + " 🪙" : "Gesperrt");
head.append(title, status);
card.appendChild(head);
const bar = document.createElement("div");
bar.className = "level-bar";
for (let j = 0; j < 5; j++) {
const segment = document.createElement("span");
if (j < level && upgradeLevel >= j + 1) segment.classList.add("on");
bar.appendChild(segment);
}
card.appendChild(bar);
const desc = document.createElement("p");
desc.textContent = "Schild: " + (7 + level * 3) +
" Sek. · Magnet: " + (8 + level * 3) + " Sek.";
card.appendChild(desc);
const button = document.createElement("button");
button.style.width = "100%";
if (bought) {
button.textContent = "Bereits gekauft";
button.disabled = true;
} else if (!next) {
button.textContent = "Erst Stufe " + (level - 1) + " kaufen";
button.disabled = true;
} else {
button.textContent = "⬆ Kaufen – " + price + " Münzen";
button.disabled = coins < price;
button.addEventListener("click", () => buyUpgrade(level));
}
card.appendChild(button);
list.appendChild(card);
}
if (upgradeLevel >= 5) {
const max = document.createElement("p");
max.style.color = "#00f5ff";
max.textContent = "⭐ Maximale Upgrade-Stufe erreicht!";
list.appendChild(max);
}
}
function buyUpgrade(level) {
if (level !== upgradeLevel + 1 || level < 1 || level > 5) return;
const price = UPGRADE_PRICES[level - 1];
if (coins < price) {
setMessage("Du hast nicht genug Münzen!");
return;
}
coins -= price;
upgradeLevel = level;
saveAll();
updateHUD();
renderUpgrades();
setMessage("Upgrade " + level + " gekauft! Schild und Magnet halten länger.");
}
/* ---------------- SKIN-SHOP ---------------- */
function renderSkins() {
$("shopCoins").textContent = coins;
const list = $("skinList");
list.innerHTML = "";
SKINS.forEach((skin, i) => {
const card = document.createElement("div");
card.className = "upgrade-card";
const head = document.createElement("div");
head.className = "upgrade-head";
const title = document.createElement("strong");
title.textContent = "🚗 " + skin.name;
title.style.color = skin.color;
const status = document.createElement("span");
status.className = "small";
status.textContent = ownedSkins.includes(i) ? (selectedSkin === i ? "Ausgewählt" : "Besitzt du") : skin.price + " 🪙";
head.append(title, status);
card.appendChild(head);
const button = document.createElement("button");
button.style.width = "100%";
if (ownedSkins.includes(i)) {
button.textContent = selectedSkin === i ? "Ausgewählt" : "Skin auswählen";
button.disabled = selectedSkin === i;
button.addEventListener("click", () => {
selectedSkin = i;
saveAll();
setPlayerSkin();
renderSkins();
});
} else {
button.textContent = "Kaufen – " + skin.price + " Münzen";
button.disabled = coins < skin.price;
button.addEventListener("click", () => {
if (coins < skin.price) return;
coins -= skin.price;
ownedSkins.push(i);
selectedSkin = i;
saveAll();
updateHUD();
setPlayerSkin();
renderSkins();
setMessage("Neuer Skin freigeschaltet!");
});
}
card.appendChild(button);
list.appendChild(card);
});
}
/* ---------------- TAGESAUFGABEN ---------------- */
function renderQuests() {
const list = $("questList");
list.innerHTML = "";
quests.forEach(q => {
const progress = Math.min(q.target, getQuestProgress(q));
const done = progress >= q.target;
const isClaimed = !!claimed[q.id];
const card = document.createElement("div");
card.className = "upgrade-card";
const title = document.createElement("strong");
title.textContent = q.name;
card.appendChild(title);
const desc = document.createElement("p");
desc.textContent = progress + " / " + q.target + " · Belohnung: " + q.reward + " Münzen";
card.appendChild(desc);
const button = document.createElement("button");
button.style.width = "100%";
button.textContent = isClaimed ? "Belohnung abgeholt" : done ? "Belohnung abholen" : "Noch nicht geschafft";
button.disabled = !done || isClaimed;
button.addEventListener("click", () => {
if (!done || claimed[q.id]) return;
claimed[q.id] = true;
coins += q.reward;
saveAll();
updateHUD();
renderQuests();
setMessage("Aufgabe abgeschlossen! +" + q.reward + " Münzen");
});
card.appendChild(button);
list.appendChild(card);
});
}
function addCoins(amount) {
coins += amount;
runCoins += amount;
daily.coins += amount;
saveAll();
updateHUD();
}
/* ---------------- SPIEL ---------------- */
function clearObjects() {
enemies.forEach(e => e.el.remove());
pickups.forEach(p => p.el.remove());
enemies = [];
pickups = [];
}
function resetGame() {
clearObjects();
score = 0;
runCoins = 0;
elapsed = 0;
spawnTimer = 0;
coinTimer = 0;
powerTimer = 0;
roadOffset = 0;
shieldTime = 0;
magnetTime = 0;
lastQuestSecond = 0;
lane = 1;
setLane(lane);
updateHUD();
$("effects").innerHTML = "";
$("shieldRing").style.display = "none";
setMessage("Viel Glück!");
}
function startGame() {
cancelAnimationFrame(animationId);
resetGame();
running = true;
paused = false;
gameOver = false;
overlay.classList.add("hidden");
lastTime = performance.now();
animationId = requestAnimationFrame(loop);
}
function spawnEnemy() {
const laneIndex = Math.floor(Math.random() * 3);
const el = makeElement("car enemy");
el.style.left = laneX(laneIndex) + "px";
el.style.top = "-90px";
enemies.push({el, lane:laneIndex, y:-90, passed:false});
}
function spawnPickup() {
const laneIndex = Math.floor(Math.random() * 3);
const roll = Math.random();
let type = "coin";
let symbol = "🪙";
if (roll > .91) {
type = "shield";
symbol = "🛡️";
} else if (roll > .82) {
type = "magnet";
symbol = "🧲";
}
const el = makeElement("pickup", symbol);
el.style.left = laneX(laneIndex) + "px";
el.style.top = "-25px";
pickups.push({el, lane:laneIndex, y:-25, type});
}
function activatePickup(type) {
if (type === "shield") {
shieldTime = currentShieldDuration();
setMessage("🛡️ Schild aktiviert: " + Math.ceil(shieldTime) + " Sekunden!");
} else if (type === "magnet") {
magnetTime = currentMagnetDuration();
setMessage("🧲 Magnet aktiviert: " + Math.ceil(magnetTime) + " Sekunden!");
}
}
function updateEffects() {
const effects = $("effects");
effects.innerHTML = "";
if (shieldTime > 0) {
const el = document.createElement("span");
el.className = "effect";
el.textContent = "🛡️ Schild " + Math.ceil(shieldTime) + "s";
effects.appendChild(el);
}
if (magnetTime > 0) {
const el = document.createElement("span");
el.className = "effect";
el.textContent = "🧲 Magnet " + Math.ceil(magnetTime) + "s";
effects.appendChild(el);
}
$("shieldRing").style.display = shieldTime > 0 ? "block" : "none";
}
function endGame() {
if (gameOver) return;
gameOver = true;
running = false;
cancelAnimationFrame(animationId);
best = Math.max(best, Math.floor(score));
daily.score = Math.max(daily.score, Math.floor(score));
daily.games += 1;
saveAll();
updateHUD();
$("finalScore").textContent = Math.floor(score);
$("finalCoins").textContent = runCoins;
renderQuests();
showPanel("overMenu");
}
function moveLane(direction) {
if (!running || paused) return;
setLane(lane + direction);
}
function togglePause() {
if (!running && !paused) return;
if (paused) {
paused = false;
overlay.classList.add("hidden");
lastTime = performance.now();
animationId = requestAnimationFrame(loop);
} else {
paused = true;
showPanel("pauseMenu");
$("pauseBtn").textContent = "▶ Weiter";
cancelAnimationFrame(animationId);
}
}
function collision(a, b) {
const ar = a.getBoundingClientRect();
const br = b.getBoundingClientRect();
return ar.left < br.right - 7 &&
ar.right > br.left + 7 &&
ar.top < br.bottom - 8 &&
ar.bottom > br.top + 8;
}
function loop(now) {
if (!running || paused || gameOver) return;
const dt = Math.min((now - lastTime) / 1000, .05);
lastTime = now;
elapsed += dt;
/* Die Schwierigkeit steigt langsam und ist begrenzt. */
const speed = Math.min(520, 230 + score * .018);
const spawnInterval = Math.max(.62, 1.05 - score * .000025);
roadOffset = (roadOffset + speed * dt) % 55;
$("line1").style.transform = "translateY(" + roadOffset + "px)";
$("line2").style.transform = "translateY(" + roadOffset + "px)";
score += dt * 10;
shieldTime = Math.max(0, shieldTime - dt);
magnetTime = Math.max(0, magnetTime - dt);
spawnTimer += dt;
coinTimer += dt;
powerTimer += dt;
if (spawnTimer >= spawnInterval) {
spawnTimer = 0;
spawnEnemy();
if (score > 1800 && Math.random() < .08) spawnEnemy();
}
if (coinTimer >= .85) {
coinTimer = 0;
spawnPickup();
}
if (powerTimer >= 5.5) {
powerTimer = 0;
if (Math.random() < .22) spawnPickup();
}
const pRect = player.getBoundingClientRect();
const playerCenterY = pRect.top + pRect.height / 2;
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 (collision(player, enemy.el)) {
if (shieldTime > 0) {
shieldTime = 0;
enemy.el.remove();
enemies.splice(i, 1);
setMessage("Schild hat den Zusammenstoß abgefangen!");
continue;
}
endGame();
return;
}
if (enemy.y > game.clientHeight + 100) {
enemy.el.remove();
enemies.splice(i, 1);
score += 5;
}
}
for (let i = pickups.length - 1; i >= 0; i--) {
const pickup = pickups[i];
pickup.y += speed * dt;
pickup.el.style.top = pickup.y + "px";
if (magnetTime > 0 && pickup.type === "coin") {
const targetX = laneX(lane);
const currentX = parseFloat(pickup.el.style.left);
const dx = targetX - currentX;
pickup.el.style.left = (currentX + dx * Math.min(1, dt * 5)) + "px";
pickup.lane = lane;
}
if (collision(player, pickup.el)) {
if (pickup.type === "coin") {
addCoins(1);
score += 15;
} else {
activatePickup(pickup.type);
}
pickup.el.remove();
pickups.splice(i, 1);
continue;
}
if (pickup.y > game.clientHeight + 40) {
pickup.el.remove();
pickups.splice(i, 1);
}
}
const wholeSecond = Math.floor(elapsed);
if (wholeSecond > lastQuestSecond) {
lastQuestSecond = wholeSecond;
}
updateEffects();
updateHUD();
save("nr_best", best);
animationId = requestAnimationFrame(loop);
}
/* ---------------- BEDIENUNG ---------------- */
$("startBtn").addEventListener("click", startGame);
$("againBtn").addEventListener("click", startGame);
$("upgradeBtn").addEventListener("click", () => {
renderUpgrades();
showPanel("upgradeMenu");
});
$("upgradeBack").addEventListener("click", () => showPanel("mainMenu"));
$("shopBtn").addEventListener("click", () => {
renderSkins();
showPanel("shopMenu");
});
$("shopBack").addEventListener("click", () => showPanel("mainMenu"));
$("questBtn").addEventListener("click", () => {
renderQuests();
showPanel("questMenu");
});
$("questBack").addEventListener("click", () => showPanel("mainMenu"));
$("pauseBtn").addEventListener("click", togglePause);
$("resumeBtn").addEventListener("click", togglePause);
$("pauseHomeBtn").addEventListener("click", showMainMenu);
$("overHomeBtn").addEventListener("click", showMainMenu);
$("leftBtn").addEventListener("click", () => moveLane(-1));
$("rightBtn").addEventListener("click", () => moveLane(1));
document.addEventListener("keydown", event => {
if (event.key === "ArrowLeft" || event.key.toLowerCase() === "a") moveLane(-1);
if (event.key === "ArrowRight" || event.key.toLowerCase() === "d") moveLane(1);
if (event.key === "Escape" || event.key.toLowerCase() === "p") togglePause();
if (event.key === " " && !running && !paused) startGame();
});
let touchStartX = null;
game.addEventListener("touchstart", event => {
touchStartX = event.changedTouches[0].clientX;
}, {passive:true});
game.addEventListener("touchend", event => {
if (touchStartX === null) return;
const diff = event.changedTouches[0].clientX - touchStartX;
if (Math.abs(diff) > 25) moveLane(diff > 0 ? 1 : -1);
touchStartX = null;
}, {passive:true});
window.addEventListener("resize", () => setLane(lane));
/* ---------------- START ---------------- */
setPlayerSkin();
setLane(1);
updateHUD();
renderUpgrades();
</script>
</body>
</html>
Game Source: Neon Racer X
Creator: SolarKnight80
Libraries: none
Complexity: complex (979 lines, 25.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-x-solarknight80" to link back to the original. Then publish at arcadelab.ai/publish.