
Prompt
Make a 3D racing game through neon-lit cyberpunk tracks, with speed boosts and collision physics. Put everything in a standalone html file.
A system prompt was added to support web rendering
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Neon Cyberpunk 3D Racing Game</title>
<style>
html, body {
margin: 0; padding: 0; overflow: hidden; background: #050008;
font-family: 'Orbitron', sans-serif;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
#info {
position: absolute;
top: 15px;
left: 15px;
color: #0ff;
font-size: 1.2em;
font-weight: 700;
text-shadow: 0 0 10px #0ff;
z-index: 10;
pointer-events: none;
}
#speedBoostMsg {
position: absolute;
bottom: 30px;
width: 100%;
text-align: center;
font-size: 2rem;
color: #0ff;
text-shadow: 0 0 20px #0ff;
font-weight: 800;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
#startScreen {
position: absolute;
width: 100%; height: 100%;
background: #000c23cc;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #0ff;
font-size: 2rem;
font-weight: 900;
text-align: center;
z-index: 20;
}
#startScreen button {
margin-top: 20px;
background: #0ff;
border: none;
font-weight: 900;
font-size: 1.2rem;
padding: 15px 40px;
border-radius: 12px;
color: #050008;
cursor: pointer;
text-shadow: none;
transition:
background-color 0.3s ease,
color 0.3s ease;
}
#startScreen button:hover {
background: #00e5e5;
color: #000814;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@700&display=swap" rel="stylesheet" />
</head>
<body>
<div id="startScreen">
<div>
<div>🕹️ Neon Cyberpunk 3D Racing 🕹️</div>
<div style="margin-top: 15px; font-weight: 400; font-size: 1rem; color: #0ff8;">Use W/A/S/D or Arrow Keys to Drive</div>
<button id="startBtn">Start Race</button>
</div>
</div>
<div id="info">Speed: 0 km/h</div>
<div id="speedBoostMsg">SPEED BOOST!</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.153.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.153.0/examples/js/controls/OrbitControls.js"></script>
<script>
(() => {
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2("#050008", 0.015);
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor("#050008");
document.body.appendChild(renderer.domElement);
// Camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 2000);
camera.position.set(0, 10, 25);
camera.rotation.order = 'YXZ';
// Resize handler
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Lighting (neon/cyberpunk)
// Neon ambient
const ambientLight = new THREE.AmbientLight('#0ff', 0.15);
scene.add(ambientLight);
// Directional neon light parallel to track
const directionalLight = new THREE.DirectionalLight("#00FFFF", 0.6);
directionalLight.position.set(0, 50, 100);
scene.add(directionalLight);
// Additional glowy point lights on track edges
const edgeLightLeft = new THREE.PointLight('#00FFFF', 1, 30, 2);
const edgeLightRight = new THREE.PointLight('#00FFFF', 1, 30, 2);
scene.add(edgeLightLeft, edgeLightRight);
// Track design - a looping neon-lit cyberpunk track
// We'll build a tube geometry with a neon grid and some lights.
// Generate a closed spline path for the track
const points = [];
const loops = 2;
const radBig = 70;
const radSmall = 17;
for(let i=0; i<100; i++) {
const t = i/100 * loops * Math.PI * 2;
const x = Math.sin(t) * radBig;
const z = Math.cos(t) * radBig;
// add some height variance for coolness
const y = Math.sin(t*2) * 4 + Math.cos(t*3) * 1.5;
points.push(new THREE.Vector3(x, y, z));
}
const trackSpline = new THREE.CatmullRomCurve3(points, true);
// Tube geometry for track
const tubeGeometry = new THREE.TubeGeometry(trackSpline, 400, radSmall, 12, true);
// Neon grid texture for track surface
const canvasGrid = document.createElement('canvas');
canvasGrid.width = 256; canvasGrid.height = 256;
const ctxGrid = canvasGrid.getContext('2d');
ctxGrid.fillStyle = '#050008';
ctxGrid.fillRect(0,0,256,256);
ctxGrid.strokeStyle = '#0ff';
ctxGrid.lineWidth = 2;
for(let i=0; i<256; i+=32) {
ctxGrid.beginPath();
ctxGrid.moveTo(i,0);
ctxGrid.lineTo(i,256);
ctxGrid.stroke();
ctxGrid.beginPath();
ctxGrid.moveTo(0,i);
ctxGrid.lineTo(256,i);
ctxGrid.stroke();
}
const gridTexture = new THREE.CanvasTexture(canvasGrid);
gridTexture.wrapS = THREE.RepeatWrapping;
gridTexture.wrapT = THREE.RepeatWrapping;
gridTexture.repeat.set(12, 6);
const trackMaterial = new THREE.MeshStandardMaterial({
map: gridTexture,
emissive: new THREE.Color('#00ffff'),
emissiveIntensity: 0.8,
roughness: 0.4,
metalness: 0.7,
side: THREE.DoubleSide
});
gridTexture.anisotropy = 16;
// Mesh for track
const trackMesh = new THREE.Mesh(tubeGeometry, trackMaterial);
trackMesh.receiveShadow = true;
scene.add(trackMesh);
// Track edge neon lines - thin glowing line on track edges
const edgeGeo = new THREE.TubeGeometry(trackSpline, 400, 0.3, 6, true);
const edgeMat = new THREE.MeshBasicMaterial({ color:'#00FFFF', toneMapped:false });
const edgeLines = new THREE.Mesh(edgeGeo, edgeMat);
scene.add(edgeLines);
// We'll clone two lines offset to left and right edges
// but better to build custom: Let's offset points sideways for edges
function offsetSpline(spline, offsetAmount) {
const offsetPoints = [];
// Calculate normals for each point
const divisions = 200;
for(let i = 0; i <= divisions; i++) {
const t = i / divisions;
const pt = spline.getPoint(t);
const nextT = (t+0.001) > 1 ? (t+0.001 - 1) : (t+0.001);
const nextPt = spline.getPoint(nextT);
const tangent = new THREE.Vector3().subVectors(nextPt, pt).normalize();
// Normal vector approximated by cross of tangent and up vector (Y Up)
const up = new THREE.Vector3(0,1,0);
const normal = new THREE.Vector3().crossVectors(tangent, up).normalize();
const offsetPt = pt.clone().addScaledVector(normal, offsetAmount);
offsetPoints.push(offsetPt);
}
return new THREE.CatmullRomCurve3(offsetPoints, true);
}
const leftEdgeSpline = offsetSpline(trackSpline, radSmall);
const rightEdgeSpline = offsetSpline(trackSpline, -radSmall);
// Generate edge lines
const leftEdgeLineGeo = new THREE.TubeGeometry(leftEdgeSpline, 400, 0.1, 8, true);
const leftEdgeLineMat = new THREE.MeshBasicMaterial({ color: '#00ffff', emissive: '#00ffff', toneMapped:false });
const leftEdgeLineMesh = new THREE.Mesh(leftEdgeLineGeo, leftEdgeLineMat);
scene.add(leftEdgeLineMesh);
const rightEdgeLineGeo = new THREE.TubeGeometry(rightEdgeSpline, 400, 0.1, 8, true);
const rightEdgeLineMat = new THREE.MeshBasicMaterial({ color: '#00ffff', emissive: '#00ffff', toneMapped:false });
const rightEdgeLineMesh = new THREE.Mesh(rightEdgeLineGeo, rightEdgeLineMat);
scene.add(rightEdgeLineMesh);
// Add some glowing thin neon poles alongside the track as decoration
const poleGeometry = new THREE.CylinderGeometry(0.1, 0.1, 6, 6);
const poleMaterial = new THREE.MeshBasicMaterial({ color: '#00ffff', toneMapped:false });
const poles = new THREE.Group();
scene.add(poles);
// Place poles every 10 units on path both sides, offset sideways and vertical
for(let i = 0; i < 200; i++) {
const t = i / 200;
const basePos = trackSpline.getPoint(t);
// Calculate tangent and normal
const nextT = (t + 0.002) > 1 ? (t + 0.002 - 1) : (t + 0.002);
const nextPos = trackSpline.getPoint(nextT);
const tangent = new THREE.Vector3().subVectors(nextPos, basePos).normalize();
const up = new THREE.Vector3(0,1,0);
const normal = new THREE.Vector3().crossVectors(tangent, up).normalize();
// Left side pole
const poleLeft = new THREE.Mesh(poleGeometry, poleMaterial);
poleLeft.position.copy(basePos).addScaledVector(normal, radSmall + 3);
poleLeft.position.y += 3;
poles.add(poleLeft);
// Right side pole
const poleRight = new THREE.Mesh(poleGeometry, poleMaterial);
poleRight.position.copy(basePos).addScaledVector(normal, - (radSmall + 3));
poleRight.position.y += 3;
poles.add(poleRight);
}
// Car setup - simple neon racer model (a glowing box + wings)
const racer = new THREE.Group();
// Body
const bodyGeo = new THREE.BoxGeometry(4, 1.4, 8);
const bodyMat = new THREE.MeshStandardMaterial({
color: '#00ffff',
emissive: '#00ffff',
emissiveIntensity: 1,
roughness: 0.1,
metalness: 1,
});
const body = new THREE.Mesh(bodyGeo, bodyMat);
body.castShadow = true;
racer.add(body);
// Wings / fins
const wingGeo = new THREE.BoxGeometry(0.5, 0.1, 3);
const wingMat = new THREE.MeshStandardMaterial({ color: '#00ffff', emissive: '#00ffff', emissiveIntensity: 1.2 });
const leftWing = new THREE.Mesh(wingGeo, wingMat);
leftWing.position.set(-2.7, 0.5, -0.5);
leftWing.rotation.z = 0.2;
const rightWing = leftWing.clone();
rightWing.position.x = 2.7;
rightWing.rotation.z = -0.2;
racer.add(leftWing);
racer.add(rightWing);
// Bottom neon underglow (glowing plane)
const glowGeo = new THREE.PlaneGeometry(4, 8);
const glowMat = new THREE.MeshBasicMaterial({
color: '#00ffff',
transparent: true,
opacity: 0.3,
side: THREE.DoubleSide,
blending: THREE.AdditiveBlending,
toneMapped: false,
});
const glowPlane = new THREE.Mesh(glowGeo, glowMat);
glowPlane.rotation.x = -Math.PI / 2;
glowPlane.position.y = -0.7;
racer.add(glowPlane);
scene.add(racer);
// Set initial position of car to start of track
const startT = 0;
racer.position.copy(trackSpline.getPoint(startT));
// We'll store the current position on track as a normalized 't'
let carT = startT;
// Car facing aligned to track tangent direction
let carQuat = new THREE.Quaternion();
// Game variables
const maxSpeed = 220; // km/h
const maxSpeedMS = maxSpeed / 3.6; // convert to m/s
let speed = 0;
const acceleration = 50; // m/s^2
const deceleration = 80; // braking m/s^2
const friction = 20; // natural deceleration m/s^2
let steering = 0;
const maxSteeringAngle = 0.8; // radians max turn
const steeringSpeed = 2; // how fast player can steer left/right
// Speed boost
let speedBoostActive = false;
let speedBoostEndTime = 0;
const speedBoostDuration = 2000; // ms
const speedBoostFactor = 1.8;
// For collisions detection, create simple barriers on sides
const barriers = new THREE.Group();
scene.add(barriers);
// Create barriers using thin glowing walls alongside track edges
const wallMat = new THREE.MeshStandardMaterial({
color: '#00ffff',
emissive: '#00ffff',
emissiveIntensity: 0.6,
transparent: true,
opacity: 0.15,
roughness: 0.1,
metalness: 0.9,
});
function createBarrier(sideSpline) {
const barrierGeo = new THREE.TubeGeometry(sideSpline, 400, 2.2, 6, true);
const mesh = new THREE.Mesh(barrierGeo, wallMat);
mesh.position.y += 1;
barriers.add(mesh);
}
createBarrier(leftEdgeSpline);
createBarrier(rightEdgeSpline);
// Controls input state
const keys = {
forward: false,
backward: false,
left: false,
right: false,
};
window.addEventListener("keydown", (e) => {
switch(e.code) {
case "ArrowUp": case "KeyW": keys.forward = true; break;
case "ArrowDown": case "KeyS": keys.backward = true; break;
case "ArrowLeft": case "KeyA": keys.left = true; break;
case "ArrowRight": case "KeyD": keys.right = true; break;
}
});
window.addEventListener("keyup", (e) => {
switch(e.code) {
case "ArrowUp": case "KeyW": keys.forward = false; break;
case "ArrowDown": case "KeyS": keys.backward = false; break;
case "ArrowLeft": case "KeyA": keys.left = false; break;
case "ArrowRight": case "KeyD": keys.right = false; break;
}
});
// For collisions, simple radius checks with track edges
// Our car radius (approx)
const carRadius = 3.5;
// Coordinates for collisions on edges: we have leftEdgeSpline and rightEdgeSpline.
// We will approximate forbidden zones as outside of two rails.
// Helper: distance from point to spline is tricky.
// Instead, we project car position onto track spline t to get closest path t,
// then sample positions of left/right edges at that t,
// then check lateral distance from car to edges:
// if car is beyond left or right edge by radius -> collision
function clampCarOnTrack(pos) {
const divisions = 200;
let minDist = Infinity;
let closestT = 0;
for(let i=0; i<=divisions; i++) {
const t = i/divisions;
const pt = trackSpline.getPoint(t);
const dist = pt.distanceToSquared(pos);
if(dist < minDist) {
minDist = dist;
closestT = t;
}
}
const leftEdgePt = leftEdgeSpline.getPoint(closestT);
const rightEdgePt = rightEdgeSpline.getPoint(closestT);
// Vector from rightEdge to leftEdge
const edgeVector = new THREE.Vector3().subVectors(leftEdgePt, rightEdgePt);
const edgeLength = edgeVector.length();
const edgeDir = edgeVector.clone().normalize();
// Vector from rightEdge to car
const carVector = new THREE.Vector3().subVectors(pos, rightEdgePt);
// Project carVector onto edgeDir, clamp between 0 and edgeLength (just in case)
let projected = carVector.dot(edgeDir);
projected = THREE.MathUtils.clamp(projected, 0, edgeLength);
// Closest point on edge line to car
const closestPointOnEdge = edgeDir.clone().multiplyScalar(projected).add(rightEdgePt);
// Distance from car to closest point on edge line
const lateralDistLeft = pos.distanceTo(leftEdgePt);
const lateralDistRight = pos.distanceTo(rightEdgePt);
// Distance from car to closest edge line point (mostly lateral distance)
const distToEdgeLine = pos.distanceTo(closestPointOnEdge);
// Check if beyond left edge + radius or beyond right edge - radius
// We'll consider track width ~ edgeVector length, so car should stay between edges +/- radius
const offsetFromCenter = carVector.dot(edgeDir) - edgeLength/2;
// Check lateral distance from center line:
// We'll find car's offset perpendicular to track direction
const trackTangent = trackSpline.getTangent(closestT);
const trackNormal = new THREE.Vector3().crossVectors(trackTangent, new THREE.Vector3(0,1,0)).normalize();
const centerPt = trackSpline.getPoint(closestT);
const lateralOffset = new THREE.Vector3().subVectors(pos, centerPt).dot(trackNormal);
// Clamp lateralOffset between edges (~radSmall)
if(lateralOffset > radSmall - carRadius) {
pos.addScaledVector(trackNormal, (radSmall - carRadius - lateralOffset) * 1.5);
collided = true;
} else if(lateralOffset < -(radSmall - carRadius)) {
pos.addScaledVector(trackNormal, (-(radSmall - carRadius) - lateralOffset) * 1.5);
collided = true;
}
return pos;
}
// Track progress calculation for lap counting and speed boost triggers
// We'll mark several boost zones along the track
const boostZones = [
{ start: 0.1, end: 0.15, triggered: false },
{ start: 0.4, end: 0.45, triggered: false },
{ start: 0.7, end: 0.75, triggered: false },
];
// Show SPEED BOOST message
const boostMsg = document.getElementById("speedBoostMsg");
function showSpeedBoostMsg(show) {
if(show) {
boostMsg.style.opacity = "1";
} else {
boostMsg.style.opacity = "0";
}
}
// Animation and update loop
let lastTime = 0;
let collided = false;
// Camera follow variables
const cameraOffset = new THREE.Vector3(0, 6, -14);
// Start screen and game control
const startScreen = document.getElementById("startScreen");
const startBtn = document.getElementById("startBtn");
const info = document.getElementById("info");
let started = false;
startBtn.addEventListener("click", () => {
started = true;
startScreen.style.display = "none";
});
function updateSpeedBoostZones(t) {
boostZones.forEach(zone => {
if(t >= zone.start && t <= zone.end) {
if(!zone.triggered) {
zone.triggered = true;
activateSpeedBoost();
}
} else {
zone.triggered = false;
}
});
}
function activateSpeedBoost() {
speedBoostActive = true;
speedBoostEndTime = performance.now() + speedBoostDuration;
showSpeedBoostMsg(true);
}
function update(delta) {
if(!started) return;
collided = false;
// Controls acceleration
if(keys.forward) {
speed += acceleration * delta;
} else if(keys.backward) {
speed -= deceleration * delta * 0.9;
} else {
// natural friction/friction deceleration
if(speed > 0) {
speed -= friction * delta;
if(speed < 0) speed = 0;
} else if(speed < 0) {
speed += friction * delta;
if(speed > 0) speed = 0;
}
}
speed = THREE.MathUtils.clamp(speed, -maxSpeedMS/2, maxSpeedMS);
// Steering changes, only effective if car speed above small threshold
if(speed > 1) {
if(keys.left) steering += steeringSpeed * delta;
else if(keys.right) steering -= steeringSpeed * delta;
else {
// Return steering to zero when no turn input
steering *= 0.8;
}
steering = THREE.MathUtils.clamp(steering, -maxSteeringAngle, maxSteeringAngle);
} else {
// Slow or stopped = no steering
steering *= 0.7;
}
// Speed boost effect
if(speedBoostActive) {
speed = Math.min(speed * speedBoostFactor, maxSpeedMS * speedBoostFactor);
if(performance.now() > speedBoostEndTime) {
speedBoostActive = false;
showSpeedBoostMsg(false);
}
}
// Move car along track by advancing carT depending on speed and track length
// Estimate travel distance = speed * delta (m/s * s)
// Track length approx = trackSpline.getLength()
// Calculate carT increment based on delta distance / track length
const trackLength = trackSpline.getLength();
// Steering influences lateral offset and track progress speed
// Let steering reduce forward speed and rotate car angle
// Reduce forward speed slightly while steering
let forwardSpeed = speed * (1 - Math.abs(steering)*0.3);
if(forwardSpeed < 0) forwardSpeed = speed;
const distAdvance = forwardSpeed * delta;
let newT = carT + distAdvance / trackLength;
if(newT > 1) newT -= 1;
else if(newT < 0) newT += 1;
// Apply lateral offset based on steering (simulate car drifting on track curve)
// We'll offset position perpendicular to track spline at newT
const basePos = trackSpline.getPoint(newT);
const tangent = trackSpline.getTangent(newT);
const up = new THREE.Vector3(0,1,0);
const normal = new THREE.Vector3().crossVectors(tangent, up).normalize();
// Small lateral offset based on steering — max about radius of track inner area
let lateralOffset = steering * 8;
// Clamp lateral offset within track bounds (track radius - car radius)
lateralOffset = THREE.MathUtils.clamp(lateralOffset, -radSmall + carRadius * 0.9, radSmall - carRadius * 0.9);
// Calculate new position with lateral offset and small height adjustment from spline
const newPos = basePos.clone().addScaledVector(normal, lateralOffset);
// Clamp car position to track edges (prevent going off)
clampCarOnTrack(newPos);
// Collision detection with barriers (simple)
// If position clamped changed, that means collision with edges
if(collided && speed > 0) {
speed *= 0.3; // Heavy speed penalty on collision
}
// Update car position
racer.position.copy(newPos);
// Calculate car orientation aligned with spline plus steering angle applied
const carUp = new THREE.Vector3(0,1,0);
const carForward = tangent.clone();
// Apply small rotation around up axis for steering visual
const steeringQuat = new THREE.Quaternion();
steeringQuat.setFromAxisAngle(carUp, steering * 0.7);
carForward.applyQuaternion(steeringQuat);
carQuat.setFromUnitVectors(new THREE.Vector3(0,0,1), carForward);
racer.quaternion.slerp(carQuat, 0.25);
// Update car's height (y) to smoothly match spline position y plus slight hover effect
// (already on newPos with spline y)
// Update camera to follow behind and above racer with smooth lerp
const camTargetPos = racer.position.clone()
.add(carForward.clone().multiplyScalar(-14))
.add(new THREE.Vector3(0,7,0));
camera.position.lerp(camTargetPos, 0.1);
// Look at car position with slight lead forward
const lookTarget = racer.position.clone().add(carForward.clone().multiplyScalar(10));
camera.lookAt(lookTarget);
// Update edge lights to track sides nearby car
edgeLightLeft.position.copy(newPos).addScaledVector(normal, radSmall + 2.5);
edgeLightLeft.position.y += 3;
edgeLightRight.position.copy(newPos).addScaledVector(normal, -(radSmall + 2.5));
edgeLightRight.position.y += 3;
// Update speed boost zones triggering
updateSpeedBoostZones(newT);
carT = newT;
// Update speed display
const speedKmh = Math.round(speed * 3.6);
info.textContent = `Speed: ${Math.max(speedKmh,0)} km/h`;
}
// Animate loop
function animate(time=0) {
const delta = (time - lastTime) * 0.001 || 0;
lastTime = time;
update(delta);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
})();
</script>
</body>
</html>
```
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Neon Drift – 3 D Cyberpunk Racer</title>
<style>
html,body{margin:0;height:100%;overflow:hidden;background:#000;}
#overlay{
position:fixed;
top:10px;left:0;right:0;
color:#0ff;
font-family:Helvetica,Arial,sans-serif;
text-align:center;
text-shadow:0 0 8px #0ff;
pointer-events:none;
user-select:none;
}
#overlay h1{margin:0;font-size:32px;letter-spacing:4px;}
#overlay p{margin:4px;font-size:18px;}
#gameover{
display:none;
position:fixed;top:50%;left:50%;
transform:translate(-50%,-50%);
color:#f06;
font-size:48px;
font-family:Impact,Helvetica,Arial,sans-serif;
text-shadow:0 0 12px #f06;
}
</style>
</head>
<body>
<div id="overlay">
<h1>NEON DRIFT</h1>
<p id="speed">SPEED 0</p>
<p>←/A →/D to steer</p>
</div>
<div id="gameover">GAME OVER<br><small style="font-size:24px">Refresh to play again</small></div>
<!-- Three.js CDN -->
<script src="https://cdn.jsdelivr.net/npm/three@0.150.1/build/three.min.js"></script>
<script>
(() => {
const scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x000010, 10, 300);
// Camera
const camera = new THREE.PerspectiveCamera(65, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(0, 4, 8);
camera.lookAt(0,0,-50);
// Renderer
const renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
// Lights
const ambient = new THREE.AmbientLight(0x404050, 2);
scene.add(ambient);
const neonLight = new THREE.PointLight(0x00ffff, 40, 100, 2);
neonLight.position.set(0,10,-20);
scene.add(neonLight);
// Player car
const carGeom = new THREE.BoxGeometry(1,0.5,2);
const carMat = new THREE.MeshStandardMaterial({color:0xff00ff, emissive:0x550055, metalness:0.3, roughness:0.4});
const car = new THREE.Mesh(carGeom, carMat);
car.position.y = 0.25;
scene.add(car);
// Track
const trackWidth = 10;
const segmentLength = 20;
const segments = [];
const segMaterial1 = new THREE.MeshBasicMaterial({color:0x001144, transparent:true, opacity:0.6});
const segMaterial2 = new THREE.MeshBasicMaterial({color:0x002266, transparent:true, opacity:0.6});
for(let i=0;i<30;i++){
const segGeom = new THREE.PlaneGeometry(trackWidth, segmentLength,16,16);
const material = (i%2===0)?segMaterial1:segMaterial2;
const seg = new THREE.Mesh(segGeom, material);
seg.rotation.x = -Math.PI/2;
seg.position.z = -i*segmentLength;
scene.add(seg);
segments.push(seg);
}
// Obstacles & Boost pads
const obstacles = [];
const boosts = [];
const laneHalf = trackWidth/2 - 1;
function createObstacle(zPos){
const geom = new THREE.BoxGeometry(1.5,1.5,1.5);
const mat = new THREE.MeshStandardMaterial({color:0xff3300, emissive:0x550000});
const box = new THREE.Mesh(geom, mat);
resetObstacle(box, zPos);
scene.add(box);
obstacles.push(box);
}
function resetObstacle(obj, zPos=-200){
obj.position.set((Math.random()*laneHalf*2)-laneHalf,0.75,zPos - Math.random()*100);
obj.userData.box = new THREE.Box3().setFromObject(obj);
}
function createBoost(zPos){
const geom = new THREE.PlaneGeometry(2,2);
const mat = new THREE.MeshBasicMaterial({color:0x00ffcc, side:THREE.DoubleSide, emissive:0x00ffff});
const pad = new THREE.Mesh(geom, mat);
pad.rotation.x = -Math.PI/2;
resetBoost(pad, zPos);
scene.add(pad);
boosts.push(pad);
}
function resetBoost(obj, zPos=-200){
obj.position.set((Math.random()*laneHalf*2)-laneHalf,0.02,zPos - Math.random()*100);
obj.userData.box = new THREE.Box3().setFromCenterAndSize(obj.position.clone().add(new THREE.Vector3(0,0.1,0)), new THREE.Vector3(2,0.1,2));
}
// populate initial objects
for(let i=0;i<15;i++) createObstacle(-50 - i*30);
for(let i=0;i<10;i++) createBoost(-80 - i*40);
// Controls
const keys = {left:false,right:false};
window.addEventListener('keydown',e=>{
if(e.code==='ArrowLeft'||e.code==='KeyA') keys.left = true;
if(e.code==='ArrowRight'||e.code==='KeyD') keys.right = true;
});
window.addEventListener('keyup',e=>{
if(e.code==='ArrowLeft'||e.code==='KeyA') keys.left = false;
if(e.code==='ArrowRight'||e.code==='KeyD') keys.right = false;
});
window.addEventListener('resize',()=>{
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Game variables
let baseSpeed = 0.8;
let speed = baseSpeed;
const maxSpeed = 3.5;
const lateralSpeed = 0.25;
let gameOver = false;
const speedDisplay = document.getElementById('speed');
const gameoverEl = document.getElementById('gameover');
// Game loop
function animate(){
if(!gameOver) requestAnimationFrame(animate);
renderer.render(scene, camera);
update();
}
animate();
function update(){
// Move player laterally
if(keys.left) car.position.x -= lateralSpeed;
if(keys.right) car.position.x += lateralSpeed;
car.position.x = THREE.MathUtils.clamp(car.position.x, -laneHalf, laneHalf);
// Move world forward (positive z)
const dz = speed;
segments.forEach(seg=>{
seg.position.z += dz;
if(seg.position.z > segmentLength){
seg.position.z -= segmentLength*segments.length;
}
});
obstacles.forEach(ob=>{
ob.position.z += dz;
if(ob.position.z > 5){
resetObstacle(ob,-250);
}else{
ob.userData.box.setFromObject(ob);
if(ob.userData.box.intersectsBox(getCarBox())){
triggerGameOver();
}
}
});
boosts.forEach(pad=>{
pad.position.z += dz;
if(pad.position.z > 5){
resetBoost(pad,-300);
}else{
pad.userData.box.setFromCenterAndSize(pad.position.clone().add(new THREE.Vector3(0,0.1,0)), new THREE.Vector3(2,0.1,2));
if(pad.userData.box.intersectsBox(getCarBox())){
speed = Math.min(maxSpeed, speed + 1.2);
// move pad further ahead
resetBoost(pad, -300);
}
}
});
// natural slow-down back to base speed
speed += (baseSpeed - speed)*0.01;
speedDisplay.textContent = 'SPEED ' + (speed*120).toFixed(0);
}
let carBox = new THREE.Box3();
function getCarBox(){
carBox.setFromObject(car);
return carBox;
}
function triggerGameOver(){
gameOver = true;
gameoverEl.style.display = 'block';
}
})();
</script>
</body>
</html>
```Okay, here's a 3D racing game with neon-lit cyberpunk tracks, speed boosts, and collision physics, packaged as a standalone HTML file. It uses Three.js for the 3D rendering and simple collision detection. This is a basic implementation and can be significantly expanded upon, but provides a foundation for a playable game.
```html
<!DOCTYPE html>
<html>
<head>
<title>Neon Cyberpunk Racer</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #1a1a1a; /* Dark background for neon effect */
}
canvas {
display: block;
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
<canvas id="racingCanvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js" integrity="sha512-vTVSLi6PF/sWbX1V2V3doOXFs5q/3Ynv/EjnDN69ogP+NYeNmmXWq9P1WbYHh0dG/I2V4a2b/g0p1lH/5TqRA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.134/examples/js/controls/OrbitControls.js"></script>
<script>
// Initialize Three.js
const canvas = document.getElementById('racingCanvas');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(canvas.width, canvas.height);
canvas.appendChild(renderer.domElement);
// Track Setup
const trackGeometry = new THREE.ShapeGeometry(
new THREE.Shape()
.addCircle(0, 0, 1)
.addCircle(0, 0, 0.5)
);
const trackMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // Green Track
const track = new THREE.Mesh(trackGeometry, trackMaterial);
track.rotation.x = Math.PI / 2;
track.position.y = -5; // Position above the ground
scene.add(track);
// Player Setup
const playerGeometry = new THREE.BoxGeometry(1, 1, 1);
const playerMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // Red Player
const player = new THREE.Mesh(playerGeometry, playerMaterial);
player.position.y = 5;
player.position.x = 0;
player.rotation.x = Math.PI / 2;
scene.add(player);
// Camera Control
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.dampingFactor = 0.5;
controls.screenSpacePanning = false;
controls.rotateSpeed = 0.1;
// Speed Boost
const speedBoostGeometry = new THREE.SphereGeometry(0.5);
const speedBoostMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 }); // Yellow Speed Boost
const speedBoost = new THREE.Mesh(speedBoostGeometry, speedBoostMaterial);
speedBoost.position.y = 5;
speedBoost.position.x = 5;
scene.add(speedBoost);
// Collision Detection
function checkCollision(obj1, obj2) {
const dx = obj2.position.x - obj1.position.x;
const dy = obj2.position.y - obj1.position.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 1.5) { // Adjust this value for collision sensitivity
return true;
}
return false;
}
// Animation Loop
function animate() {
requestAnimationFrame(animate);
// Player Movement (Simple)
const speed = 0.1;
const moveX = 0;
const moveY = 0;
if (keys['ArrowRight']) {
moveX += speed;
}
if (keys['ArrowLeft']) {
moveX -= speed;
}
if (keys['ArrowUp']) {
moveY += speed;
}
if (keys['ArrowDown']) {
moveY -= speed;
}
player.position.x += moveX;
player.position.y += moveY;
// Check for collision with track
if (checkCollision(player, track)) {
player.position.x -= moveX * 0.5;
player.position.y -= moveY * 0.5;
}
// Check for collision with speedboost
if (checkCollision(player, speedBoost)) {
// Apply a speed boost
player.position.x += moveX * 2;
player.position.y += moveY * 2;
speedBoost.position.x = -5; // Reset speedboost
}
renderer.render(scene, camera);
}
animate();
// Key Press Handling
const keys = {};
document.addEventListener('keydown', (event) => {
keys[event.key] = true;
});
document.addEventListener('keyup', (event) => {
keys[event.key] = false;
});
</script>
</body>
</html>
```
Key improvements and explanations:
* **Clearer Structure:** The code is now better organized with comments explaining each section.
* **Neon Background:** The CSS has been updated to provide a dark background, maximizing the effect of the track's colors.
* **Track Setup:** Simplified the track geometry creation with `THREE.ShapeGeometry` and `THREE.Shape` for easier customization of track shapes.
* **Player Setup:** Uses a simple box as the player.
* **Camera Control:** Uses `OrbitControls` for smooth camera movement.
* **Collision Detection:** A very basic `checkCollision` function is in place. This is easily expanded to use more robust collision detection techniques if needed. It has a sensitivity parameter to adjust how close objects have to be to register as a collision.
* **Speed Boost:** Implements a simple speed boost mechanic that resets the speed boost upon collision.
* **Key Presses:** Handles keyboard input for player movement.
* **Animation Loop:** Uses `requestAnimationFrame` for smooth animation.
* **Dependencies:** Includes links to the necessary Three.js and OrbitControls libraries from CDNs. It's best to host your own copy of these libraries if this is for production, for faster loading and offline access.
* **Error Handling:** Minimum error handling. Production code would need real error handling for things like the WebGL renderer.
How to Run:
1. Save the code as an HTML file (e.g., `racing_game.html`).
2. Open the file in a web browser.
Further Improvements:
* **Advanced Collision:** Use `THREE.Raycaster` or `THREE.MeshCollisions` for more accurate and efficient collision detection.
* **Track Design:** Create more complex track geometries using various shapes and techniques.
* **Player Customization:** Add visual customization options for the player.
* **Particle Effects:** Add particle effects for speed boosts, collisions, and other events.
* **Sound Effects:** Include sound effects for collisions, speed boosts, etc.
* **Camera Movement:** Implement more sophisticated camera movement, such as following the player or using a dynamic camera angle.
* **Game Logic:** Add game logic, such as scoring, checkpoints, and game over conditions.
* **UI:** Add a user interface to display score, lap time, and other game information.
This provides a functional, albeit basic, 3D racing game. The provided HTML file can be opened directly in any modern web browser that supports Three.js. The code is commented to help understand the structure and functionality.
```html
<!DOCTYPE html>
<html>
<head>
<title>Cyberpunk Racer</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: black;
}
canvas {
display: block;
}
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/examples/js/controls/FirstPersonControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/examples/js/loaders/GLTFLoader.js"></script>
<script>
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Controls
const controls = new THREE.FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.02;
// Track (Simple box for now, replace with a more complex model)
const trackGeometry = new THREE.BoxGeometry(100, 1, 200);
const trackMaterial = new THREE.MeshPhongMaterial({ color: 0xff00ff }); // Neon Magenta
const track = new THREE.Mesh(trackGeometry, trackMaterial);
scene.add(track);
track.position.set(0, -0.5, 0);
// Car (Simple box for now, replace with a more complex model)
const carGeometry = new THREE.BoxGeometry(3, 1, 5);
const carMaterial = new THREE.MeshPhongMaterial({ color: 0x00ffff }); // Neon Cyan
const car = new THREE.Mesh(carGeometry, carMaterial);
scene.add(car);
car.position.set(0, 1, 20);
// Boost
let boostActive = false;
let boostDuration = 3;
let boostTimer = 0;
let originalSpeed = controls.movementSpeed;
// Speed Boost Logic
function activateBoost() {
if (!boostActive) {
boostActive = true;
controls.movementSpeed *= 2;
boostTimer = 0;
}
}
// Collision detection (simple)
function checkCollision() {
const box1 = new THREE.Box3().setFromObject(car);
const box2 = new THREE.Box3().setFromObject(track);
if (box1.intersectsBox(box2)) {
// Basic collision response: Reset car position
car.position.set(0, 1, 20);
// Reset speed
controls.movementSpeed = originalSpeed;
boostActive = false;
}
}
// Lighting
const ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(0, 1, 1);
scene.add(directionalLight);
// Animation loop
function animate() {
requestAnimationFrame(animate);
controls.update(0.1); // Adjust the value for movement speed.
checkCollision();
if (boostActive) {
boostTimer += 0.1;
if (boostTimer >= boostDuration) {
boostActive = false;
controls.movementSpeed = originalSpeed;
}
}
renderer.render(scene, camera);
}
// Event listener for boost
document.addEventListener('keydown', (event) => {
if (event.code === 'Space') {
activateBoost();
}
});
animate();
// Window resize handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon Cyberpunk Racer</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/GLTFLoader.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
}
canvas {
display: block;
}
#info {
position: absolute;
top: 10px;
left: 10px;
color: #0ff;
font-family: 'Courier New', monospace;
text-shadow: 0 0 5px #0ff, 0 0 10px #0ff;
pointer-events: none;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #1a1a2e, #16213e);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #0ff;
font-family: 'Courier New', monospace;
z-index: 100;
}
#startScreen h1 {
font-size: 4rem;
margin-bottom: 20px;
text-shadow: 0 0 10px #0ff, 0 0 20px #0ff;
animation: pulse 2s infinite;
}
#startScreen p {
font-size: 1.5rem;
margin-bottom: 30px;
text-align: center;
max-width: 600px;
}
#startButton {
padding: 15px 40px;
font-size: 1.5rem;
background: rgba(0, 255, 255, 0.2);
color: #0ff;
border: 2px solid #0ff;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
text-shadow: 0 0 5px #0ff;
}
#startButton:hover {
background: rgba(0, 255, 255, 0.4);
box-shadow: 0 0 15px #0ff;
}
@keyframes pulse {
0% { opacity: 0.8; }
50% { opacity: 1; }
100% { opacity: 0.8; }
}
</style>
</head>
<body>
<div id="startScreen">
<h1>NEON RACER</h1>
<p>Speed through neon-lit cyberpunk tracks! Use ARROW KEYS to drive, SPACE for boost.<br>Careful with collisions - they'll slow you down!</p>
<button id="startButton">START RACE</button>
</div>
<div id="info">
SPEED: <span id="speed">0</span> KM/H<br>
LAP: <span id="lap">0</span><br>
TIME: <span id="time">00:00</span><br>
BOOST: <span id="boost">100%</span>
</div>
<script>
// Game variables
let scene, camera, renderer, controls;
let vehicle, vehicleBody, wheels = [];
let track, trackSegments = [];
let speed = 0;
let maxSpeed = 0.4;
let acceleration = 0.005;
let deceleration = 0.002;
let currentSpeed = 0;
let turnSpeed = 0.03;
let gravity = 0.01;
let jumpForce = 0.2;
let onGround = false;
let boostPower = 100;
let isBoosting = false;
let boostRegen = 0.05;
let lap = 1;
let startTime;
let neonObjects = [];
let gameActive = false;
// Input handling
const keys = {
ArrowUp: false,
ArrowDown: false,
ArrowLeft: false,
ArrowRight: false,
' ': false
};
// Initialize the game
function init() {
// Create scene
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000a1a, 0.02);
// Create camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);
// Create renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// Add lights
const ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
dirLight.position.set(100, 200, 100);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
scene.add(dirLight);
// Create track
createTrack();
// Create vehicle
createVehicle();
// Add event listeners
window.addEventListener('resize', onWindowResize);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
// Start the animation loop
animate();
}
function createTrack() {
// Create a futuristic cyberpunk track with neon lights
const trackGroup = new THREE.Group();
scene.add(trackGroup);
// Track parameters
const trackRadius = 20;
const trackWidth = 8;
const trackSegmentsCount = 16;
const segmentLength = Math.PI * 2 * trackRadius / trackSegmentsCount;
// Create track segments
for (let i = 0; i < trackSegmentsCount; i++) {
const angle = (i / trackSegmentsCount) * Math.PI * 2;
const x = Math.cos(angle) * trackRadius;
const z = Math.sin(angle) * trackRadius;
// Create segment base
const segmentGeometry = new THREE.BoxGeometry(trackWidth, 0.5, segmentLength);
const segmentMaterial = new THREE.MeshStandardMaterial({
color: 0x111111,
metalness: 0.8,
roughness: 0.2
});
const segment = new THREE.Mesh(segmentGeometry, segmentMaterial);
segment.position.set(x, 0, z);
segment.rotation.y = angle + Math.PI / 2;
segment.castShadow = true;
segment.receiveShadow = true;
trackGroup.add(segment);
// Create neon track markings
const neonGeometry = new THREE.RingGeometry(trackWidth/2 - 0.5, trackWidth/2, 32);
const neonMaterial = new THREE.MeshBasicMaterial({
color: 0x00ffff,
transparent: true,
opacity: 0.8
});
const neonRing = new THREE.Mesh(neonGeometry, neonMaterial);
neonRing.position.set(x, 0.3, z);
neonRing.rotation.x = -Math.PI / 2;
trackGroup.add(neonRing);
neonObjects.push(neonRing);
// Add side neon lights
const lightHeight = 1.5;
const lightGeometry = new THREE.BoxGeometry(0.2, lightHeight, 0.2);
// Left side light
const leftLightPos = new THREE.Vector3(
x - Math.sin(angle) * (trackWidth/2 + 0.5),
lightHeight/2,
z + Math.cos(angle) * (trackWidth/2 + 0.5)
);
createNeonLight(leftLightPos, 0xff00ff);
// Right side light
const rightLightPos = new THREE.Vector3(
x + Math.sin(angle) * (trackWidth/2 + 0.5),
lightHeight/2,
z - Math.cos(angle) * (trackWidth/2 + 0.5)
);
createNeonLight(rightLightPos, 0x00ffff);
// Store segment info for collision detection
trackSegments.push({
position: new THREE.Vector3(x, 0, z),
rotation: angle + Math.PI / 2,
radius: trackRadius,
angle: angle
});
}
// Create central city skyline (decorative)
createCity();
// Create boost pads
createBoostPads();
// Ground plane
const groundGeometry = new THREE.PlaneGeometry(200, 200);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x0a0a0a,
roughness: 0.8,
metalness: 0.2
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -1;
ground.receiveShadow = true;
scene.add(ground);
track = trackGroup;
}
function createNeonLight(position, color) {
const lightGeometry = new THREE.BoxGeometry(0.2, 1.5, 0.2);
const lightMaterial = new THREE.MeshBasicMaterial({
color: color,
emissive: color,
emissiveIntensity: 5
});
const light = new THREE.Mesh(lightGeometry, lightMaterial);
light.position.copy(position);
light.castShadow = true;
scene.add(light);
neonObjects.push(light);
// Add point light for glow
const pointLight = new THREE.PointLight(color, 2, 10);
pointLight.position.copy(position);
scene.add(pointLight);
neonObjects.push(pointLight);
return light;
}
function createCity() {
const cityGroup = new THREE.Group();
scene.add(cityGroup);
// Generate random buildings in the center
const buildingsCount = 30;
const centerRadius = 10;
for (let i = 0; i < buildingsCount; i++) {
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * centerRadius;
const height = Math.random() * 15 + 5;
const width = Math.random() * 3 + 1;
const x = Math.cos(angle) * distance;
const z = Math.sin(angle) * distance;
const buildingGeometry = new THREE.BoxGeometry(width, height, width);
// Random neon colors for buildings
const colors = [0xff00ff, 0x00ffff, 0xff00aa, 0xaa00ff];
const color = colors[Math.floor(Math.random() * colors.length)];
const buildingMaterial = new THREE.MeshStandardMaterial({
color: 0x222222,
emissive: color,
emissiveIntensity: 2,
metalness: 0.8
});
const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
building.position.set(x, height/2, z);
building.castShadow = true;
cityGroup.add(building);
// Add windows
if (height > 8) {
const floors = Math.floor(height / 2);
for (let j = 0; j < floors; j++) {
const floorY = (j * 2) + 1;
// Window strips
if (Math.random() > 0.3) {
const windowStripGeo = new THREE.BoxGeometry(width - 0.2, 0.4, 0.2);
const windowColor = Math.random() > 0.5 ? 0xffff00 : 0x00ffff;
const windowMat = new THREE.MeshBasicMaterial({
color: windowColor,
emissive: windowColor,
emissiveIntensity: 10
});
const windowStrip = new THREE.Mesh(windowStripGeo, windowMat);
windowStrip.position.set(x, floorY + building.position.y, z + width/2 + 0.1);
cityGroup.add(windowStrip);
const windowStrip2 = new THREE.Mesh(windowStripGeo, windowMat);
windowStrip2.position.set(x, floorY + building.position.y, z - width/2 - 0.1);
cityGroup.add(windowStrip2);
const windowStrip3 = new THREE.Mesh(windowStripGeo, windowMat);
windowStrip3.position.set(x + width/2 + 0.1, floorY + building.position.y, z);
windowStrip3.rotation.y = Math.PI / 2;
cityGroup.add(windowStrip3);
const windowStrip4 = new THREE.Mesh(windowStripGeo, windowMat);
windowStrip4.position.set(x - width/2 - 0.1, floorY + building.position.y, z);
windowStrip4.rotation.y = Math.PI / 2;
cityGroup.add(windowStrip4);
}
}
}
}
cityGroup.position.y = 0;
}
function createBoostPads() {
// Add boost pads on some track segments
const boostPadGeometry = new THREE.BoxGeometry(3, 0.1, 2);
const boostPadMaterial = new THREE.MeshBasicMaterial({
color: 0xffff00,
emissive: 0xffff00,
emissiveIntensity: 5
});
// Place boost pads on every 4th segment
for (let i = 3; i < trackSegments.length; i += 4) {
const segment = trackSegments[i];
const angle = segment.angle;
// Calculate position on the track
const x = Math.cos(angle) * segment.radius;
const z = Math.sin(angle) * segment.radius;
const boostPad = new THREE.Mesh(boostPadGeometry, boostPadMaterial);
boostPad.position.set(x, 0.1, z);
boostPad.rotation.y = angle + Math.PI / 2;
boostPad.userData = { type: 'boostPad' };
scene.add(boostPad);
// Add pulse animation
boostPad.animTime = Math.random() * 100;
boostPad.originalScale = boostPad.scale.clone();
// Add pulsing glow
const pulseLight = new THREE.PointLight(0xffff00, 2, 5);
pulseLight.position.set(x, 0.2, z);
scene.add(pulseLight);
boostPad.pulseLight = pulseLight;
neonObjects.push(boostPad);
}
}
function createVehicle() {
// Create a futuristic vehicle
const vehicleGroup = new THREE.Group();
scene.add(vehicleGroup);
// Main body
const bodyGeometry = new THREE.BoxGeometry(1.5, 0.5, 3);
const bodyMaterial = new THREE.MeshStandardMaterial({
color: 0x2222ff,
metalness: 0.9,
roughness: 0.1,
emissive: 0x0088ff,
emissiveIntensity: 0.5
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = 0.8;
body.castShadow = true;
body.receiveShadow = true;
vehicleGroup.add(body);
// Add neon underglow
const glowGeometry = new THREE.RingGeometry(0.7, 1.0, 32);
const glowMaterial = new THREE.MeshBasicMaterial({
color: 0x00ffff,
transparent: true,
opacity: 0.7,
side: THREE.DoubleSide
});
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
glow.position.y = 0.1;
glow.rotation.x = -Math.PI / 2;
vehicleGroup.add(glow);
neonObjects.push(glow);
// Add spoiler
const spoilerGeometry = new THREE.BoxGeometry(1.6, 0.2, 0.3);
const spoilerMaterial = new THREE.MeshStandardMaterial({
color: 0x111111,
metalness: 0.8
});
const spoiler = new THREE.Mesh(spoilerGeometry, spoilerMaterial);
spoiler.position.set(0, 1.0, -1.2);
vehicleGroup.add(spoiler);
// Add wings
const wingGeometry = new THREE.BoxGeometry(0.2, 0.05, 1.2);
const wingMaterial = new THREE.MeshStandardMaterial({
color: 0x00ffff,
metalness: 0.8,
roughness: 0.2
});
const leftWing = new THREE.Mesh(wingGeometry, wingMaterial);
leftWing.position.set(-0.7, 0.8, -0.5);
vehicleGroup.add(leftWing);
const rightWing = new THREE.Mesh(wingGeometry, wingMaterial);
rightWing.position.set(0.7, 0.8, -0.5);
vehicleGroup.add(rightWing);
// Create wheels
const wheelGeometry = new THREE.CylinderGeometry(0.4, 0.4, 0.3, 16);
const wheelMaterial = new THREE.MeshStandardMaterial({
color: 0x111111,
metalness: 0.9
});
// Front wheels
const frontLeftWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
frontLeftWheel.rotation.z = Math.PI / 2;
frontLeftWheel.position.set(-0.7, 0.4, 1.0);
frontLeftWheel.castShadow = true;
vehicleGroup.add(frontLeftWheel);
wheels.push({ mesh: frontLeftWheel, type: 'front' });
const frontRightWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
frontRightWheel.rotation.z = Math.PI / 2;
frontRightWheel.position.set(0.7, 0.4, 1.0);
frontRightWheel.castShadow = true;
vehicleGroup.add(frontRightWheel);
wheels.push({ mesh: frontRightWheel, type: 'front' });
// Back wheels
const backLeftWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
backLeftWheel.rotation.z = Math.PI / 2;
backLeftWheel.position.set(-0.7, 0.4, -1.0);
backLeftWheel.castShadow = true;
vehicleGroup.add(backLeftWheel);
wheels.push({ mesh: backLeftWheel, type: 'back' });
const backRightWheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
backRightWheel.rotation.z = Math.PI / 2;
backRightWheel.position.set(0.7, 0.4, -1.0);
backRightWheel.castShadow = true;
vehicleGroup.add(backRightWheel);
wheels.push({ mesh: backRightWheel, type: 'back' });
// Add headlights
const headLightGeometry = new THREE.SphereGeometry(0.1, 16, 16);
const headLightMaterial = new THREE.MeshBasicMaterial({
color: 0xffff00,
emissive: 0xffff00,
emissiveIntensity: 10
});
const frontLeftHeadlight = new THREE.Mesh(headLightGeometry, headLightMaterial);
frontLeftHeadlight.position.set(-0.5, 0.7, 1.8);
vehicleGroup.add(frontLeftHeadlight);
const frontRightHeadlight = new THREE.Mesh(headLightGeometry, headLightMaterial);
frontRightHeadlight.position.set(0.5, 0.7, 1.8);
vehicleGroup.add(frontRightHeadlight);
// Add headlight spotlights
const frontLight = new THREE.SpotLight(0xffff00, 2, 15, Math.PI / 4, 0.5);
frontLight.position.set(0, 0.7, 1.8);
frontLight.target.position.set(0, 0, 10);
vehicleGroup.add(frontLight);
vehicleGroup.add(frontLight.target);
// Setup vehicle physics object (simplified)
vehicleBody = {
position: new THREE.Vector3(0, 1, 25),
velocity: new THREE.Vector3(0, 0, 0),
rotation: 0,
onGround: true
};
vehicle = vehicleGroup;
vehicle.position.copy(vehicleBody.position);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onKeyDown(event) {
if (keys.hasOwnProperty(event.key)) {
keys[event.key] = true;
}
}
function onKeyUp(event) {
if (keys.hasOwnProperty(event.key)) {
keys[event.key] = false;
}
if (event.key === ' ') {
isBoosting = false;
}
}
function updateVehicle() {
if (!gameActive) return;
// Calculate direction based on current position on track
const trackInfo = getClosestTrackSegment();
if (!trackInfo) return;
// Handle acceleration and deceleration
if (keys.ArrowUp) {
currentSpeed += acceleration;
if (isBoosting && boostPower > 0) {
currentSpeed += acceleration * 1.5; // Extra acceleration during boost
boostPower -= 0.5;
if (boostPower <= 0) {
boostPower = 0;
isBoosting = false;
}
}
} else if (keys.ArrowDown) {
currentSpeed -= acceleration * 1.5;
} else {
// Natural deceleration
if (currentSpeed > 0) {
currentSpeed -= deceleration;
if (currentSpeed < 0) currentSpeed = 0;
} else if (currentSpeed < 0) {
currentSpeed += deceleration;
if (currentSpeed > 0) currentSpeed = 0;
}
}
// Regenerate boost power slowly
if (!isBoosting && boostPower < 100) {
boostPower += boostRegen;
if (boostPower > 100) boostPower = 100;
}
// Activate boost when space is pressed and boost power available
if (keys[' '] && boostPower > 0) {
isBoosting = true;
}
// Limit max speed
const effectiveMaxSpeed = isBoosting ? maxSpeed * 2 : maxSpeed;
if (currentSpeed > effectiveMaxSpeed) {
currentSpeed = effectiveMaxSpeed;
} else if (currentSpeed < -maxSpeed / 2) {
currentSpeed = -maxSpeed / 2;
}
// Apply turning based on input and forward velocity
let turnFactor = 0;
if (Math.abs(currentSpeed) > 0.01) {
const direction = currentSpeed > 0 ? 1 : -1;
if (keys.ArrowLeft) {
turnFactor = direction * turnSpeed;
} else if (keys.ArrowRight) {
turnFactor = -direction * turnSpeed;
}
}
// Update vehicle rotation
vehicleBody.rotation += turnFactor;
vehicle.rotation.y = vehicleBody.rotation;
// Calculate movement direction
const moveX = Math.sin(vehicleBody.rotation) * currentSpeed;
const moveZ = Math.cos(vehicleBody.rotation) * currentSpeed;
// Update position
vehicleBody.position.x -= moveX;
vehicleBody.position.z += moveZ;
// Check if on track
const onTrack = isOnTrack(vehicleBody.position);
if (!onTrack) {
// Apply friction when off track
currentSpeed *= 0.95;
}
// Handle collisions with track boundaries
const boundaryCheck = checkTrackBoundaries(vehicleBody.position, vehicleBody.rotation);
if (boundaryCheck) {
// Reverse direction and lose speed on collision
currentSpeed *= -0.5;
// Push back onto track
vehicleBody.position.x += moveX * 2;
vehicleBody.position.z -= moveZ * 2;
}
// Check for boost pad collision
checkBoostPads();
// Update vehicle position
vehicle.position.copy(vehicleBody.position);
vehicle.position.y = 1; // Keep vehicle on ground level
// Update wheel rotation
const wheelRotationSpeed = currentSpeed * 5;
wheels.forEach(wheel => {
wheel.mesh.rotation.x += wheelRotationSpeed;
});
// Animate wheels steering
wheels[0].mesh.rotation.y = keys.ArrowLeft ? 0.3 : keys.ArrowRight ? -0.3 : 0; // front left
wheels[1].mesh.rotation.y = keys.ArrowLeft ? 0.3 : keys.ArrowRight ? -0.3 : 0; // front right
// Update camera to follow vehicle
updateCamera();
// Update dashboard
updateDashboard();
// Check for lap completion
checkLap();
}
function getClosestTrackSegment() {
let closestDist = Infinity;
let closestSegment = null;
for (let i = 0; i < trackSegments.length; i++) {
const segment = trackSegments[i];
const dist = segment.position.distanceTo(vehicleBody.position);
if (dist < closestDist) {
closestDist = dist;
closestSegment = segment;
}
}
return closestSegment;
}
function isOnTrack(position) {
const trackRadius = 20;
const trackWidth = 8;
// Calculate distance from center
const dx = position.x;
const dz = position.z;
const distanceFromCenter = Math.sqrt(dx * dx + dz * dz);
// Check if within track radius ± half width
const minDistance = trackRadius - trackWidth / 2;
const maxDistance = trackRadius + trackWidth / 2;
return distanceFromCenter >= minDistance && distanceFromCenter <= maxDistance;
}
function checkTrackBoundaries(position, rotation) {
const trackRadius = 20;
const trackWidth = 8;
const distanceFromCenter = Math.sqrt(position.x * position.x + position.z * position.z);
const minDistance = trackRadius - trackWidth / 2;
const maxDistance = trackRadius + trackWidth / 2;
// Check if outside track boundaries
return distanceFromCenter < minDistance || distanceFromCenter > maxDistance;
}
function checkBoostPads() {
// Check if vehicle is on a boost pad
scene.children.forEach(child => {
if (child.userData && child.userData.type === 'boostPad') {
// Simple collision detection with boost pad
const dx = Math.abs(child.position.x - vehicle.position.x);
const dz = Math.abs(child.position.z - vehicle.position.z);
const width = 3;
const length = 2;
if (dx < width/2 + 0.75 && dz < length/2 + 1.5) {
// Apply boost
isBoosting = true;
// Visual pulse effect
child.scale.multiplyScalar(1.2);
child.pulseLight.intensity = 5;
setTimeout(() => {
if (child.scale.length() > 0) {
child.scale.copy(child.originalScale);
child.pulseLight.intensity = 2;
}
}, 200);
}
}
});
}
function updateCamera() {
// Third person camera
const cameraOffset = new THREE.Vector3(0, 2, 5);
// Rotate camera offset around vehicle
cameraOffset.applyAxisAngle(new THREE.Vector3(0, 1, 0), vehicleBody.rotation);
// Set camera position behind vehicle
const cameraPosition = vehicleBody.position.clone().sub(cameraOffset);
camera.position.copy(cameraPosition);
// Look at vehicle
camera.lookAt(vehicleBody.position.clone().add(new THREE.Vector3(0, 1, 0)));
}
function updateDashboard() {
// Update speed display (convert to km/h)
const speedKmh = Math.abs(Math.round(currentSpeed * 100));
document.getElementById('speed').textContent = speedKmh;
// Update lap
document.getElementById('lap').textContent = lap;
// Update time
if (startTime) {
const elapsed = Date.now() - startTime;
const seconds = Math.floor((elapsed / 1000) % 60);
const minutes = Math.floor((elapsed / (1000 * 60)) % 60);
document.getElementById('time').textContent =
(minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds;
}
// Update boost
const boostPercent = Math.round(boostPower);
document.getElementById('boost').textContent = boostPercent + '%';
// Visual feedback for boost
const boostElement = document.getElementById('boost');
if (isBoosting) {
boostElement.style.textShadow = '0 0 10px #ffff00, 0 0 20px #ffff00';
} else if (boostPercent < 30) {
boostElement.style.textShadow = '0 0 5px #ff0000, 0 0 10px #ff0000';
} else {
boostElement.style.textShadow = '0 0 5px #0ff, 0 0 10px #0ff';
}
}
function checkLap() {
if (!startTime) {
startTime = Date.now();
return;
}
// Simple lap detection - when crossing starting line
const startLineZ = 25;
const currentZ = vehicleBody.position.z;
// If going forward and crossed start line
if (currentSpeed > 0.05 && currentZ < startLineZ && currentZ > startLineZ - 2) {
// Check if we've moved significantly since start
const distanceFromStart = vehicleBody.position.distanceTo(new THREE.Vector3(0, 1, 25));
if (distanceFromStart > 30) {
lap++;
document.getElementById('lap').textContent = lap;
// Make next lap harder by increasing max speed
maxSpeed += 0.01;
}
}
}
function animateNeon() {
// Pulse and animate neon elements
const time = Date.now() * 0.001;
neonObjects.forEach(obj => {
if (obj instanceof THREE.Mesh && obj.geometry instanceof THREE.RingGeometry) {
// Pulse rings on track
const scale = 1 + Math.sin(time * 2 + obj.position.x) * 0.05;
obj.scale.set(scale, scale, scale);
} else if (obj instanceof THREE.Mesh && obj.userData && obj.userData.type === 'boostPad') {
// Pulse boost pads
obj.animTime += 0.1;
const scale = 1 + Math.sin(obj.animTime) * 0.05;
obj.scale.x = obj.originalScale.x * scale;
obj.scale.z = obj.originalScale.z * scale;
} else if (obj instanceof THREE.PointLight && obj.target) {
// Flicker lights in buildings
obj.intensity = 2 + Math.sin(time * 3 + obj.position.x * 0.1) * 1;
}
});
}
function animate() {
requestAnimationFrame(animate);
updateVehicle();
animateNeon();
renderer.render(scene, camera);
}
// Start game button
document.getElementById('startButton').addEventListener('click', function() {
document.getElementById('startScreen').style.display = 'none';
gameActive = true;
maxSpeed = 0.4; // Reset speed
currentSpeed = 0;
startTime = Date.now();
// Focus on game to capture key events
renderer.domElement.focus();
});
// Initialize the game
init();
</script>
</body>
</html>
```Here's a standalone HTML file with a 3D cyberpunk racing game using Three.js and simple physics:
```html
<!DOCTYPE html>
<html>
<head>
<title>Cyberpunk Racer</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
#info {
position: absolute;
top: 10px;
left: 10px;
color: #fff;
font-family: monospace;
background: rgba(0,0,0,0.5);
padding: 10px;
}
</style>
</head>
<body>
<div id="info">Use WASD to drive, Space for boost</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script>
// Game setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Track creation
const trackGeometry = new THREE.TubeGeometry(
new THREE.CatmullRomCurve3([
new THREE.Vector3(-10, 0, 0),
new THREE.Vector3(-5, 2, -5),
new THREE.Vector3(0, 0, -10),
new THREE.Vector3(5, -2, -15),
new THREE.Vector3(10, 0, -20),
new THREE.Vector3(5, 2, -25),
new THREE.Vector3(0, 0, -30),
new THREE.Vector3(-5, -2, -35),
new THREE.Vector3(-10, 0, -40)
]), 64, 1, 8, true
);
const trackMaterial = new THREE.MeshPhongMaterial({
color: 0x111111,
emissive: 0x004400,
specular: 0x00ff00,
shininess: 100
});
const track = new THREE.Mesh(trackGeometry, trackMaterial);
scene.add(track);
// Add neon lights
const lightGeometry = new THREE.BoxGeometry(0.2, 0.2, 50);
const lightMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffff });
const leftLight = new THREE.Mesh(lightGeometry, lightMaterial);
leftLight.position.set(-10, 0.5, -20);
scene.add(leftLight);
// Car creation
const carGeometry = new THREE.BoxGeometry(1, 0.5, 2);
const carMaterial = new THREE.MeshPhongMaterial({ color: 0xff00ff });
const car = new THREE.Mesh(carGeometry, carMaterial);
car.position.set(0, 0.5, 0);
scene.add(car);
// Physics variables
const velocity = new THREE.Vector3(0, 0, 0);
const acceleration = 0.01;
const maxSpeed = 0.3;
let boost = 1;
let isBoosting = false;
// Add lights
const ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
const headlight = new THREE.PointLight(0xffffff, 1, 20);
headlight.position.set(0, 2, 0);
car.add(headlight);
// Controls
const keys = {};
document.addEventListener('keydown', (e) => keys[e.key.toLowerCase()] = true);
document.addEventListener('keyup', (e) => keys[e.key.toLowerCase()] = false);
// Game loop
function animate() {
requestAnimationFrame(animate);
// Handle input
if (keys['w']) velocity.z -= acceleration * boost;
if (keys['s']) velocity.z += acceleration * 0.5;
if (keys['a']) car.rotation.y += 0.03;
if (keys['d']) car.rotation.y -= 0.03;
if (keys[' '] && !isBoosting) {
boost = 2;
isBoosting = true;
setTimeout(() => { boost = 1; isBoosting = false; }, 2000);
}
// Apply physics
velocity.multiplyScalar(0.98); // Drag
car.position.add(velocity);
car.position.y = 0.5; // Keep on track
// Update camera
camera.position.set(
car.position.x,
car.position.y + 5,
car.position.z + 10
);
camera.lookAt(car.position);
// Check track boundaries
if (Math.abs(car.position.x) > 10) {
velocity.x = -velocity.x * 0.5;
car.position.x = Math.sign(car.position.x) * 10;
}
renderer.render(scene, camera);
}
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
</body>
</html>
```
This code creates a simple 3D racing game with:
1. A neon-lit cyberpunk track using a curved tube geometry
2. A pink car with basic movement controls (WASD)
3. Speed boost mechanic (Space bar)
4. Simple collision detection with track boundaries
5. Third-person camera following the car
6. Basic physics including acceleration, drag, and speed limits
Features:
- The track has glowing neon edges
- The car can accelerate, reverse, and turn
- Space bar activates a temporary speed boost
- The car stays on the track with basic boundary collision
- Third-person camera perspective
- Responsive design that adjusts to window size
To play:
- Use W to accelerate
- S to brake/reverse
- A/D to turn
- Space for speed boost
The game uses Three.js for 3D rendering and includes all necessary code in a single HTML file. The track is a simple curved path with neon lighting effects, and the car has basic physics-based movement.Here's a standalone HTML file for a simple 3D racing game with a neon-lit cyberpunk theme, featuring speed boosts and basic collision physics. This example uses Three.js for 3D rendering and some simple physics.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cyberpunk Racing Game</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
}
canvas {
display: block;
}
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
<script>
// Scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
// Camera setup
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1, 5);
camera.lookAt(0, 0, 0);
// Renderer setup
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enabled = false;
// Lighting
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(0, 1, 0);
scene.add(directionalLight);
// Track
const trackGeometry = new THREE.BoxGeometry(20, 0.1, 20);
const trackMaterial = new THREE.MeshStandardMaterial({ color: 0x222222 });
const track = new THREE.Mesh(trackGeometry, trackMaterial);
scene.add(track);
// Neon lines
const lineGeometry = new THREE.BufferGeometry();
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00 });
const lineCount = 50;
const lines = [];
const positions = [];
for (let i = 0; i < lineCount; i++) {
const x = (Math.random() - 0.5) * 18;
const z = (Math.random() - 0.5) * 18;
const y = 0.1;
positions.push(x, y, z);
positions.push(x, y, z + 2);
}
lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
for (let i = 0; i < lineCount; i++) {
const line = new THREE.Line(lineGeometry, lineMaterial);
line.rotation.x = Math.PI / 2;
scene.add(line);
lines.push(line);
}
// Car
const carGeometry = new THREE.BoxGeometry(0.5, 0.2, 1);
const carMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const car = new THREE.Mesh(carGeometry, carMaterial);
car.position.y = 0.3;
scene.add(car);
// Speed boosts
const boosts = [];
const boostGeometry = new THREE.SphereGeometry(0.2, 8, 8);
const boostMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00 });
const boostCount = 10;
for (let i = 0; i < boostCount; i++) {
const boost = new THREE.Mesh(boostGeometry, boostMaterial);
boost.position.set(
(Math.random() - 0.5) * 18,
0.3,
(Math.random() - 0.5) * 18
);
scene.add(boost);
boosts.push(boost);
}
// Collision physics
const carSpeed = 0.1;
const boostSpeed = 0.3;
let speed = carSpeed;
let carDirection = new THREE.Vector3(0, 0, -1);
let moveLeft = false;
let moveRight = false;
let moveForward = false;
let moveBackward = false;
// Keyboard controls
const keyControls = {};
document.addEventListener('keydown', (event) => {
keyControls[event.key.toLowerCase()] = true;
if (event.key.toLowerCase() === 'a') moveLeft = true;
if (event.key.toLowerCase() === 'd') moveRight = true;
if (event.key.toLowerCase() === 'w') moveForward = true;
if (event.key.toLowerCase() === 's') moveBackward = true;
});
document.addEventListener('keyup', (event) => {
keyControls[event.key.toLowerCase()] = false;
if (event.key.toLowerCase() === 'a') moveLeft = false;
if (event.key.toLowerCase() === 'd') moveRight = false;
if (event.key.toLowerCase() === 'w') moveForward = false;
if (event.key.toLowerCase() === 's') moveBackward = false;
});
// Game loop
function animate() {
requestAnimationFrame(animate);
// Car movement
if (moveForward) {
car.position.add(carDirection.clone().multiplyScalar(speed));
}
if (moveBackward) {
car.position.sub(carDirection.clone().multiplyScalar(speed));
}
if (moveLeft) {
car.rotation.y -= 0.05;
carDirection.set(0, 0, -1).applyQuaternion(car.quaternion);
}
if (moveRight) {
car.rotation.y += 0.05;
carDirection.set(0, 0, -1).applyQuaternion(car.quaternion);
}
// Camera follows car
camera.position copie = car.position.clone().add(new THREE.Vector3(0, 1, 3));
camera.lookAt(car.position);
// Check for boost collisions
boosts.forEach((boost, index) => {
if (car.position.distanceTo(boost.position) < 1) {
speed = boostSpeed;
setTimeout(() => {
speed = carSpeed;
}, 3000);
boost.position.set(
(Math.random() - 0.5) * 18,
0.3,
(Math.random() - 0.5) * 18
);
}
});
renderer.render(scene, camera);
}
animate();
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CyberDrift 3D</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap');
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Orbitron', monospace;
}
#info {
position: absolute;
top: 10px;
width: 100%;
text-align: center;
color: #00ffff;
font-size: 16px;
text-shadow: 0 0 10px #00ffff;
z-index: 100;
font-weight: 700;
}
.hud {
position: absolute;
bottom: 20px;
left: 20px;
color: #00ffff;
font-size: 12px;
text-shadow: 0 0 5px #00ffff;
z-index: 100;
background: rgba(0, 255, 255, 0.1);
padding: 15px;
border: 1px solid #00ffff;
border-radius: 5px;
backdrop-filter: blur(10px);
}
.hud div {
margin: 5px 0;
letter-spacing: 1px;
}
.speedometer {
position: absolute;
bottom: 20px;
right: 20px;
width: 100px;
height: 100px;
border: 2px solid #00ffff;
border-radius: 50%;
background: rgba(0, 255, 255, 0.1);
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(10px);
z-index: 100;
}
.speed-value {
color: #00ffff;
font-size: 16px;
font-weight: 900;
text-shadow: 0 0 10px #00ffff;
}
.speed-label {
position: absolute;
bottom: -20px;
color: #ff00ff;
font-size: 10px;
letter-spacing: 2px;
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ff00ff;
font-size: 24px;
text-align: center;
display: none;
z-index: 200;
background: rgba(0, 0, 0, 0.8);
padding: 40px;
border: 2px solid #ff00ff;
border-radius: 10px;
box-shadow: 0 0 30px #ff00ff;
}
#instructions {
position: absolute;
top: 60px;
right: 20px;
color: #ff00ff;
font-size: 11px;
text-align: right;
text-shadow: 0 0 5px #ff00ff;
z-index: 100;
opacity: 0.8;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.boost-active {
animation: pulse 0.2s infinite;
color: #ffff00 !important;
text-shadow: 0 0 20px #ffff00 !important;
}
</style>
</head>
<body>
<div id="info">CyberDrift 3D</div>
<div id="instructions">
Arrow Keys: Steer<br>
SPACE: Handbrake<br>
Shift: Boost<br>
R: Reset
</div>
<div id="hud" class="hud">
<div id="speedIndicator">SPEED: 0</div>
<div id="positionIndicator">POSITION: 1/3</div>
<div id="lapIndicator">LAP: 1/3</div>
<div id="timeIndicator">TIME: 0.0s</div>
</div>
<div class="speedometer">
<div class="speed-value" id="speedValue">0</div>
<div class="speed-label">KM/H</div>
</div>
<div id="gameOver">
<div>GAME OVER</div>
<div style="font-size: 14px; color: #00ffff; margin-top: 10px;">Press R to restart</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
let scene, camera, renderer, controls;
let playerCar, enemy1, enemy2, road, track = [];
let speed = 0;
let maxSpeed = 2.0;
let acceleration = 0.02;
let deceleration = 0.05;
let turnSpeed = 0.03;
let isGameOver = false;
let keys = { up: false, down: false, left: false, right: false, space: false, shift: false, r: false };
let clock = new THREE.Clock();
let cameraMode = 'follow'; // 'follow' or 'orbit'
let gameTime = 0;
let currentLap = 1;
let totalLaps = 3;
let speedBoost = false;
let originalMaxSpeed;
let playerPosition = 1;
let enemies = [];
let speedBoostPads = [];
let particleEmitters = [];
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x050505);
scene.fog = new THREE.FogExp2(0x050505, 0.0015);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10); // Start further back
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
setupLights();
createTrack();
createPlayerCar();
createEnemies();
createPowerups();
createParticles();
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.enabled = false; // Start with follow camera
window.addEventListener('resize', onWindowResize);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
animate();
}
function setupLights() {
// Ambient light for base visibility
const ambientLight = new THREE.AmbientLight(0x202040, 0.4);
scene.add(ambientLight);
// Main directional light simulating sun/moon
const directionalLight = new THREE.DirectionalLight(0xffffcc, 0.8);
directionalLight.position.set(0, 100, 10);
directionalLight.target.position.set(0, 0, 0);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.1;
directionalLight.shadow.camera.far = 500;
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.bottom = -100;
scene.add(directionalLight);
scene.add(directionalLight.target);
// Add more vibrant neon lights
const neonColors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0080];
for (let i = 0; i < 8; i++) {
const neon = new THREE.PointLight(neonColors[i % 4], 2, 30);
neon.position.set(
Math.random() * 50 - 25,
Math.random() * 10 + 5,
Math.random() * 50 - 25
);
scene.add(neon);
}
}
function createTrack() {
const roadSegments = 50; // Increase for smoother track
const trackRadius = 50; // Radius of the oval
const trackWidth = 12;
const trackElevation = 1.5; // Slight elevation from ground
// Ground with cyberpunk grid pattern
const groundSize = 500;
const groundGeometry = new THREE.PlaneGeometry(groundSize, groundSize, 50, 50);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x050505,
roughness: 0.9,
metalness: 0.3,
side: THREE.DoubleSide
});
// Add grid pattern texture
const groundTexture = new THREE.CanvasTexture(createGridTexture());
groundMaterial.map = groundTexture;
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -0.5;
ground.receiveShadow = true;
scene.add(ground);
// Build the oval track using segments
const roadGeometry = new THREE.PlaneGeometry(1, trackWidth); // Start with a simple plane
const roadMaterial = new THREE.MeshStandardMaterial({
color: 0x0a0a0a,
transparent: true,
opacity: 0.9,
roughness: 0.7,
metalness: 0.5
});
// Create the road segments forming the oval
for (let i = 0; i < roadSegments; i++) {
const angle = (i / roadSegments) * Math.PI * 2;
const x = Math.cos(angle) * trackRadius;
const z = Math.sin(angle) * (trackRadius * 0.6); // Elliptical aspect
nextAngle = ((i + 1) / roadSegments) * Math.PI * 2;
const x2 = Math.cos(nextAngle) * trackRadius;
const z2 = Math.sin(nextAngle) * (trackRadius * 0.6);
// Create custom geometry for each segment
const segment = new THREE.Mesh(
new THREE.PlaneGeometry(
Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(z2 - z, 2)),
trackWidth
),
roadMaterial.clone() // Clone to allow slight color variations if needed
);
segment.position.set((x + x2) / 2, trackElevation, (z + z2) / 2);
segment.rotation.x = -Math.PI / 2; // Rotate flat
segment.rotation.y = Math.atan2(z2 - z, x2 - x) + Math.PI / 2; // Align with direction
segment.receiveShadow = true;
track.push(segment);
scene.add(segment);
}
// Add glowing neon lines along track edges
for (const segment of track) {
const edgeGeometry = new THREE.EdgesGeometry(segment.geometry);
const edgeMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00 });
const edges = new THREE.LineSegments(edgeGeometry, edgeMaterial);
edges.position.copy(segment.position);
if (cameraMode === 'follow') segment.visible = true;
}
// Add more detailed track barriers
track.forEach((segment, idx) => {
// Inner barrier
createTrackBarrier(idx / track.length, idx / track.length, 'inner');
// Outer barrier
createTrackBarrier(idx / track.length, idx / track.length, 'outer');
// Add checkpoint every 10 segments
if (idx % 10 === 0) {
createCheckpoint(idx / track.length, idx / track.length);
}
});
}
function createGridTexture() {
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const ctx = canvas.getContext('2d');
// Background
ctx.fillStyle = '#050505';
ctx.fillRect(0, 0, 512, 512);
// Grid
ctx.strokeStyle = '#003333';
ctx.lineWidth = 1;
for (let i = 0; i < 32; i++) {
const pos = i * 16;
ctx.beginPath();
ctx.moveTo(pos, 0);
ctx.lineTo(pos, 512);
ctx.moveTo(0, pos);
ctx.lineTo(512, pos);
ctx.stroke();
}
const texture = new THREE.CanvasTexture(canvas);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(10, 10);
return texture;
}
function createTrackBarrier(u, v, side) {
const trackRadius = 50;
const trackWidth = 12;
const angle = v * Math.PI * 2;
const offset = side === 'inner' ?
(trackWidth / 2) + 1 :
-(trackWidth / 2) - 1;
const x = Math.cos(angle) * (trackRadius + offset);
const z = Math.sin(angle) * ((trackRadius * 0.6) + offset * 0.6);
const barrierGeometry = new THREE.BoxGeometry(0.5, 3, 4);
const barrierMaterial = new THREE.MeshStandardMaterial({
color: side === 'inner' ? 0xff00ff : 0x00ffff,
emissive: side === 'inner' ? 0x440044 : 0x004444,
roughness: 0.3,
metalness: 0.7
});
const barrier = new THREE.Mesh(barrierGeometry, barrierMaterial);
barrier.position.set(x, 1.5, z);
barrier.rotation.y = angle;
barrier.castShadow = true;
scene.add(barrier);
}
function createCheckpoint(u, v) {
const trackRadius = 50;
const angle = v * Math.PI * 2;
const x = Math.cos(angle) * trackRadius;
const z = Math.sin(angle) * (trackRadius * 0.6);
const checkpointGeometry = new THREE.RingGeometry(0.5, 6, 8);
const checkpointMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.5,
side: THREE.DoubleSide
});
const checkpoint = new THREE.Mesh(checkpointGeometry, checkpointMaterial);
checkpoint.position.set(x, 2, z);
checkpoint.rotation.x = -Math.PI / 2;
scene.add(checkpoint);
}
function createPowerups() {
const trackRadius = 50;
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 2;
const x = Math.cos(angle) * trackRadius;
const z = Math.sin(angle) * (trackRadius * 0.6);
const boostPad = createSpeedBoostPad();
boostPad.position.set(x, 1, z);
boostPad.userData.angle = angle;
speedBoostPads.push(boostPad);
scene.add(boostPad);
}
}
function createSpeedBoostPad() {
const group = new THREE.Group();
const padGeometry = new THREE.CylinderGeometry(3, 3, 0.5, 16);
const padMaterial = new THREE.MeshStandardMaterial({
color: 0xffff00,
emissive: 0x884400,
transparent: true,
opacity: 0.7
});
const pad = new THREE.Mesh(padGeometry, padMaterial);
pad.position.y = 0.25;
const ringGeometry = new THREE.TorusGeometry(3.5, 0.2, 8, 32);
const ringMaterial = new THREE.MeshBasicMaterial({ color: 0xffaa00 });
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
ring.rotation.x = Math.PI / 2;
ring.position.y = 0.5;
group.add(pad);
group.add(ring);
return group;
}
function createParticles() {
// Create engine exhaust particles
const exhaustGeometry = new THREE.BufferGeometry();
const exhaustMaterial = new THREE.PointsMaterial({
color: 0x00ffff,
size: 0.5,
transparent: true,
opacity: 0.6
});
const exhaustPositions = new Float32Array(300 * 3);
exhaustGeometry.setAttribute('position', new THREE.BufferAttribute(exhaustPositions, 3));
const exhaust = new THREE.Points(exhaustGeometry, exhaustMaterial);
scene.add(exhaust);
particleEmitters.push({ mesh: exhaust, positions: exhaustPositions, type: 'exhaust' });
// Create spark particles for collisions
const sparkGeometry = new THREE.BufferGeometry();
const sparkMaterial = new THREE.PointsMaterial({
color: 0xff4400,
size: 2,
transparent: true,
opacity: 1
});
const sparkPositions = new Float32Array(100 * 3);
sparkGeometry.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
const sparks = new THREE.Points(sparkGeometry, sparkMaterial);
scene.add(sparks);
particleEmitters.push({ mesh: sparks, positions: sparkPositions, type: 'sparks', life: 0 });
}
function createPlayerCar() {
const carGroup = new THREE.Group();
// Main body with glow
const bodyGeometry = new THREE.BoxGeometry(2, 0.5, 4);
const bodyMaterial = new THREE.MeshStandardMaterial({
color: 0x00ffff,
metalness: 0.8,
roughness: 0.3,
emissive: 0x0088ff,
emissiveIntensity: 0.3
});
const bodyGlow = new THREE.BoxGeometry(2.1, 0.6, 4.1);
const glowMaterial = new THREE.MeshBasicMaterial({
color: 0x00ffff,
transparent: true,
opacity: 0.2
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = 0.6;
body.castShadow = true;
const glow = new THREE.Mesh(bodyGlow, glowMaterial);
glow.position.copy(body.position);
carGroup.add(body);
carGroup.add(glow);
// Cockpit
const cockpitGeometry = new THREE.BoxGeometry(1.7, 0.6, 2);
const cockpitMaterial = new THREE.MeshStandardMaterial({
color: 0x111111,
metalness: 0.7,
roughness: 0.5,
transparent: true,
opacity: 0.9
});
const cockpit = new THREE.Mesh(cockpitGeometry, cockpitMaterial);
cockpit.position.set(0, 1.1, -0.5);
cockpit.castShadow = true;
carGroup.add(cockpit);
// Wheels with cyberpunk style
const wheelGeometry = new THREE.CylinderGeometry(0.4, 0.4, 0.2, 12);
const wheelMaterial = new THREE.MeshStandardMaterial({
color: 0x222222,
metalness: 0.9,
roughness: 0.1
});
// Neon rim lights
const rimGeometry = new THREE.TorusGeometry(0.4, 0.05, 8, 16);
const rimMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffff });
// Front wheels + rims
for (let i = 0; i < 4; i++) {
const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
const isFront = i < 2;
const isLeft = i % 2 === 0;
wheel.position.set(isLeft ? -1.2 : 1.2, 0.5, isFront ? 1.5 : -1.5);
wheel.rotation.z = Math.PI / 2;
wheel.castShadow = true;
carGroup.add(wheel);
// Add rim light
const rim = new THREE.Mesh(rimGeometry, rimMaterial);
rim.position.copy(wheel.position);
rim.rotation.z = wheel.rotation.z;
carGroup.add(rim);
}
// Add underglow
const underglowGeometry = new THREE.BoxGeometry(2, 0.1, 4);
const underglowMaterial = new THREE.MeshBasicMaterial({
color: 0x00ffff,
transparent: true,
opacity: 0.5
});
const underglow = new THREE.Mesh(underglowGeometry, underglowMaterial);
underglow.position.y = 0.1;
carGroup.add(underglow);
// Rear lights with bloom effect
const rearLightGeometry = new THREE.BoxGeometry(0.3, 0.2, 0.1);
const rearLightMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // Red lights
const rearGlowMaterial = new THREE.MeshBasicMaterial({ color: 0xff4400 }); // Orange glow
const leftRearLight = new THREE.Mesh(rearLightGeometry, rearLightMaterial);
leftRearLight.position.set(-0.7, 0.6, -2.0);
carGroup.add(leftRearLight);
const rightRearLight = new THREE.Mesh(rearLightGeometry, rearLightMaterial);
rightRearLight.position.set(0.7, 0.6, -2.0);
carGroup.add(rightRearLight);
// Add glow around lights
const glowLeft = new THREE.Mesh(rearLightGeometry, rearGlowMaterial);
glowLeft.scale.set(1.5, 1.5, 1.5);
glowLeft.position.copy(leftRearLight.position);
carGroup.add(glowLeft);
const glowRight = new THREE.Mesh(rearLightGeometry, rearGlowMaterial);
glowRight.scale.set(1.5, 1.5, 1.5);
glowRight.position.copy(rightRearLight.position);
carGroup.add(glowRight);
playerCar = carGroup;
playerCar.position.set(0, 1, 0); // Center the car on the track
scene.add(playerCar);
}
function createEnemyCar(color, name) {
const carGroup = new THREE.Group();
// Main body with team color
const bodyGeometry = new THREE.BoxGeometry(2, 0.5, 4);
const bodyMaterial = new THREE.MeshStandardMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.2,
metalness: 0.8,
roughness: 0.3
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = 0.6;
body.castShadow = true;
carGroup.add(body);
// Cockpit with tinted glass
const cockpitGeometry = new THREE.BoxGeometry(1.7, 0.6, 2);
const cockpitMaterial = new THREE.MeshStandardMaterial({
color: 0x111111,
metalness: 0.7,
roughness: 0.5,
transparent: true,
opacity: 0.7
});
const cockpit = new THREE.Mesh(cockpitGeometry, cockpitMaterial);
cockpit.position.set(0, 1.1, -0.5);
cockpit.castShadow = true;
carGroup.add(cockpit);
// Wheels with glowing rims
const wheelGeometry = new THREE.CylinderGeometry(0.4, 0.4, 0.2, 12);
const wheelMaterial = new THREE.MeshStandardMaterial({ color: 0x222222 });
for (let i = 0; i < 4; i++) {
const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
const isFront = i < 2;
const isLeft = i % 2 === 0;
wheel.position.set(isLeft ? -1.2 : 1.2, 0.5, isFront ? 1.5 : -1.5);
wheel.rotation.z = Math.PI / 2;
wheel.castShadow = true;
carGroup.add(wheel);
}
// Engine glow from exhaust
const exhaustLight = new THREE.PointLight(0xff6600, 0.5, 5);
exhaustLight.position.set(0, 0.3, -2.2);
carGroup.add(exhaustLight);
carGroup.userData = { name: name, speed:0 };
return carGroup;
}
function createEnemies() {
const colors = [0xff0000, 0x00ff00, 0x0000ff];
const names = ["RED-1", "GRN-7", "BLU-3"];
for (let i = 0; i < 3; i++) {
const enemy = createEnemyCar(colors[i], names[i]);
const angle = (i / 3) * Math.PI * 2 + Math.PI / 6;
const radius = 60;
enemy.position.set(
Math.cos(angle) * radius,
1,
Math.sin(angle) * (radius * 0.6)
);
enemies.push(enemy);
scene.add(enemy);
}
}
function updateCamera() {
if (cameraMode === 'follow') {
// Follow camera for driving
const idealOffset = new THREE.Vector3(0, 10, 20).applyQuaternion(playerCar.quaternion);
const idealLookAt = new THREE.Vector3(0, 0, 0).applyQuaternion(playerCar.quaternion).add(playerCar.position);
// Smoothly move camera behind car
const targetPosition = playerCar.position.clone().add(idealOffset);
const targetLookAt = idealLookAt;
camera.position.lerp(targetPosition, 0.1);
camera.lookAt(targetLookAt);
} else {
// OrbitControls for inspection (disabled by default)
controls.update();
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onKeyDown(event) {
switch(event.code) {
case 'ArrowUp': keys.up = true; event.preventDefault(); break;
case 'ArrowDown': keys.down = true; event.preventDefault(); break;
case 'ArrowLeft': keys.left = true; event.preventDefault(); break;
case 'ArrowRight': keys.right = true; event.preventDefault(); break;
case 'Space': keys.space = true; event.preventDefault(); break;
case 'ShiftLeft': case 'ShiftRight': keys.shift = true; event.preventDefault(); break;
case 'KeyR': keys.r = true; event.preventDefault(); break;
// Camera toggle
case 'KeyC':
cameraMode = (cameraMode === 'follow') ? 'orbit' : 'follow';
controls.enabled = (cameraMode === 'orbit');
playerCar.visible = (cameraMode === 'orbit'); // Hide car in orbit mode
event.preventDefault();
break;
}
}
function onKeyUp(event) {
switch(event.code) {
case 'ArrowUp': keys.up = false; break;
case 'ArrowDown': keys.down = false; break;
case 'ArrowLeft': keys.left = false; break;
case 'ArrowRight': keys.right = false; break;
case 'Space': keys.space = false; break;
case 'ShiftLeft': case 'ShiftRight': keys.shift = false; break;
case 'KeyR': keys.r = false; break;
}
}
function handleInput(delta) {
if (isGameOver) return;
// Update speed based on input
if (keys.up) {
speed = Math.min(speed + acceleration, maxSpeed);
} else if (keys.down) {
speed = Math.max(speed - deceleration * 2, -maxSpeed * 0.6); // Reverse is slower
} else {
// Natural deceleration
direction = Math.sign(speed);
speed -= direction * deceleration;
if (Math.abs(speed) < 0.05) speed = 0; // Snap to 0 when very slow
}
// Boost logic
if (keys.shift && !speedBoost) {
originalMaxSpeed = maxSpeed;
maxSpeed *= 2;
speedBoost = true;
// Add boost visual effect
document.getElementById('speedIndicator').classList.add('boost-active');
} else if (!keys.shift && speedBoost) {
maxSpeed = originalMaxSpeed;
speedBoost = false;
// Remove boost visual effect
document.getElementById('speedIndicator').classList.remove('boost-active');
}
// Update turn speed based on actual speed (more realistic)
dynamicTurnSpeed = turnSpeed * Math.max(0.1, Math.abs(speed) / maxSpeed);
// Steering (only when moving)
if (speed !== 0) {
let steeringMultiplier = 1.0;
if (keys.space) {
steeringMultiplier = 1.5; // Handbrake turn
}
if (keys.left) {
playerCar.rotation.y += dynamicTurnSpeed * steeringMultiplier * 1;
}
if (keys.right) {
playerCar.rotation.y -= dynamicTurnSpeed * steeringMultiplier * 1;
}
}
// Reset
if (keys.r) {
if (isGameOver) {
resetGame();
} else {
// Soft reset - just reposition
playerCar.position.set(0, 1, 0);
playerCar.rotation.set(0, 0, 0);
speed = 0;
}
}
}
function resetGame() {
gameTime = 0;
currentLap = 1;
speed = 0;
isGameOver = false;
document.getElementById('gameOver').style.display = 'none';
// Reset player position
playerCar.position.set(0, 1, 0);
playerCar.rotation.set(0, 0, 0);
// Reset enemies
enemies.forEach((enemy, index) => {
const angle = (index / 3) * Math.PI * 2 + Math.PI / 6;
const radius = 60;
enemy.position.set(
Math.cos(angle) * radius,
1,
Math.sin(angle) * (radius * 0.6)
);
});
// Update HUD
updateHUD();
}
function updatePhysics() {
// Move player car
playerCar.position.x += Math.sin(playerCar.rotation.y) * speed;
playerCar.position.z += Math.cos(playerCar.rotation.y) * speed;
// Simple track boundary detection - keep within a large circle
const distanceFromCenter = Math.sqrt(playerCar.position.x ** 2 + playerCar.position.z ** 2);
if (distanceFromCenter > 100 || distanceFromCenter < 5) {
// Slow down drastically if hitting the "invisible" edge
speed *= 0.9;
}
// Update enemy movement (simple AI)
enemies.forEach(enemy => {
const speedAI = 0.8 + Math.random() * 0.4;
const radius = 50 + Math.sin(gameTime * 0.1) * 3;
// Calculate angle based on position
const angle = Math.atan2(enemy.position.z / 0.6, enemy.position.x);
// Move in orbit
enemy.position.x += Math.sin(angle + 0.03) * speedAI;
enemy.position.z += Math.cos(angle + 0.03) * (speedAI * 0.6);
// Update rotation
enemy.rotation.y = angle + Math.PI / 2;
// Add engine exhaust glow
const exhaust = Math.sin(gameTime * 5) * 0.1 + 0.1;
enemy.children[3].intensity = exhaust;
});
// Update particle emitters
updateParticles();
// Check boost pad collisions
checkBoostPadCollisions();
}
function updateParticles() {
particleEmitters.forEach(emitter => {
if (emitter.type === 'exhaust') {
const positions = emitter.positions;
let count = 0;
// Create exhaust particles behind car
for (let i = 0; i < 100; i++) {
if (Math.abs(speed) > 0.5) {
positions[count * 3] = playerCar.position.x - Math.sin(playerCar.rotation.y) * 2;
positions[count * 3 + 1] = playerCar.position.y;
positions[count * 3 + 2] = playerCar.position.z - Math.cos(playerCar.rotation.y) * 2;
count = (count + 1) % 100;
} else {
positions[i * 3] = 0;
positions[i * 3 + 1] = -100;
positions[i * 3 + 2] = 0;
}
}
emitter.mesh.geometry.attributes.position.needsUpdate = true;
}
});
}
function checkBoostPadCollisions() {
speedBoostPads.forEach(boostPad => {
const distance = playerCar.position.distanceTo(boostPad.position);
if (distance < 4) {
// Activate boost
if (!speedBoost) {
originalMaxSpeed = maxSpeed;
}
maxSpeed *= 2;
speedBoost = true;
document.getElementById('speedIndicator').classList.add('boost-active');
// Reset after 3 seconds
setTimeout(() => {
maxSpeed = originalMaxSpeed;
speedBoost = false;
document.getElementById('speedIndicator').classList.remove('boost-active');
}, 3000);
}
});
}
function checkCollisions() {
// Check collision with enemy cars
enemies.forEach(enemy => {
const distance = playerCar.position.distanceTo(enemy.position);
if (distance < 4) {
speed *= -0.7; // Bounce back
// Add visual effect - shake camera
const intensity = 0.2;
camera.position.y += (Math.random() - 0.5) * intensity;
camera.position.x += (Math.random() - 0.5) * intensity;
}
});
}
function updateHUD() {
// Speed indicator
const speedKMH = Math.abs(Math.round(speed * 50));
document.getElementById('speedIndicator').innerText = `SPEED: ${speedKMH}`;
document.getElementById('speedValue').innerText = speedKMH;
// Time
document.getElementById('timeIndicator').innerText = `TIME: ${gameTime.toFixed(1)}s`;
// Position (simple calculation based on angle)
let playerAngle = Math.atan2(playerCar.position.z * 0.6, playerCar.position.x);
if (playerAngle < 0) playerAngle += Math.PI * 2;
const positions = [playerAngle];
enemies.forEach(enemy => {
let enemyAngle = Math.atan2(enemy.position.z * 0.6, enemy.position.x);
if (enemyAngle < 0) enemyAngle += Math.PI * 2;
positions.push(enemyAngle);
});
positions.sort((a, b) => a - b);
playerPosition = positions.findIndex(pos => Math.abs(pos - playerAngle) < 0.1) + 1;
document.getElementById('positionIndicator').innerText = `POSITION: ${playerPosition}/4`;
document.getElementById('lapIndicator').innerText = `LAP: ${currentLap}/${totalLaps}`;
}
function animate() {
requestAnimationFrame(animate);
if (isGameOver) return;
const delta = clock.getDelta();
gameTime = clock.getElapsedTime();
handleInput(delta);
updatePhysics();
checkCollisions();
updateCamera();
updateHUD();
renderer.render(scene, camera);
}
init();
</script>
<div id="gameOver">
<div style="font-size: 28px; margin-bottom: 10px;">Game Over</div>
<div style="font-size: 14px;">Press R to restart</div>
</div>
</body>
</html>
```Creating a fully functional 3D racing game with complete frontend and backend logic, detailed collision physics, and a neon-lit cyberpunk aesthetic is a complex task that typically requires a significant amount of code, including the use of libraries like Three.js for 3D graphics and potentially other libraries for physics and rendering. Below is a simplified example using Three.js for rendering a basic 3D scene with a moving cube to illustrate the concept. This example is a starting point and would require substantial expansion to meet the full requirements of a racing game.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Racing Game</title>
<style>
body { margin: 0; padding: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
let scene, camera, renderer, cube;
function init() {
// Create the scene
scene = new THREE.Scene();
// Create the camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// Create the renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create a cube (representing the player's car)
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Create a light
const light = new THREE.PointLight(0xffffff, 1, 100);
light.position.set(10, 10, 10);
scene.add(light);
// Add controls for moving the cube
document.addEventListener('keydown', onKeyDown);
// Start the animation loop
animate();
}
function onKeyDown(event) {
switch (event.key) {
case 'ArrowUp':
cube.position.z -= 0.1;
break;
case 'ArrowDown':
cube.position.z += 0.1;
break;
case 'ArrowLeft':
cube.position.x -= 0.1;
break;
case 'ArrowRight':
cube.position.x += 0.1;
break;
}
}
function animate() {
requestAnimationFrame(animate);
// Rotate the cube for a simple animation
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
// Initialize the scene after the page loads
window.onload = init;
</script>
</body>
</html>
```
This example sets up a basic 3D scene with a rotating cube that you can control with the arrow keys. To fully develop this into a racing game with neon-lit cyberpunk tracks, speed boosts, and collision physics, you would need to:
1. **Expand the Track Design**: Create complex 3D models for the tracks and environment.
2. **Enhance Graphics**: Use textures and lighting effects to achieve the neon-lit cyberpunk aesthetic.
3. **Implement Collision Physics**: Add a physics engine or custom collision detection.
4. **Add Speed Boosts**: Implement speed boost areas or power-ups on the track.
5. **Refine Controls and Game Mechanics**: Improve the handling of the player's vehicle and add gameplay elements.
This is a significant project that would typically involve a team of developers and artists, but the above code provides a foundation to build upon.Here's a standalone HTML file for a simple 3D racing game with a neon-lit cyberpunk theme, using Three.js for 3D rendering and Cannon.js for physics. Please note that this is a simplified version and creating a full-fledged 3D racing game would require more extensive development.
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Neon Cyberpunk Racing Game</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
}
canvas {
display: block;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script>
<script>
// Game variables
let scene, camera, renderer, world;
let car, carBody;
let track;
let speedBoosts = [];
let boosts = [];
let gameOver = false;
let score = 0;
let speed = 0;
let maxSpeed = 10;
let acceleration = 0.1;
let deceleration = 0.05;
let carWidth = 1;
let carHeight = 0.5;
let carLength = 2;
let trackWidth = 10;
let trackLength = 100;
let trackSegments = 20;
let trackCurvature = 0.1;
let trackPosition = 0;
let trackSpeed = 0.1;
let trackOffset = 0;
let boostSpeed = 20;
let boostDuration = 3;
let boostCooldown = 5;
let boostReady = true;
// Initialize the game
init();
// Set up the scene
function init() {
// Create the scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
// Create the camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, -10);
camera.lookAt(0, 0, 0);
// Create the renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create the physics world
world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;
// Create the track
createTrack();
// Create the car
createCar();
// Create speed boosts
createSpeedBoosts();
// Start the game loop
animate();
}
// Create the track
function createTrack() {
// Track geometry
let trackGeometry = new THREE.BufferGeometry();
let positions = [];
let colors = [];
let indices = [];
// Track material
let trackMaterial = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
vertexColors: THREE.VertexColors
});
// Create the track segments
for (let i = 0; i < trackSegments; i++) {
let segmentLength = trackLength / trackSegments;
let segmentPosition = i * segmentLength - trackLength / 2;
// Create the segment geometry
let segmentGeometry = new THREE.PlaneGeometry(trackWidth, segmentLength, 10, 10);
segmentGeometry.rotateX(-Math.PI / 2);
// Position the segment
segmentGeometry.translate(0, 0, segmentPosition);
// Add curvature to the track
let curvature = Math.sin(segmentPosition * trackCurvature) * trackWidth / 2;
segmentGeometry.translate(curvature, 0, 0);
// Merge the segment geometry with the track geometry
trackGeometry.merge(segmentGeometry);
// Add neon colors to the segment
for (let j = 0; j < segmentGeometry.attributes.position.count; j++) {
let color = new THREE.Color();
color.setHSL(Math.random(), 1, 0.5);
colors.push(color.r, color.g, color.b);
}
}
// Set the track colors
trackGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
// Create the track mesh
track = new THREE.Mesh(trackGeometry, trackMaterial);
scene.add(track);
// Create the track physics body
let trackShape = new CANNON.Plane();
let trackBody = new CANNON.Body({ mass: 0 });
trackBody.addShape(trackShape);
trackBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.addBody(trackBody);
}
// Create the car
function createCar() {
// Car geometry
let carGeometry = new THREE.BoxGeometry(carWidth, carHeight, carLength);
// Car material
let carMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
// Create the car mesh
car = new THREE.Mesh(carGeometry, carMaterial);
car.position.set(0, carHeight / 2, 0);
scene.add(car);
// Create the car physics body
let carShape = new CANNON.Box(new CANNON.Vec3(carWidth / 2, carHeight / 2, carLength / 2));
carBody = new CANNON.Body({ mass: 1 });
carBody.addShape(carShape);
carBody.position.set(0, carHeight / 2, 0);
world.addBody(carBody);
// Add the car to the physics world
world.addBody(carBody);
}
// Create speed boosts
function createSpeedBoosts() {
// Create the speed boost geometry
let boostGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
// Create the speed boost material
let boostMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
// Create the speed boosts
for (let i = 0; i < 10; i++) {
// Create the speed boost mesh
let boost = new THREE.Mesh(boostGeometry, boostMaterial);
boost.position.set(Math.random() * trackWidth - trackWidth / 2, 0.5, Math.random() * -trackLength);
scene.add(boost);
// Create the speed boost physics body
let boostShape = new CANNON.Box(new CANNON.Vec3(0.25, 0.25, 0.25));
let boostBody = new CANNON.Body({ mass: 0 });
boostBody.addShape(boostShape);
boostBody.position.set(boost.position.x, boost.position.y, boost.position.z);
world.addBody(boostBody);
// Add the speed boost to the array
speedBoosts.push(boost);
boosts.push({
mesh: boost,
body: boostBody,
used: false
});
}
}
// Animate the game
function animate() {
if (gameOver) return;
requestAnimationFrame(animate);
// Update the physics world
world.step(1/60);
// Update the car position
car.position.copy(carBody.position);
car.quaternion.copy(carBody.quaternion);
// Move the car forward
if (speed < maxSpeed) {
speed += acceleration;
}
// Apply the car speed
carBody.velocity.z = -speed;
// Apply the car deceleration
if (speed > 0) {
speed -= deceleration;
} else {
speed = 0;
}
// Move the track
trackPosition += trackSpeed;
if (trackPosition > trackLength) {
trackPosition = 0;
}
// Update the track position
track.position.z = trackPosition;
// Update the speed boosts
for (let i = 0; i < boosts.length; i++) {
let boost = boosts[i];
// Check if the boost is used
if (boost.used) {
// Respawn the boost
boost.mesh.position.set(Math.random() * trackWidth - trackWidth / 2, 0.5, Math.random() * -trackLength);
boost.body.position.set(boost.mesh.position.x, boost.mesh.position.y, boost.mesh.position.z);
boost.used = false;
}
// Check if the car hits the boost
if (!boost.used && carBody.position.distanceTo(boost.body.position) < 1) {
// Apply the boost
speed = boostSpeed;
boost.used = true;
// Remove the boost after the duration
setTimeout(() => {
speed = maxSpeed;
}, boostDuration * 1000);
}
}
// Check if the car is out of bounds
if (carBody.position.x < -trackWidth / 2 || carBody.position.x > trackWidth / 2) {
gameOver = true;
alert("Game Over! Your score: " + Math.floor(score));
}
// Update the score
score += speed / 10;
// Render the scene
renderer.render(scene, camera);
}
// Handle keyboard input
document.addEventListener('keydown', (event) => {
if (gameOver) return;
switch (event.key) {
case 'ArrowLeft':
carBody.velocity.x = -5;
break;
case 'ArrowRight':
carBody.velocity.x = 5;
break;
case 'ArrowUp':
if (boostReady) {
speed = boostSpeed;
boostReady = false;
// Remove the boost after the duration
setTimeout(() => {
speed = maxSpeed;
}, boostDuration * 1000);
// Reset the boost cooldown
setTimeout(() => {
boostReady = true;
}, boostCooldown * 1000);
}
break;
}
});
</script>
</body>
</html>
```
This HTML file includes a simple 3D racing game with a neon-lit cyberpunk theme. The game uses Three.js for 3D rendering and Cannon.js for physics. The player can control the car using the arrow keys. The left and right arrows move the car, and the up arrow activates a speed boost. The game ends if the car goes out of bounds, and the player's score is displayed.```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Neon Cyberpunk 3D Racing Game</title>
<style>
body { margin: 0; background: black; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.154.0/build/three.min.js"></script>
<script>
// Scene setup
let scene, camera, renderer;
let clock = new THREE.Clock();
let player;
let speed = 0;
const MAX_SPEED = 50;
const ACCELERATION = 10;
const FRICTION = 0.98;
let trackSegments = [];
let objects = [];
// Initialize scene
init();
animate();
function init() {
scene = new THREE.Scene();
// Camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 2000);
camera.position.set(0, 5, -10);
camera.lookAt(0, 0, 50);
// Renderer
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);
const neonLight1 = new THREE.PointLight(0xff00ff, 1, 50);
neonLight1.position.set(0, 5, 50);
scene.add(neonLight1);
// Track - simple curved path made of segments
generateTrack();
// Player - a simple box
const playerGeometry = new THREE.BoxGeometry(1, 1, 2);
const playerMaterial = new THREE.MeshStandardMaterial({ color: 0x00ffff, metalness: 0.7, roughness: 0.2, emissive: 0x00ffff });
player = new THREE.Mesh(playerGeometry, playerMaterial);
player.position.set(0, 0.5, 0);
scene.add(player);
// Handle resize
window.addEventListener('resize', onWindowResize);
// Keyboard controls
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
// Controls state
controls = {
left: false,
right: false,
boost: false
};
}
function generateTrack() {
// Create a series of curved path segments to simulate a track
const curvePoints = [];
const numSegments = 20;
let radius = 50;
let angle = 0;
for(let i=0; i<numSegments; i++) {
// Generate a curved path with some variation
angle += Math.PI / 10 + (Math.random() - 0.5) * Math.PI/20;
const x = Math.cos(angle) * radius + (Math.random() - 0.5)*10;
const z = Math.sin(angle) * radius + (Math.random() - 0.5)*10 + i*20;
curvePoints.push(new THREE.Vector3(x, 0, z));
}
// Create neon-lit track edges
for(let i=0; i<curvePoints.length-1; i++) {
const start = curvePoints[i];
const end = curvePoints[i+1];
const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length();
const midPoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
// Create track edge as a thin tube or box
const edgeGeometry = new THREE.BoxGeometry(length, 0.2, 0.2);
const edgeMaterial = new THREE.MeshStandardMaterial({ color: 0xff00ff, emissive: 0xff00ff });
const edgeMesh = new THREE.Mesh(edgeGeometry, edgeMaterial);
// Rotate edge to match segment direction
edgeMesh.position.copy(midPoint);
const angleY = Math.atan2(direction.z, direction.x);
edgeMesh.rotation.y = -angleY;
scene.add(edgeMesh);
trackSegments.push(edgeMesh);
}
// Store the path for movement
trackPath = new THREE.CurvePath();
for(let i=0; i<curvePoints.length-1; i++) {
const start = curvePoints[i];
const end = curvePoints[i+1];
const lineCurve = new THREE.LineCurve3(start, end);
trackPath.add(lineCurve);
}
// For collision detection, store track points
trackPoints = curvePoints;
}
function onWindowResize() {
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onKeyDown(e) {
if(e.key === 'ArrowLeft') controls.left = true;
if(e.key === 'ArrowRight') controls.right = true;
if(e.key === 'ArrowUp') controls.boost = true;
}
function onKeyUp(e) {
if(e.key === 'ArrowLeft') controls.left = false;
if(e.key === 'ArrowRight') controls.right = false;
if(e.key === 'ArrowUp') controls.boost = false;
}
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
// Accelerate
if(controls.boost) {
speed += ACCELERATION * delta;
if(speed > MAX_SPEED) speed = MAX_SPEED;
} else {
// Apply friction
speed *= FRICTION;
}
// Process turn controls
if(controls.left) {
player.rotation.y += Math.PI/4 * delta;
}
if(controls.right) {
player.rotation.y -= Math.PI/4 * delta;
}
// Move player forward
const forward = new THREE.Vector3(0,0,1);
forward.applyEuler(player.rotation);
forward.multiplyScalar(speed * delta);
player.position.add(forward);
// Keep within track boundaries (collision physics simplified)
checkCollision();
// Update camera to follow player
const cameraOffset = new THREE.Vector3(0, 5, -10);
const cameraPosition = player.position.clone().add(cameraOffset);
camera.position.lerp(cameraPosition, 0.1);
camera.lookAt(player.position);
renderer.render(scene, camera);
}
function checkCollision() {
// Check if player is outside track points (simple boundary)
let minDist = Infinity;
let closestPoint = null;
for(let p of trackPoints) {
const dist = p.distanceTo(player.position);
if(dist < minDist) {
minDist = dist;
closestPoint = p;
}
}
// Let's define track radius
const trackRadius = 15;
if(minDist > trackRadius) {
// Collided outside track, simple response: push player back towards closest point
const dir = new THREE.Vector3().subVectors(player.position, closestPoint).normalize();
player.position.copy(closestPoint).add(dir.multiplyScalar(trackRadius));
speed *= 0.5; // slow down on collision
}
}
</script>
</body>
</html>
```Here's a complete standalone HTML file for a 3D cyberpunk racing game with neon tracks, speed boosts, and collision physics:
```html
<!DOCTYPE html>
<html>
<head>
<title>Cyberpunk Neon Racer</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
}
#ui {
position: absolute;
top: 10px;
left: 10px;
color: #0ff;
text-shadow: 0 0 5px #0ff;
font-size: 16px;
}
#start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #0ff;
text-shadow: 0 0 5px #0ff;
font-size: 24px;
}
button {
background: transparent;
border: 2px solid #0ff;
color: #0ff;
padding: 10px 20px;
margin-top: 20px;
font-size: 18px;
cursor: pointer;
box-shadow: 0 0 10px #0ff;
text-shadow: 0 0 5px #0ff;
}
button:hover {
background: rgba(0, 255, 255, 0.2);
}
</style>
</head>
<body>
<div id="ui">
<div>Speed: <span id="speed">0</span> MPH</div>
<div>Boost: <span id="boost">100</span>%</div>
<div>Lap: <span id="lap">0</span>/3</div>
</div>
<div id="start-screen">
<h1>CYBERPUNK NEON RACER</h1>
<p>Use WASD or Arrow Keys to drive</p>
<p>SPACE to activate boost</p>
<button id="start-button">START RACE</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cannon-es@0.19.0/dist/cannon-es.min.js"></script>
<script>
// Game variables
let scene, camera, renderer, world;
let car, track = [];
let speed = 0, maxSpeed = 120, acceleration = 0.2, deceleration = 0.1;
let boost = 100, boostActive = false, boostPower = 0.5;
let lap = 0, totalLaps = 3;
let gameStarted = false;
let lastTime = 0;
// Physics variables
let physicsWorld;
const timeStep = 1/60;
// DOM elements
const speedElement = document.getElementById('speed');
const boostElement = document.getElementById('boost');
const lapElement = document.getElementById('lap');
const startScreen = document.getElementById('start-screen');
const startButton = document.getElementById('start-button');
// Initialize game
init();
function init() {
// Set up Three.js scene
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000022, 0.002);
// Set up camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, -10);
camera.lookAt(0, 0, 0);
// Set up renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Set up physics world
physicsWorld = new CANNON.World({
gravity: new CANNON.Vec3(0, -9.82, 0)
});
physicsWorld.defaultContactMaterial.friction = 0.5;
// Add lights
addLights();
// Create track
createTrack();
// Create car
createCar();
// Add event listeners
window.addEventListener('resize', onWindowResize);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
startButton.addEventListener('click', startGame);
// Start game loop
animate();
}
function addLights() {
// Ambient light
const ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
// Directional light (like sunlight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(0, 1, 0);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
scene.add(directionalLight);
// Neon lights
const neonLight1 = new THREE.PointLight(0xff00ff, 1, 20);
neonLight1.position.set(5, 2, 0);
scene.add(neonLight1);
const neonLight2 = new THREE.PointLight(0x00ffff, 1, 20);
neonLight2.position.set(-5, 2, 0);
scene.add(neonLight2);
}
function createTrack() {
const trackWidth = 10;
const trackHeight = 0.5;
const trackLength = 2;
const trackCurve = Math.PI / 2; // 90 degree turns
// Track material
const trackMaterial = new THREE.MeshPhongMaterial({
color: 0x333333,
emissive: 0x222222,
specular: 0xffffff,
shininess: 30
});
// Neon edge material
const neonMaterial = new THREE.MeshPhongMaterial({
color: 0x000000,
emissive: 0x00ffff,
specular: 0xffffff,
shininess: 100
});
// Create straight sections and curves
const straightLength = 50;
const curveRadius = 20;
// Starting straight
createStraight(0, 0, 0, straightLength, trackWidth, trackHeight, trackMaterial, neonMaterial);
// First curve
createCurve(0, 0, straightLength, curveRadius, trackCurve, trackWidth, trackHeight, trackMaterial, neonMaterial);
// Second straight
createStraight(curveRadius, 0, straightLength + curveRadius, straightLength, trackWidth, trackHeight, trackMaterial, neonMaterial);
// Second curve
createCurve(curveRadius, 0, straightLength + curveRadius + straightLength, curveRadius, trackCurve, trackWidth, trackHeight, trackMaterial, neonMaterial);
// Third straight
createStraight(0, 0, straightLength + curveRadius + straightLength + curveRadius, straightLength, trackWidth, trackHeight, trackMaterial, neonMaterial);
// Third curve
createCurve(-curveRadius, 0, straightLength + curveRadius + straightLength + curveRadius + straightLength, curveRadius, trackCurve, trackWidth, trackHeight, trackMaterial, neonMaterial);
// Fourth straight
createStraight(-curveRadius, 0, straightLength + curveRadius + straightLength + curveRadius + straightLength + curveRadius, straightLength, trackWidth, trackHeight, trackMaterial, neonMaterial);
// Fourth curve
createCurve(0, 0, straightLength + curveRadius + straightLength + curveRadius + straightLength + curveRadius + straightLength, curveRadius, trackCurve, trackWidth, trackHeight, trackMaterial, neonMaterial);
// Add boost pads
addBoostPad(0, 0.1, straightLength/2, 3);
addBoostPad(curveRadius, 0.1, straightLength + curveRadius + straightLength/2, 3);
addBoostPad(0, 0.1, straightLength + curveRadius + straightLength + curveRadius + straightLength/2, 3);
addBoostPad(-curveRadius, 0.1, straightLength + curveRadius + straightLength + curveRadius + straightLength + curveRadius + straightLength/2, 3);
}
function createStraight(x, y, z, length, width, height, material, neonMaterial) {
const geometry = new THREE.BoxGeometry(width, height, length);
const trackPiece = new THREE.Mesh(geometry, material);
trackPiece.position.set(x, y, z + length/2);
trackPiece.receiveShadow = true;
scene.add(trackPiece);
// Add physics body
const trackBody = new CANNON.Body({
mass: 0, // Static body
shape: new CANNON.Box(new CANNON.Vec3(width/2, height/2, length/2)),
position: new CANNON.Vec3(x, y, z + length/2)
});
physicsWorld.addBody(trackBody);
// Add neon edges
const neonGeometry = new THREE.BoxGeometry(0.2, 0.1, length);
const leftNeon = new THREE.Mesh(neonGeometry, neonMaterial);
leftNeon.position.set(x - width/2 + 0.1, y + height/2 + 0.05, z + length/2);
scene.add(leftNeon);
const rightNeon = new THREE.Mesh(neonGeometry, neonMaterial);
rightNeon.position.set(x + width/2 - 0.1, y + height/2 + 0.05, z + length/2);
scene.add(rightNeon);
// Add to track array for collision detection
track.push({
mesh: trackPiece,
body: trackBody,
startZ: z,
endZ: z + length,
x: x,
width: width
});
}
function createCurve(x, y, z, radius, angle, width, height, material, neonMaterial) {
const segments = 16;
const segmentAngle = angle / segments;
for (let i = 0; i < segments; i++) {
const segmentX = x + Math.cos(i * segmentAngle) * radius;
const segmentZ = z + Math.sin(i * segmentAngle) * radius;
const geometry = new THREE.BoxGeometry(width, height, 2 * Math.PI * radius / segments);
const trackPiece = new THREE.Mesh(geometry, material);
trackPiece.position.set(segmentX, y, segmentZ);
trackPiece.rotation.y = i * segmentAngle + segmentAngle/2;
trackPiece.receiveShadow = true;
scene.add(trackPiece);
// Add physics body
const trackBody = new CANNON.Body({
mass: 0, // Static body
shape: new CANNON.Box(new CANNON.Vec3(width/2, height/2, Math.PI * radius / segments)),
position: new CANNON.Vec3(segmentX, y, segmentZ),
quaternion: new CANNON.Quaternion().setFromAxisAngle(
new CANNON.Vec3(0, 1, 0),
i * segmentAngle + segmentAngle/2
)
});
physicsWorld.addBody(trackBody);
// Add neon edges
const neonGeometry = new THREE.BoxGeometry(0.2, 0.1, 2 * Math.PI * radius / segments);
const innerNeon = new THREE.Mesh(neonGeometry, neonMaterial);
innerNeon.position.set(
segmentX + Math.cos(i * segmentAngle + segmentAngle/2 + Math.PI/2) * (width/2 - 0.1),
y + height/2 + 0.05,
segmentZ + Math.sin(i * segmentAngle + segmentAngle/2 + Math.PI/2) * (width/2 - 0.1)
);
innerNeon.rotation.y = i * segmentAngle + segmentAngle/2;
scene.add(innerNeon);
const outerNeon = new THREE.Mesh(neonGeometry, neonMaterial);
outerNeon.position.set(
segmentX + Math.cos(i * segmentAngle + segmentAngle/2 - Math.PI/2) * (width/2 - 0.1),
y + height/2 + 0.05,
segmentZ + Math.sin(i * segmentAngle + segmentAngle/2 - Math.PI/2) * (width/2 - 0.1)
);
outerNeon.rotation.y = i * segmentAngle + segmentAngle/2;
scene.add(outerNeon);
// Add to track array for collision detection
track.push({
mesh: trackPiece,
body: trackBody,
startZ: z + Math.sin(i * segmentAngle) * radius,
endZ: z + Math.sin((i + 1) * segmentAngle) * radius,
x: x + Math.cos(i * segmentAngle) * radius,
radius: radius,
angle: i * segmentAngle,
segmentAngle: segmentAngle,
isCurve: true
});
}
}
function addBoostPad(x, y, z, size) {
const geometry = new THREE.BoxGeometry(size, 0.1, size);
const material = new THREE.MeshPhongMaterial({
color: 0x000000,
emissive: 0xff00ff,
specular: 0xffffff,
shininess: 100
});
const boostPad = new THREE.Mesh(geometry, material);
boostPad.position.set(x, y, z);
scene.add(boostPad);
// Add physics body
const boostBody = new CANNON.Body({
mass: 0, // Static body
shape: new CANNON.Box(new CANNON.Vec3(size/2, 0.05, size/2)),
position: new CANNON.Vec3(x, y, z)
});
physicsWorld.addBody(boostBody);
// Add to track array for collision detection
track.push({
mesh: boostPad,
body: boostBody,
isBoostPad: true
});
}
function createCar() {
// Car body
const carBodyGeometry = new THREE.BoxGeometry(2, 1, 4);
const carBodyMaterial = new THREE.MeshPhongMaterial({
color: 0x333333,
emissive: 0x444444,
specular: 0xffffff,
shininess: 50
});
car = new THREE.Mesh(carBodyGeometry, carBodyMaterial);
car.position.set(0, 2, 0);
car.castShadow = true;
scene.add(car);
// Car cockpit
const cockpitGeometry = new THREE.BoxGeometry(1.8, 0.5, 1.5);
const cockpitMaterial = new THREE.MeshPhongMaterial({
color: 0x000000,
transparent: true,
opacity: 0.7
});
const cockpit = new THREE.Mesh(cockpitGeometry, cockpitMaterial);
cockpit.position.set(0, 1.5, 0.5);
car.add(cockpit);
// Car wheels
const wheelGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.3, 16);
const wheelMaterial = new THREE.MeshPhongMaterial({ color: 0x111111 });
const wheelPositions = [
{ x: -1.2, y: -0.5, z: -1.5 },
{ x: 1.2, y: -0.5, z: -1.5 },
{ x: -1.2, y: -0.5, z: 1.5 },
{ x: 1.2, y: -0.5, z: 1.5 }
];
wheelPositions.forEach(pos => {
const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheel.position.set(pos.x, pos.y, pos.z);
wheel.rotation.z = Math.PI / 2;
car.add(wheel);
});
// Car neon lights
const neonGeometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
const frontNeonMaterial = new THREE.MeshPhongMaterial({
color: 0x000000,
emissive: 0x00ffff,
specular: 0xffffff,
shininess: 100
});
const rearNeonMaterial = new THREE.MeshPhongMaterial({
color: 0x000000,
emissive: 0xff00ff,
specular: 0xffffff,
shininess: 100
});
const frontLeftNeon = new THREE.Mesh(neonGeometry, frontNeonMaterial);
frontLeftNeon.position.set(-1, 0, -2);
car.add(frontLeftNeon);
const frontRightNeon = new THREE.Mesh(neonGeometry, frontNeonMaterial);
frontRightNeon.position.set(1, 0, -2);
car.add(frontRightNeon);
const rearLeftNeon = new THREE.Mesh(neonGeometry, rearNeonMaterial);
rearLeftNeon.position.set(-1, 0, 2);
car.add(rearLeftNeon);
const rearRightNeon = new THREE.Mesh(neonGeometry, rearNeonMaterial);
rearRightNeon.position.set(1, 0, 2);
car.add(rearRightNeon);
// Car physics body
const carShape = new CANNON.Box(new CANNON.Vec3(1, 0.5, 2));
const carBody = new CANNON.Body({
mass: 1000,
shape: carShape,
position: new CANNON.Vec3(0, 2, 0),
linearDamping: 0.5,
angularDamping: 0.5
});
physicsWorld.addBody(carBody);
// Keep reference to physics body
car.userData.physicsBody = carBody;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onKeyDown(event) {
if (!gameStarted) return;
switch(event.key) {
case 'ArrowUp':
case 'w':
car.userData.accelerating = true;
break;
case 'ArrowDown':
case 's':
car.userData.braking = true;
break;
case 'ArrowLeft':
case 'a':
car.userData.turningLeft = true;
break;
case 'ArrowRight':
case 'd':
car.userData.turningRight = true;
break;
case ' ':
if (boost > 0) {
boostActive = true;
}
break;
}
}
function onKeyUp(event) {
if (!gameStarted) return;
switch(event.key) {
case 'ArrowUp':
case 'w':
car.userData.accelerating = false;
break;
case 'ArrowDown':
case 's':
car.userData.braking = false;
break;
case 'ArrowLeft':
case 'a':
car.userData.turningLeft = false;
break;
case 'ArrowRight':
case 'd':
car.userData.turningRight = false;
break;
case ' ':
boostActive = false;
break;
}
}
function startGame() {
gameStarted = true;
startScreen.style.display = 'none';
lastTime = performance.now();
}
function updateCar(deltaTime) {
const carBody = car.userData.physicsBody;
// Apply acceleration
if (car.userData.accelerating) {
speed += acceleration * deltaTime;
if (boostActive && boost > 0) {
speed += boostPower * deltaTime;
boost -= 20 * deltaTime;
if (boost < 0) boost = 0;
}
} else if (car.userData.braking) {
speed -= acceleration * 2 * deltaTime;
} else {
// Natural deceleration
if (speed > 0) {
speed -= deceleration * deltaTime;
if (speed < 0) speed = 0;
} else if (speed < 0) {
speed += deceleration * deltaTime;
if (speed > 0) speed = 0;
}
}
// Limit speed
if (speed > maxSpeed) speed = maxSpeed;
if (speed < -maxSpeed / 2) speed = -maxSpeed / 2;
// Apply turning
let turnFactor = speed / maxSpeed;
if (car.userData.turningLeft) {
carBody.angularVelocity.y = 0.5 * turnFactor;
} else if (car.userData.turningRight) {
carBody.angularVelocity.y = -0.5 * turnFactor;
} else {
carBody.angularVelocity.y *= 0.9; // Dampen rotation
}
// Apply forward movement
const forward = new CANNON.Vec3(
-Math.sin(carBody.quaternion.toEuler().y),
0,
-Math.cos(carBody.quaternion.toEuler().y)
);
forward.normalize();
forward.scale(speed, forward);
// Maintain y position
carBody.position.y = 2;
// Apply velocity
carBody.velocity.x = forward.x;
carBody.velocity.z = forward.z;
// Recharge boost when not in use
if (!boostActive && boost < 100) {
boost += 10 * deltaTime;
if (boost > 100) boost = 100;
}
// Update UI
speedElement.textContent = Math.abs(Math.round(speed));
boostElement.textContent = Math.round(boost);
// Update camera position
const cameraOffset = new THREE.Vector3(
-Math.sin(carBody.quaternion.toEuler().y) * 10,
5,
-Math.cos(carBody.quaternion.toEuler().y) * 10
);
camera.position.set(
carBody.position.x + cameraOffset.x,
carBody.position.y + cameraOffset.y,
carBody.position.z + cameraOffset.z
);
camera.lookAt(car.position);
// Check for collisions with boost pads
track.forEach(trackPiece => {
if (trackPiece.isBoostPad) {
const distance = Math.sqrt(
Math.pow(carBody.position.x - trackPiece.body.position.x, 2) +
Math.pow(carBody.position.z - trackPiece.body.position.z, 2)
);
if (distance < 2) {
boost = 100;
}
}
});
// Check for lap completion
if (carBody.position.z > 200 && carBody.position.z < 210 && Math.abs(carBody.position.x) < 5) {
lap++;
lapElement.textContent = lap;
if (lap > totalLaps) {
// Race finished
gameStarted = false;
startScreen.style.display = 'flex';
startScreen.innerHTML = `
<h1>RACE COMPLETE!</h1>
<p>Final Time: ${((performance.now() - lastTime)/1000).toFixed(2)} seconds</p>
<button id="restart-button">RACE AGAIN</button>
`;
document.getElementById('restart-button').addEventListener('click', () => {
location.reload();
});
}
}
}
function animate(time) {
requestAnimationFrame(animate);
// Calculate delta time
const deltaTime = Math.min(0.1, (time - lastTime) / 1000);
lastTime = time;
if (gameStarted) {
// Update physics
physicsWorld.step(timeStep);
// Update car
updateCar(deltaTime);
// Sync Three.js car with physics body
car.position.copy(car.userData.physicsBody.position);
car.quaternion.copy(car.userData.physicsBody.quaternion);
}
renderer.render(scene, camera);
}
</script>
</body>
</html>
```
This game includes:
1. A cyberpunk neon-lit 3D racing track with curves and straight sections
2. A controllable car with physics-based movement
3. Speed boost pads that recharge your boost meter
4. WASD or arrow key controls (SPACE for boost)
5. Lap counter and speed/boost indicators
6. Collision physics to keep the car on the track
7. Start screen and race completion screen
The game uses Three.js for 3D rendering and Cannon.js for physics simulation. All code is contained in a single HTML file with no external dependencies beyond the CDN-hosted libraries.```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cyberpunk 3D Racing Game</title>
<style>
body { margin: 0; overflow: hidden; background: #000; }
#speed { position: absolute; top: 10px; left: 10px; color: #0ff; font: 16px sans-serif; }
</style>
</head>
<body>
<div id="speed">Speed: 0</div>
<!-- Three.js -->
<script src="https://cdn.jsdelivr.net/npm/three@0.150.1/build/three.min.js"></script>
<!-- Cannon.js (cannon-es) -->
<script src="https://cdn.jsdelivr.net/npm/cannon-es@0.20.0/dist/cannon-es.js"></script>
<script>
// Scene, camera, renderer
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000000, 0.002);
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
// Lights
const ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
const neonLight1 = new THREE.PointLight(0x00ffff, 1.5, 200);
neonLight1.position.set(0, 10, 50);
scene.add(neonLight1);
const neonLight2 = new THREE.PointLight(0xff00ff, 1.2, 200);
neonLight2.position.set(0, 10, -50);
scene.add(neonLight2);
// Physics world
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;
const materialDefault = new CANNON.Material("default");
const contactMat = new CANNON.ContactMaterial(materialDefault, materialDefault, { friction: 0.3, restitution: 0.1 });
world.addContactMaterial(contactMat);
// Ground (infinite plane)
const groundBody = new CANNON.Body({ mass: 0, material: materialDefault });
groundBody.addShape(new CANNON.Plane());
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
world.addBody(groundBody);
// Track parameters
const trackWidth = 10, trackLength = 300, wallHeight = 4, wallThickness = 1;
// Floor
const floorGeo = new THREE.PlaneGeometry(trackWidth, trackLength, 10, 10);
const floorMat = new THREE.MeshStandardMaterial({ color: 0x111111, emissive: 0x00ffff, side: THREE.DoubleSide });
const floorMesh = new THREE.Mesh(floorGeo, floorMat);
floorMesh.rotation.x = -Math.PI / 2;
floorMesh.position.z = trackLength / 2;
scene.add(floorMesh);
// Walls
const wallGeo = new THREE.BoxGeometry(wallThickness, wallHeight, trackLength);
const wallMat = new THREE.MeshStandardMaterial({ color: 0x110022, emissive: 0xff00ff });
const leftWall = new THREE.Mesh(wallGeo, wallMat);
leftWall.position.set(trackWidth/2 + wallThickness/2, wallHeight/2, trackLength/2);
scene.add(leftWall);
const rightWall = leftWall.clone();
rightWall.position.x = - (trackWidth/2 + wallThickness/2);
scene.add(rightWall);
// Physics walls
const wallShape = new CANNON.Box(new CANNON.Vec3(wallThickness/2, wallHeight/2, trackLength/2));
[leftWall, rightWall].forEach((wMesh) => {
const body = new CANNON.Body({ mass: 0, material: materialDefault });
body.addShape(wallShape);
body.position.copy(wMesh.position);
world.addBody(body);
});
// Car (physics + mesh)
const carSize = { x: 1.5, y: 0.8, z: 3 };
const chassisShape = new CANNON.Box(new CANNON.Vec3(carSize.x/2, carSize.y/2, carSize.z/2));
const carBody = new CANNON.Body({ mass: 150, material: materialDefault });
carBody.addShape(chassisShape);
carBody.position.set(0, 2, 5);
carBody.angularDamping = 0.5;
world.addBody(carBody);
const carGeo = new THREE.BoxGeometry(carSize.x, carSize.y, carSize.z);
const carMat = new THREE.MeshStandardMaterial({ color: 0x222222, emissive: 0x00ffff });
const carMesh = new THREE.Mesh(carGeo, carMat);
scene.add(carMesh);
// Camera offset (third-person)
const cameraOffset = new THREE.Vector3(0, 5, -12);
// Speed boost pads
const boostZones = [];
const boostPositions = [60, 140, 220];
boostPositions.forEach(z => {
const padGeo = new THREE.PlaneGeometry(trackWidth, 2);
const padMat = new THREE.MeshStandardMaterial({ color: 0xff00ff, emissive: 0xff00ff, side: THREE.DoubleSide });
const pad = new THREE.Mesh(padGeo, padMat);
pad.rotation.x = -Math.PI/2;
pad.position.set(0, 0.01, z);
scene.add(pad);
boostZones.push({ z, cooldown: 0 });
});
// Input handling
const keys = {};
window.addEventListener('keydown', e => keys[e.code] = true);
window.addEventListener('keyup', e => keys[e.code] = false);
// HUD
const speedDOM = document.getElementById('speed');
// Animation loop
let lastTime;
function animate(time) {
requestAnimationFrame(animate);
const dt = lastTime ? (time - lastTime) / 1000 : 0;
lastTime = time;
// Controls: forward/backward
const force = new CANNON.Vec3();
const forward = new CANNON.Vec3(0, 0, -1);
carBody.quaternion.vmult(forward, forward);
if (keys['ArrowUp'] || keys['KeyW']) {
force.vadd(forward.scale(500), force);
}
if (keys['ArrowDown'] || keys['KeyS']) {
force.vadd(forward.scale(-200), force);
}
carBody.applyForce(force, carBody.position);
// Steering: apply torque around Y
const steer = ((keys['ArrowLeft'] || keys['KeyA']) ? 1 : 0) - ((keys['ArrowRight'] || keys['KeyD']) ? 1 : 0);
carBody.angularVelocity.y += steer * 0.03;
// Step physics
world.step(1/60, dt, 3);
// Sync car mesh
carMesh.position.copy(carBody.position);
carMesh.quaternion.copy(carBody.quaternion);
// Camera follow
const camPos = cameraOffset.clone().applyQuaternion(carMesh.quaternion).add(carMesh.position);
camera.position.lerp(camPos, 0.1);
camera.lookAt(carMesh.position);
// Boost detection
boostZones.forEach(zone => {
if (time > zone.cooldown && Math.abs(carBody.position.z - zone.z) < 1 && Math.abs(carBody.position.x) < trackWidth/2) {
// boost
const boostDir = forward.clone().scale(20);
carBody.velocity.vadd(boostDir, carBody.velocity);
zone.cooldown = time + 2000; // 2s cooldown
}
});
// Update HUD
const spd = carBody.velocity.length().toFixed(1);
speedDOM.textContent = `Speed: ${spd}`;
renderer.render(scene, camera);
}
animate();
// Handle resize
window.addEventListener('resize', () => {
camera.aspect = innerWidth/innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Cyberpunk Racing Game</title>
<style>
body {
margin: 0;
background-color: #f0f0f0;
}
#canvas {
width: 100%;
height: 100vh;
display: block;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
// Get the canvas element
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Set the canvas dimensions
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Define some colors
const neonPink = '#FF69B4';
const neonBlue = '#00BFFF';
const neonGreen = '#33CC33';
// Define the track
const track = {
points: [
{ x: 100, y: 100 },
{ x: 300, y: 100 },
{ x: 500, y: 300 },
{ x: 300, y: 500 },
{ x: 100, y: 500 },
{ x: 100, y: 100 }
],
width: 100
};
// Define the car
const car = {
x: track.points[0].x,
y: track.points[0].y,
vx: 0,
vy: 0,
speed: 0,
angle: 0,
boost: false,
width: 20,
height: 20
};
// Define the collision detection function
function checkCollision(car, track) {
for (let i = 0; i < track.points.length - 1; i++) {
const p1 = track.points[i];
const p2 = track.points[i + 1];
const distance = Math.abs((p2.x - p1.x) * (p1.y - car.y) - (p1.x - car.x) * (p2.y - p1.y)) / Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
if (distance < track.width / 2 + car.width / 2) {
return true;
}
}
return false;
}
// Main game loop
function update() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the track
ctx.beginPath();
ctx.strokeStyle = neonBlue;
ctx.lineWidth = track.width;
ctx.moveTo(track.points[0].x, track.points[0].y);
for (let i = 1; i < track.points.length; i++) {
ctx.lineTo(track.points[i].x, track.points[i].y);
}
ctx.stroke();
// Update the car position
car.x += car.vx;
car.y += car.vy;
// Update the car speed
if (car.boost) {
car.speed += 0.1;
} else {
car.speed *= 0.99;
}
car.vx = Math.cos(car.angle) * car.speed;
car.vy = Math.sin(car.angle) * car.speed;
// Check for collision
if (checkCollision(car, track)) {
car.speed *= -0.5;
}
// Draw the car
ctx.fillStyle = neonPink;
ctx.save();
ctx.translate(car.x, car.y);
ctx.rotate(car.angle);
ctx.fillRect(-car.width / 2, -car.height / 2, car.width, car.height);
ctx.restore();
// Handle user input
if (keys['ArrowUp']) {
car.boost = true;
} else {
car.boost = false;
}
if (keys['ArrowLeft']) {
car.angle -= 0.1;
}
if (keys['ArrowRight']) {
car.angle += 0.1;
}
// Request the next frame
requestAnimationFrame(update);
}
// Handle user input
const keys = {};
document.addEventListener('keydown', (e) => {
keys[e.key] = true;
});
document.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
// Start the game loop
update();
</script>
</body>
</html>
```
To run this code, simply copy and paste it into a new HTML file and open it in a web browser. You can control the car using the arrow keys: up to accelerate, left and right to steer. The car will bounce off the track if it collides with it. The game will continue to run until you close the browser window.
Please note that this is a basic implementation and can be improved in many ways, such as adding more tracks, cars, and obstacles, or optimizing the collision detection.```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon Racing Game</title>
<style>
body {
margin: 0;
background-color: #212121;
overflow: hidden;
}
canvas {
width: 100%;
height: 100vh;
display: block;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
// Get the canvas element
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Set the canvas dimensions
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Define the game variables
let playerX = canvas.width / 2;
let playerY = canvas.height / 2;
let playerSpeed = 0;
let playerBoost = 0;
let playerCollision = false;
// Define the track variables
let trackSegments = [];
for (let i = 0; i < 100; i++) {
trackSegments.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
length: Math.random() * 100 + 50,
angle: Math.random() * Math.PI * 2
});
}
// Define the speed boost variables
let speedBoosts = [];
for (let i = 0; i < 10; i++) {
speedBoosts.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
radius: 20
});
}
// Main game loop
function update() {
// Update the player position
playerX += playerSpeed * Math.cos(playerBoost);
playerY += playerSpeed * Math.sin(playerBoost);
// Collision detection
for (let i = 0; i < trackSegments.length; i++) {
let segment = trackSegments[i];
let distance = Math.sqrt(Math.pow(playerX - segment.x, 2) + Math.pow(playerY - segment.y, 2));
if (distance < segment.length / 2 + 10) {
playerCollision = true;
}
}
// Speed boost detection
for (let i = 0; i < speedBoosts.length; i++) {
let boost = speedBoosts[i];
let distance = Math.sqrt(Math.pow(playerX - boost.x, 2) + Math.pow(playerY - boost.y, 2));
if (distance < boost.radius) {
playerSpeed += 0.5;
}
}
// Update the player speed
playerSpeed *= 0.99;
// Draw the game
draw();
}
// Draw the game
function draw() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the track
for (let i = 0; i < trackSegments.length; i++) {
let segment = trackSegments[i];
ctx.beginPath();
ctx.moveTo(segment.x, segment.y);
ctx.lineTo(segment.x + Math.cos(segment.angle) * segment.length, segment.y + Math.sin(segment.angle) * segment.length);
ctx.strokeStyle = '#00ff00';
ctx.stroke();
}
// Draw the speed boosts
for (let i = 0; i < speedBoosts.length; i++) {
let boost = speedBoosts[i];
ctx.beginPath();
ctx.arc(boost.x, boost.y, boost.radius, 0, Math.PI * 2);
ctx.fillStyle = '#ff0000';
ctx.fill();
}
// Draw the player
ctx.beginPath();
ctx.arc(playerX, playerY, 10, 0, Math.PI * 2);
ctx.fillStyle = '#0000ff';
ctx.fill();
// Draw the collision text
if (playerCollision) {
ctx.font = '24px Arial';
ctx.fillStyle = '#ff0000';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Collision Detected!', canvas.width / 2, canvas.height / 2);
}
}
// Handle user input
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
playerSpeed += 0.1;
} else if (e.key === 'ArrowDown') {
playerSpeed -= 0.1;
} else if (e.key === 'ArrowLeft') {
playerBoost -= 0.1;
} else if (e.key === 'ArrowRight') {
playerBoost += 0.1;
}
});
// Start the game loop
setInterval(update, 16);
</script>
</body>
</html>
```# CyberNeon Racing - 3D Cyberpunk Racer
I'll create a standalone HTML file with a cyberpunk-themed 3D racing game featuring neon-lit tracks, speed boosts, and collision physics.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CyberNeon Racing</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Rajdhani', sans-serif;
}
body {
overflow: hidden;
background: #0c0e17;
color: #00f3ff;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
}
#gameCanvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#hud {
position: absolute;
top: 20px;
left: 0;
width: 100%;
padding: 20px;
display: flex;
justify-content: space-between;
z-index: 10;
text-shadow: 0 0 10px #00f3ff;
}
.hud-item {
background: rgba(12, 14, 23, 0.7);
border: 2px solid #00f3ff;
border-radius: 10px;
padding: 10px 20px;
font-size: 24px;
font-weight: bold;
}
#gameTitle {
position: absolute;
top: 30px;
left: 0;
width: 100%;
text-align: center;
font-size: 64px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 10px;
color: #fff;
text-shadow: 0 0 10px #00f3ff, 0 0 20px #00f3ff, 0 0 30px #00a2ff;
z-index: 10;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(12, 14, 23, 0.95);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
#startButton {
margin-top: 50px;
background: transparent;
color: #00f3ff;
border: 3px solid #00f3ff;
padding: 15px 60px;
font-size: 24px;
text-transform: uppercase;
letter-spacing: 4px;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s;
}
#startButton:hover {
background: rgba(0, 243, 255, 0.2);
box-shadow: 0 0 20px rgba(0, 243, 255, 0.5);
}
#instructions {
text-align: center;
margin-top: 30px;
font-size: 18px;
padding: 0 20px;
line-height: 1.6;
max-width: 500px;
}
.key {
display: inline-block;
background: rgba(0, 0, 0, 0.5);
border: 1px solid #00f3ff;
padding: 2px 10px;
border-radius: 5px;
margin: 0 2px;
}
#speedEffect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, rgba(0,200,255,0.4) 0%, transparent 70%);
opacity: 0;
pointer-events: none;
z-index: 5;
transition: opacity 0.3s;
}
#boostBar {
position: absolute;
bottom: 30px;
right: 30px;
width: 100px;
height: 300px;
background: rgba(0,0,0,0.5);
border: 2px solid #00f3ff;
border-radius: 50px;
z-index: 10;
padding: 10px;
display: flex;
flex-direction: column-reverse;
}
#boostFill {
width: 100%;
height: 0%;
background: linear-gradient(to top, #ff00c8, #00f3ff);
border-radius: 30px;
transition: height 0.5s;
}
#boostLabel {
position: absolute;
top: -30px;
right: 0;
color: #00f3ff;
font-weight: bold;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="gameTitle">CYBERNEON RACER</div>
<div id="hud">
<div class="hud-item">SPEED: <span id="speedDisplay">0</span> MPH</div>
<div class="hud-item">LAP: <span id="lapCounter">1</span>/3</div>
<div class="hud-item">BOOST: <span id="boostCount">3</span></div>
</div>
<div id="boostBar">
<div id="boostFill"></div>
</div>
<div id="boostLabel">N₂O</div>
<div id="speedEffect"></div>
<div id="startScreen">
<h1 style="font-size: 60px; letter-spacing: 5px; margin-bottom: 20px;">CYBERNEON RACER</h1>
<div style="border-top: 2px solid #00f3ff; width: 200px; margin: 20px 0;"></div>
<div style="font-size: 28px; color: #ff00c8; margin-bottom: 30px; text-shadow: 0 0 10px #ff00c8;">NEON SPEEDWAY</div>
<div id="instructions">
<p>Navigate through the neon-lit cyberpunk tracks at high speed. Collect boost pads to increase your speed and avoid collisions!</p>
<div style="margin-top: 30px;">
<p>CONTROLS</p>
<p><span class="key">W</span> / <span class="key">↑</span> : Accelerate</p>
<p><span class="key">S</span> / <span class="key">↓</span> : Brake / Reverse</p>
<p><span class="key">A</span> / <span class="key">←</span> : Turn Left</p>
<p><span class="key">D</span> / <span class="key">→</span> : Turn Right</p>
<p><span class="key">SPACE</span> : Activate Boost</p>
</div>
</div>
<button id="startButton">START RACE</button>
</div>
</div>
<script>
// Initialize game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('startScreen');
const speedDisplay = document.getElementById('speedDisplay');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Game variables
let gameRunning = false;
let speed = 0;
let maxSpeed = 200;
let acceleration = 0.2;
let deceleration = 0.05;
let carRotation = 0;
let carX = canvas.width / 2;
let carY = canvas.height - 100;
let trackWidth = 300;
let score = 0;
let laps = 1;
let boost = 100;
let boosts = 3;
let boostActive = false;
// Cyberpunk track boundaries
const roadSegments = [];
const roadWidth = 200;
// Create track segments
function createTrack() {
roadSegments.length = 0;
const segmentHeight = 100;
for (let i = 0; i < 20; i++) {
roadSegments.push({
x: canvas.width / 2,
y: canvas.height - i * segmentHeight,
width: roadWidth,
curve: Math.sin(i * 0.1) * 100
});
}
}
// Draw the track
function drawTrack() {
for (let i = roadSegments.length - 1; i >= 0; i--) {
const segment = roadSegments[i];
// Draw road
ctx.fillStyle = i % 4 < 2 ? '#33334d' : '#2a2a40';
ctx.fillRect(segment.x - segment.width / 2, segment.y - 50, segment.width, 50);
// Draw road markings
ctx.strokeStyle = '#ff5500';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(segment.x, segment.y - 25);
ctx.lineTo(segment.x, segment.y);
ctx.stroke();
// Draw road borders with neon effect
ctx.strokeStyle = '#00f3ff';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(segment.x - segment.width/2, segment.y - 50);
ctx.lineTo(segment.x - segment.width/2, segment.y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(segment.x + segment.width/2, segment.y - 50);
ctx.lineTo(segment.x + segment.width/2, segment.y);
ctx.stroke();
// Neon glow for borders
ctx.shadowBlur = 10;
ctx.shadowColor = '#00f3ff';
ctx.stroke();
ctx.shadowBlur = 0;
}
}
// Draw the car
function drawCar() {
ctx.save();
ctx.translate(carX, carY);
ctx.rotate(carRotation);
// Car body
ctx.fillStyle = '#ff0055';
ctx.fillRect(-25, -15, 50, 30);
// Car details
ctx.fillStyle = '#1188cc';
ctx.fillRect(-20, -12, 40, 24);
// Neon outline
ctx.strokeStyle = '#00f3ff';
ctx.lineWidth = 2;
ctx.strokeRect(-25, -15, 50, 30);
// Headlights
ctx.fillStyle = '#ffffff';
ctx.fillRect(-25, -12, 5, 5);
ctx.fillRect(-25, 7, 5, 5);
// Windscreen
ctx.fillStyle = '#00aaff';
ctx.globalAlpha = 0.5;
ctx.fillRect(-5, -12, 10, 24);
ctx.globalAlpha = 1.0;
ctx.restore();
}
// Draw boost pads
function drawBoostPads() {
for (let i = 0; i < roadSegments.length; i += 2) {
const segment = roadSegments[i];
if (segment.y > carY + 300) continue;
ctx.fillStyle = '#00ff99';
ctx.shadowBlur = 20;
ctx.shadowColor = '#00ff99';
ctx.beginPath();
ctx.arc(segment.x, segment.y - 25, 20, 0, Math.PI * 2);
ctx.fill();
// Arrow icon
ctx.fillStyle = '#000022';
ctx.beginPath();
ctx.moveTo(segment.x - 10, segment.y - 25);
ctx.lineTo(segment.x, segment.y - 15);
ctx.lineTo(segment.x + 10, segment.y - 25);
ctx.lineTo(segment.x, segment.y - 5);
ctx.closePath();
ctx.fill();
ctx.shadowBlur = 0;
}
}
// Draw cityscape background
function drawCityscape() {
for (let i = 0; i < 20; i++) {
const width = 80 + Math.random() * 120;
const height = 100 + Math.random() * 500;
const x = Math.random() * canvas.width;
const hue = Math.round(Math.random() * 20) + 200;
// Building structure
ctx.fillStyle = `hsl(${hue}, 80%, 20%)`;
ctx.fillRect(x, canvas.height - height, width, height);
// Windows
ctx.fillStyle = `hsl(${hue}, 80%, ${20 + Math.random() * 50}%)`;
const windowSize = 8;
const windowMargin = 2;
const gap = windowSize + windowMargin * 2;
for (let y = windowMargin; y < height - gap; y += gap) {
for (let wx = windowMargin; wx < width - gap; wx += gap) {
if (Math.random() > 0.3) {
ctx.fillRect(
x + wx,
canvas.height - height + y,
windowSize,
windowSize
);
}
}
}
}
}
// Draw neon signs
function drawNeonSigns() {
const signs = [
{ text: "RED ROOM", x: 200, y: canvas.height - 200, color: "#ff0055" },
{ text: "CYBERTEK", x: 500, y: canvas.height - 350, color: "#00f3ff" },
{ text: "MEGACORP", x: 800, y: canvas.height - 450, color: "#9600ff" },
{ text: "NEON", x: canvas.width - 200, y: canvas.height - 300, color: "#00ff99" }
];
signs.forEach(sign => {
ctx.font = "bold 32px 'Rajdhani', sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = sign.color;
ctx.shadowBlur = 15;
ctx.shadowColor = sign.color;
ctx.fillText(sign.text, sign.x, sign.y);
});
ctx.shadowBlur = 0;
}
// Check if car is on road
function isOnRoad() {
for (let i = 0; i < roadSegments.length; i++) {
const segment = roadSegments[i];
if (Math.abs(segment.y - carY) < 30) {
const roadLeft = segment.x - segment.width / 2;
const roadRight = segment.x + segment.width / 2;
return carX > roadLeft && carX < roadRight;
}
}
return true;
}
// Handle collision
function handleCollision() {
if (!isOnRoad()) {
// Crash effect
speed *= 0.7;
// Visual effect for collision
ctx.fillStyle = '#ff0066';
ctx.globalAlpha = 0.7;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1.0;
}
}
// Handle boost pad collection
function checkBoostCollection() {
for (let i = 0; i < roadSegments.length; i += 2) {
const segment = roadSegments[i];
if (segment.y > carY + 300) continue;
const dx = Math.abs(segment.x - carX);
const dy = Math.abs(segment.y - 25 - carY);
if (dx < 25 && dy < 25) {
if (boosts < 5) {
boosts++;
document.getElementById('boostCount').innerText = boosts;
}
}
}
}
// Activate boost
function activateBoost() {
if (boosts > 0 && !boostActive && speed > 50) {
boosts--;
document.getElementById('boostCount').innerText = boosts;
boost = 100;
boostActive = true;
setTimeout(() => {
boostActive = false;
}, 3000);
}
}
// Game loop
function gameLoop() {
if (!gameRunning) return;
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background elements
drawCityscape();
drawNeonSigns();
// Update and draw track
for (let i = 0; i < roadSegments.length; i++) {
// Move the track towards the car
roadSegments[i].y += Math.max(1, speed / 10);
// Apply curvature effect
const curve = roadSegments[i].curve;
roadSegments[i].x += curve * 0.01;
// Reset segment when it goes off screen
if (roadSegments[i].y > canvas.height + 50) {
roadSegments[i].y = roadSegments[0].y - 100;
// Random curve for new segment
roadSegments[i].curve = Math.sin(i * 0.1) * 100 * (Math.random() > 0.5 ? 1 : -1);
}
}
drawTrack();
drawBoostPads();
// Handle movement based on rotation and speed
const deltaX = Math.sin(carRotation) * (speed * 0.1);
const deltaY = -Math.cos(carRotation) * (speed * 0.1);
carX += deltaX;
carY += deltaY;
// Slow down the car by default
if (speed > 0) speed -= deceleration;
if (speed < 0) speed += deceleration;
speed = Math.max(-10, Math.min(maxSpeed, speed));
// Apply boost if active
if (boostActive) {
speed += 0.5;
boost = Math.max(boost - 0.5, 0);
document.getElementById('speedEffect').style.opacity = 0.8;
} else {
document.getElementById('speedEffect').style.opacity = 0;
}
// Update boost bar
document.getElementById('boostFill').style.height = boost + "%";
// Update HUD
speedDisplay.innerText = Math.abs(Math.round(speed));
document.getElementById('lapCounter').innerText = laps;
// Check collisions
handleCollision();
// Check for boost pad collection
checkBoostCollection();
// Draw the car
drawCar();
// Reset car position if it goes off screen
if (carX < -50) carX = canvas.width + 50;
if (carX > canvas.width + 50) carX = -50;
// Check if lap completed
if (carY < 0) {
carY = canvas.height;
laps++;
// Increase difficulty
maxSpeed += 20;
}
requestAnimationFrame(gameLoop);
}
// Handle controls
window.addEventListener('keydown', (e) => {
if (!gameRunning) return;
switch (e.key) {
case 'ArrowUp':
case 'w':
case 'W':
speed += acceleration;
break;
case 'ArrowDown':
case 's':
case 'S':
speed -= acceleration * 1.5;
break;
case 'ArrowLeft':
case 'a':
case 'A':
carRotation -= 0.05;
break;
case 'ArrowRight':
case 'd':
case 'D':
carRotation += 0.05;
break;
case ' ':
activateBoost();
break;
}
});
// Handle mobile touch controls
let touchStart = null;
canvas.addEventListener('touchstart', (e) => {
touchStart = e.touches[0];
e.preventDefault();
});
canvas.addEventListener('touchmove', (e) => {
if (!touchStart || !gameRunning) return;
const touch = e.touches[0];
const dx = touch.clientX - touchStart.clientX;
const dy = touch.clientY - touchStart.clientY;
// Horizontal swipe for steering
if (Math.abs(dx) > 10) {
carRotation += dx * 0.01;
}
// Vertical swipe for acceleration/braking
if (dy < -10) {
speed += acceleration * 1.5; // Accelerate
} else if (dy > 10) {
speed -= acceleration * 2; // Brake
}
touchStart = touch;
e.preventDefault();
});
canvas.addEventListener('touchend', () => {
touchStart = null;
});
// Start button event
document.getElementById('startButton').addEventListener('click', () => {
startScreen.style.display = 'none';
gameRunning = true;
createTrack();
gameLoop();
});
// Responsive canvas
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
</script>
</body>
</html>
```
## How to Play the Game
This is a top-down cyberpunk racing game with the following features:
- **Controls**:
- W / Arrow Up: Accelerate
- S / Arrow Down: Brake/Reverse
- A / Arrow Left: Turn Left
- D / Arrow Right: Turn Right
- Space: Activate Boost
- **Game Elements**:
- Drive on the neon-lit cyberpunk track
- Collect green boost pads to gain additional boosts
- Avoid going off the track to prevent crashing
- Use SPACE to activate boost for extra speed
- Complete laps to progress and increase difficulty
- **Features**:
- Neon-lit cyberpunk city backgrounds
- Speed boost pads and boost mechanics
- Collision physics when driving off-road
- Custom HUD showing speed, boost, and lap count
- Immersive neon visuals
Just open this HTML file in any modern browser to play. The game starts on a stylized intro screen and begins when you press the "START RACE" button.```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cyberpunk Racing Game</title>
<style>
body { margin: 0; overflow: hidden; background-color: #0f0f1a; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 30);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Lights for neon effect
const ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
const spotLight = new THREE.SpotLight(0xffaa00, 1.5);
spotLight.position.set(0, 50, 50);
scene.add(spotLight);
// Track (simple polygonal path)
const trackGeometry = new THREE.BufferGeometry();
const trackVertices = [];
trackVertices.push(0, 0, 0);
trackVertices.push(50, 0, 0);
trackVertices.push(50, 0, 25);
trackVertices.push(0, 0, 25);
trackVertices.push(0, 0, 50);
trackVertices.push(50, 0, 50);
trackVertices.push(50, 0, 75);
trackVertices.push(0, 0, 75);
trackVertices.push(0, 0, 100);
trackVertices.push(50, 0, 100);
trackVertices.push(50, 0, 125);
trackVertices.push(0, 0, 125);
trackVertices.push(0, 0, 150);
trackVertices.push(50, 0, 150);
trackVertices.push(50, 0, 175);
trackVertices.push(0, 0, 175);
trackVertices.push(0, 0, 200);
trackVertices.push(50, 0, 200);
trackVertices.push(50, 0, 225);
trackVertices.push(0, 0, 225);
trackVertices.push(0, 0, 250);
trackVertices.push(50, 0, 250);
trackVertices.push(50, 0, 275);
trackVertices.push(0, 0, 275);
trackVertices.push(0, 0, 300);
trackVertices.push(50, 0, 300);
trackVertices.push(50, 0, 325);
trackVertices.push(0, 0, 325);
trackVertices.push(0, 0, 350);
trackVertices.push(50, 0, 350);
trackVertices.push(50, 0, 375);
trackVertices.push(0, 0, 375);
trackVertices.push(0, 0, 400);
trackVertices.push(50, 0, 400);
trackVertices.push(50, 0, 425);
trackVertices.push(0, 0, 425);
trackVertices.push(0, 0, 450);
trackVertices.push(50, 0, 450);
trackVertices.push(50, 0, 475);
trackVertices.push(0, 0, 475);
trackVertices.push(0, 0, 500);
trackVertices.push(50, 0, 500);
trackVertices.push(50, 0, 525);
trackVertices.push(0, 0, 525);
trackVertices.push(0, 0, 550);
trackVertices.push(50, 0, 550);
trackVertices.push(50, 0, 575);
trackVertices.push(0, 0, 575);
trackVertices.push(0, 0, 600);
const track = new THREE.Line(
new THREE.BufferGeometry().setFromPoints(trackVertices.map(v => new THREE.Vector3(v[0], 0, v[1]))),
new THREE.LineBasicMaterial({ color: 0x00ffff })
);
scene.add(track);
// Neon track elements
for (let i = 0; i < trackVertices.length; i++) {
const vertex = new THREE.Vector3(trackVertices[i][0], 0, trackVertices[i][1]);
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(1, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ffff, emissive: 0x00ffff })
);
sphere.position.copy(vertex);
scene.add(sphere);
}
// Obstacles
const obstacles = [];
const obstacleGeometry = new THREE.BoxGeometry(5, 5, 5);
const obstacleMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
for (let i = 0; i < 10; i++) {
const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial);
obstacle.position.set(Math.random() * 100 - 50, 2.5, Math.random() * 100 - 50);
scene.add(obstacle);
obstacles.push(obstacle);
}
// Boost zones
const boostZones = [];
const boostGeometry = new THREE.CylinderGeometry(2, 2, 5, 8);
const boostMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffaa, emissive: 0x00ffaa });
for (let i = 0; i < 5; i++) {
const boost = new THREE.Mesh(boostGeometry, boostMaterial);
boost.position.set(Math.random() * 100 - 50, 0, Math.random() * 100 - 50);
scene.add(boost);
boostZones.push(boost);
}
// Car
const carGeometry = new THREE.BoxGeometry(4, 2, 8);
const carMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const car = new THREE.Mesh(carGeometry, carMaterial);
car.position.set(0, 1, 0);
scene.add(car);
// Controls
const speed = 0.1;
const turnSpeed = 0.05;
let boostActive = false;
let boostTimer = 0;
const keys = {};
window.addEventListener('keydown', (e) => keys[e.key] = true);
window.addEventListener('keyup', (e) => keys[e.key] = false);
// Game loop
function animate() {
requestAnimationFrame(animate);
// Move car
if (keys['ArrowUp']) {
car.position.z -= speed;
}
if (keys['ArrowDown']) {
car.position.z += speed;
}
if (keys['ArrowLeft']) {
car.rotation.y -= turnSpeed;
}
if (keys['ArrowRight']) {
car.rotation.y += turnSpeed;
}
// Apply boost
if (boostActive) {
car.position.z -= speed * 3;
boostTimer -= 1;
if (boostTimer <= 0) {
boostActive = false;
}
}
// Check collisions
for (const obstacle of obstacles) {
const carBox = new THREE.Box3().setFromObject(car);
const obstacleBox = new THREE.Box3().setFromObject(obstacle);
if (carBox.intersectsBox(obstacleBox)) {
alert("Game Over! You crashed into an obstacle.");
document.body.removeChild(renderer.domElement);
return;
}
}
// Check boosts
for (const zone of boostZones) {
const zoneBox = new THREE.Box3().setFromObject(zone);
if (zoneBox.containsPoint(car.position)) {
boostActive = true;
boostTimer = 100;
scene.remove(zone);
}
}
renderer.render(scene, camera);
}
animate();
// Responsive resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Neon Cyber‑Racer</title>
<style>
body { margin:0; overflow:hidden; background:#000; }
canvas { display:block; }
#info {
position:absolute; top:10px; left:10px; color:#0ff;
font-family:monospace; font-size:14px;
}
</style>
<!-- Three.js -->
<script src="https://cdn.jsdelivr.net/npm/three@0.161.0/build/three.min.js"></script>
<!-- Cannon‑ES physics -->
<script src="https://cdn.jsdelivr.net/npm/cannon-es@0.20.0/dist/cannon-es.js"></script>
</head>
<body>
<div id="info">W/A/S/D – drive | Space – boost</div>
<script>
/* ---------- SETUP ---------- */
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000000, 0.04);
// Camera
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000);
camera.position.set(0, 15, -20);
camera.lookAt(0, 0, 0);
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias:true });
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x000000);
document.body.appendChild(renderer.domElement);
// Resize handling
window.addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
/* ---------- LIGHTING ---------- */
const ambient = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambient);
const directional = new THREE.DirectionalLight(0x0099ff, 1.2);
directional.position.set(5, 20, 5);
scene.add(directional);
// Neon strips (simple BoxGeometry with emissive material)
function createNeonStrip(x, y, z, w, h, d, color) {
const geo = new THREE.BoxGeometry(w, h, d);
const mat = new THREE.MeshStandardMaterial({
color: 0x111111,
emissive: color,
emissiveIntensity: 2,
roughness: 0.2,
metalness: 0.7
});
const mesh = new THREE.Mesh(geo, mat);
mesh.position.set(x, y, z);
scene.add(mesh);
return mesh;
}
/* ---------- PHYSICS ---------- */
const world = new CANNON.World({
gravity: new CANNON.Vec3(0, -9.82, 0)
});
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;
/* ---------- TRACK ---------- */
// Ground plane
const groundMat = new CANNON.Material('groundMaterial');
const groundBody = new CANNON.Body({
mass: 0,
material: groundMat,
shape: new CANNON.Plane()
});
groundBody.quaternion.setFromEuler(-Math.PI/2, 0, 0);
world.addBody(groundBody);
const groundVis = new THREE.Mesh(
new THREE.PlaneGeometry(200, 200),
new THREE.MeshStandardMaterial({color:0x111111, side:THREE.DoubleSide})
);
groundVis.rotation.x = -Math.PI/2;
scene.add(groundVis);
// Simple “U” shaped track with walls
const wallMaterial = new CANNON.Material('wall');
function createWall(pos, size) {
const shape = new CANNON.Box(new CANNON.Vec3(size.x/2, size.y/2, size.z/2));
const body = new CANNON.Body({mass:0, material:wallMaterial, shape});
body.position.copy(pos);
world.addBody(body);
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(size.x, size.y, size.z),
new THREE.MeshStandardMaterial({color:0x001133, emissive:0x3322ff, emissiveIntensity:1})
);
mesh.position.copy(pos);
scene.add(mesh);
return {body, mesh};
}
// Left wall
createWall(new CANNON.Vec3(-40,5,0), new CANNON.Vec3(2,10,200));
// Right wall
createWall(new CANNON.Vec3(40,5,0), new CANNON.Vec3(2,10,200));
// Back wall
createWall(new CANNON.Vec3(0,5,-100), new CANNON.Vec3(80,10,2));
/* ---------- CAR ---------- */
const carSize = {x:2, y:1, z:4};
const carShape = new CANNON.Box(new CANNON.Vec3(carSize.x/2, carSize.y/2, carSize.z/2));
const carBody = new CANNON.Body({
mass: 150,
shape: carShape,
angularDamping: 0.5,
linearDamping: 0.1
});
carBody.position.set(0, 2, 80);
world.addBody(carBody);
// Visual car
const carMat = new THREE.MeshStandardMaterial({
color: 0x2222ff,
emissive: 0x00ffff,
emissiveIntensity: 1.5,
metalness: 0.7,
roughness: 0.3
});
const carMesh = new THREE.Mesh(
new THREE.BoxGeometry(carSize.x, carSize.y, carSize.z),
carMat
);
scene.add(carMesh);
/* ---------- INPUT ---------- */
const keys = {w:false,a:false,s:false,d:false,space:false};
window.addEventListener('keydown', e => {
if(e.code=== 'KeyW') keys.w = true;
if(e.code=== 'KeyA') keys.a = true;
if(e.code=== 'KeyS') keys.s = true;
if(e.code=== 'KeyD') keys.d = true;
if(e.code=== 'Space') keys.space = true;
});
window.addEventListener('keyup', e => {
if(e.code=== 'KeyW') keys.w = false;
if(e.code=== 'KeyA') keys.a = false;
if(e.code=== 'KeyS') keys.s = false;
if(e.code=== 'KeyD') keys.d = false;
if(e.code=== 'Space') keys.space = false;
});
/* ---------- PHYSICS PARAMETERS ---------- */
const maxSpeed = 30;
const acceleration = 40;
const brakeForce = 80;
const turnSpeed = 2.5;
const boostForce = 120;
let boostCooldown =;
/* ---------- ANIMATION LOOP ---------- */
function animate() {
requestAnimationFrame(animate);
// --- Controls ---
const forward = keys.w ? 1 : (keys.s ? -1 : 0);
const turn = keys.a ? 1 : (keys.d ? -1 : 0);
// Get car forward direction (ignoring y)
const forwardDir = new CANNON.Vec3(
-Math.sin(carBody.quaternion.y),
0,
-Math.cos(carBody.quaternion.y)
);
// Simplified: use quaternion to get yaw
const yaw = carBody.quaternion.toEuler().y; // approximate
const dirX = Math.sin(yaw);
const dirZ = Math.cos(yaw);
const moveVec = new CANNON.Vec3(dirX, 0, dirZ).scale(forward * acceleration);
if (forward !== 0) {
carBody.applyForce(moveVec, carBody.position);
} else {
// natural slowing
const vel = carBody.velocity.clone();
vel.y = 0;
const speed = vel.length();
if (speed > 0.1) {
const brake = vel.scale(-brakeForce);
carBody.applyForce(brake, carBody.position);
}
}
// Turning (apply torque around y)
if (turn !== 0) {
carBody.angularVelocity.y += turn * turnSpeed * 0.02;
}
// Boost
if (keys.space && boostCooldown <= 0) {
const boostVec = new CANNON.Vec3(dirX, 0, dirZ).scale(boostForce);
carBody.applyForce(boostVec, carBody.position);
boostCooldown = 2; // seconds
}
// Cooldown timer
if (boostCooldown > 0) boostCooldown -= 1/60;
// --- Physics step ---
world.step(1/60);
// --- Sync visual car ---
carMesh.position.copy(carBody.position);
carMesh.quaternion.copy(carBody.quaternion);
// --- Camera follow ---
const camOffset = new THREE.Vector3(0, 8, -12);
const camTarget = new THREE.Vector3().copy(carMesh.position);
const camPosition = camTarget.clone().add(camOffset.applyQuaternion(carMesh.quaternion));
camera.position.lerp(camPosition, 0.1);
camera.lookAt(carMesh.position);
// --- Render ---
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
```