0% found this document useful (0 votes)
20 views14 pages

3D Car Selection Screen Design

The document outlines the HTML structure and styling for a simple 3D racing game car selection interface. It includes a splash screen, car selection options with previews, and game information display. The JavaScript section initializes the game environment using Three.js, allowing users to select different car types and manage game controls.

Uploaded by

Fahmi
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views14 pages

3D Car Selection Screen Design

The document outlines the HTML structure and styling for a simple 3D racing game car selection interface. It includes a splash screen, car selection options with previews, and game information display. The JavaScript section initializes the game environment using Three.js, allowing users to select different car types and manage game controls.

Uploaded by

Fahmi
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

<!

DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Simple 3D Racer V7 - Car Selection</title>
<!-- Changed Title -->
<style>
body {
margin: 0;
overflow: hidden;
background-color: #333; /* Darker background initially */
color: white;
font-family: Arial, sans-serif;
}
canvas {
display: block; /* Will be managed by JS */
}
/* --- UI Screens --- */
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
z-index: 100; /* High z-index */
}
#splash-screen {
background-color: #222; /* Dark splash */
font-size: 2em;
transition: opacity 0.5s ease-out; /* Fade out effect */
}
#car-select-screen {
background-color: rgba(0, 0, 50, 0.8); /* Semi-transparent blue */
display: none; /* Hidden initially */
padding: 20px;
box-sizing: border-box;
}
#car-select-screen h2 {
margin-bottom: 30px;
}
#car-options {
display: flex;
justify-content: center;
gap: 30px; /* Space between options */
flex-wrap: wrap; /* Allow wrapping on smaller screens */
}
.car-option {
padding: 15px 25px;
border: 2px solid white;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
background-color: rgba(255, 255, 255, 0.1);
min-width: 120px; /* Ensure buttons have some width */
}
.car-option:hover {
background-color: rgba(255, 255, 255, 0.3);
transform: scale(1.05);
}
/* Style previews (simple colored boxes for now) */
.car-preview {
width: 50px;
height: 25px;
margin: 0 auto 10px auto; /* Center preview above text */
border: 1px solid #ccc;
}
.preview-red {
background-color: #a52523;
}
.preview-blue {
background-color: #2355a5;
}
.preview-green {
background-color: #23a55e;
}
.preview-yellow {
background-color: #d4c831;
}

#info {
/* Game UI */
position: absolute;
top: 10px;
left: 10px;
color: white;
font-family: Arial, sans-serif;
background-color: rgba(0, 0, 0, 0.5);
padding: 5px 10px;
border-radius: 3px;
z-index: 10;
display: none; /* Hide until game starts */
}
#info::after {
content: "\A Use Mouse Drag to Orbit Camera";
white-space: pre;
display: block;
margin-top: 4px;
font-size: 0.9em;
}
</style>
</head>
<body>
<!-- UI Screens -->
<div id="splash-screen" class="overlay">
<p>Loading Racer...</p>
</div>

<div id="car-select-screen" class="overlay">


<h2>Select Your Car</h2>
<div id="car-options">
<div class="car-option" data-car-type="racer">
<div class="car-preview preview-red"></div>
Red Racer
</div>
<div class="car-option" data-car-type="truck">
<div class="car-preview preview-blue"></div>
Blue Truck
</div>
<div class="car-option" data-car-type="sporty">
<div class="car-preview preview-green"></div>
Green Sporty
</div>
<div class="car-option" data-car-type="buggy">
<div class="car-preview preview-yellow"></div>
Yellow Buggy
</div>
</div>
</div>

<!-- Game Info Display -->


<div id="info">
Use WASD or Arrow Keys to drive.<br />
Speed: <span id="speed">0.00</span>
</div>

<!-- [Link] library -->


<script
src="[Link]
<!-- OrbitControls Add-on -->
<script
src="[Link]
script>

<script>
// --- Global Scope Variables ---
let scene, camera, renderer, car, ground, clock, orbitControls;
let selectedCarType = "racer"; // Default car type

// Game state / UI elements


const splashScreen = [Link]("splash-screen");
const carSelectScreen = [Link]("car-select-screen");
const gameInfoUI = [Link]("info");
const carOptions = [Link](".car-option");

// --- GAME VARIABLES ---


const carSpeed = {
current: 0,
max: 15,
acceleration: 8,
braking: 15,
friction: 3,
handling: 1.5,
};
const controls = {
forward: false,
backward: false,
left: false,
right: false,
};
const cameraFollowOffset = new THREE.Vector3(0, 5, -10);
const cameraLookAtOffset = new THREE.Vector3(0, 1.0, 0);
const maxSteerAngle = [Link] / 6;
const steerLerpFactor = 8;

// --- INITIALIZATION (Called AFTER car selection) ---


function init(carType) {
// Set background back to sky blue for the game
[Link] = "#77b5fe";

clock = new [Link](); // Initialize clock here

// Scene, Camera, Renderer


scene = new [Link]();
[Link] = new [Link](0x77b5fe);
[Link] = new [Link](0x77b5fe, 50, 150);
camera = new [Link](
75,
[Link] / [Link],
0.1,
1000
);
renderer = new [Link]({ antialias: true });
[Link]([Link], [Link]);
[Link] = true;
[Link] = [Link];
// *** Append renderer canvas to body NOW ***
[Link]([Link]);

// Lighting
const ambientLight = new [Link](0xffffff, 0.7);
[Link](ambientLight);
const directionalLight = new [Link](0xffffff, 1.0);
[Link](70, 60, 40);
[Link] = true;
[Link] = 2048;
[Link] = 2048;
[Link] = 0.5;
[Link] = 200;
[Link] = -80;
[Link] = 80;
[Link] = 80;
[Link] = -80;
[Link] = -0.0005;
[Link](directionalLight);

// Ground
const groundGeometry = new [Link](200, 200);
const groundMaterial = new [Link]({
color: 0x448844,
side: [Link],
roughness: 0.8,
metalness: 0.2,
});
ground = new [Link](groundGeometry, groundMaterial);
[Link].x = -[Link] / 2;
[Link] = true;
[Link](ground);

// --- CREATE SELECTED CAR ---


car = createCar(carType); // Pass the selected type
[Link](0, [Link] || 0.35, 0);
[Link](car);

// --- SETUP ORBIT CONTROLS ---


orbitControls = new [Link](camera, [Link]);
const initialTarget = [Link]().add(cameraLookAtOffset);
[Link](initialTarget);
const initialCameraPos = initialTarget
.clone()
.add([Link]().applyQuaternion([Link]));
[Link](initialCameraPos);
[Link] = true;
[Link] = 0.1;
[Link] = 4;
[Link] = 40;
[Link] = [Link] / 2 - 0.05;
[Link]();

// Boundaries
createBoundary(-50, 0, 100, 1);
createBoundary(50, 0, 100, 1);
createBoundary(0, -50, 1, 100);
createBoundary(0, 50, 1, 100);

// Show Game UI
[Link] = "block";

// Event Listeners for Game


[Link]("keydown", handleKeyDown);
[Link]("keyup", handleKeyUp);
[Link]("resize", onWindowResize);

// Start the game loop


animate();
}

// --- CAR CREATION (Handles different types) ---


function createCar(type = "racer") {
// Default to racer if type is missing
const carGroup = new [Link]();
[Link] = [];
[Link] = [];

let carColor, cabinColor, wheelColor, glassColor;


let bodyGeo, bodyMat, bodyMesh;
let cabinGeo, cabinMat, cabinMesh;
let windshieldGeo, windshieldMat, windshieldMesh;
let wheelRadius = 0.35,
wheelThickness = 0.25; // Default dimensions
let wheelY, wheelXOffset, wheelZOffset;

const wheelSegments = 16;


glassColor = 0x6699cc; // Common glass color

// Define base materials


const defaultWheelMaterial = new [Link]({
color: 0x111111,
roughness: 0.8,
metalness: 0.1,
});
const defaultGlassMaterial = new [Link]({
color: glassColor,
roughness: 0.1,
metalness: 0.0,
transmission: 0.9,
transparent: true,
opacity: 0.6,
});

// --- Car Type Specific Logic ---


switch (type) {
case "truck":
carColor = 0x2355a5; // Blue
cabinColor = 0xcccccc; // Light grey
wheelColor = 0x222222; // Darker wheels
wheelRadius = 0.45; // Larger wheels
wheelThickness = 0.3;
wheelXOffset = 0.7;
wheelZOffset = 1.1;

// Body (Blockier)
bodyGeo = new [Link](1.6, 0.8, 3.0);
bodyMat = new [Link]({
color: carColor,
roughness: 0.6,
metalness: 0.4,
});
bodyMesh = new [Link](bodyGeo, bodyMat);
[Link].y = 0.4; // Higher base
[Link](bodyMesh);

// Cabin (Taller, more forward)


cabinGeo = new [Link](1.2, 0.9, 1.4);
cabinMat = new [Link]({
color: cabinColor,
roughness: 0.7,
});
cabinMesh = new [Link](cabinGeo, cabinMat);
[Link].y = 0.8 + 0.45; // Position on top of body
[Link].z = 0.1; // More forward placement
[Link](cabinMesh);

// Windshield (More upright)


windshieldGeo = new [Link](1.1, 0.7, 0.1);
windshieldMesh = new [Link](
windshieldGeo,
defaultGlassMaterial
);
[Link].y = 0.8 + 0.45;
[Link].z = 1.4 / 2 + 0.1 + 0.1 / 2; // Front of cabin
[Link].x = [Link] / 18; // Less slant
[Link](windshieldMesh);
break;

case "sporty":
carColor = 0x23a55e; // Green
cabinColor = 0xdddddd;
wheelColor = 0x1a1a1a;
wheelRadius = 0.3; // Smaller wheels
wheelThickness = 0.28;
wheelXOffset = 0.7; // Wider stance
wheelZOffset = 1.0;

// Body (Lower, wider)


bodyGeo = new [Link](1.5, 0.4, 3.2);
bodyMat = new [Link]({
color: carColor,
roughness: 0.3,
metalness: 0.7,
});
bodyMesh = new [Link](bodyGeo, bodyMat);
[Link].y = 0.2; // Lower base
[Link](bodyMesh);

// Cabin (Sleek, further back)


cabinGeo = new [Link](0.8, 0.5, 1.5); // Longer cabin
cabinMat = new [Link]({
color: cabinColor,
roughness: 0.5,
});
cabinMesh = new [Link](cabinGeo, cabinMat);
[Link].y = 0.4 + 0.25; // Top of body + half height
[Link].z = -0.5; // Further back
[Link](cabinMesh);

// Windshield (Very slanted)


windshieldGeo = new [Link](0.75, 0.4, 0.1);
windshieldMesh = new [Link](
windshieldGeo,
defaultGlassMaterial
);
[Link].y = 0.4 + 0.25;
[Link].z = 1.5 / 2 - 0.5 + 0.1 / 2; // Front of cabin
[Link].x = [Link] / 5; // More slant
[Link](windshieldMesh);

// Simple Spoiler
const spoilerGeo = new [Link](1.4, 0.1, 0.3);
const spoilerMat = new [Link]({
color: carColor,
roughness: 0.4,
metalness: 0.6,
});
const spoilerMesh = new [Link](spoilerGeo, spoilerMat);
[Link](0, 0.6, -1.5); // Behind body
[Link].x = -[Link] / 12; // Slight downward angle
[Link](spoilerMesh);
break;

case "buggy":
carColor = 0xd4c831; // Yellow
cabinColor = 0x555555; // Darker frame elements
wheelColor = 0x333333;
wheelRadius = 0.4; // Medium-large wheels
wheelThickness = 0.35; // Thicker wheels
wheelXOffset = 0.75; // Wide stance
wheelZOffset = 0.7; // Shorter wheelbase
// Body (Minimal chassis)
bodyGeo = new [Link](1.0, 0.3, 1.8);
bodyMat = new [Link]({
color: carColor,
roughness: 0.7,
metalness: 0.3,
});
bodyMesh = new [Link](bodyGeo, bodyMat);
[Link].y = 0.15; // Very low
[Link](bodyMesh);

// "Cabin" (Roll cage elements - simplified with boxes)


const cageMat = new [Link]({
color: cabinColor,
roughness: 0.5,
});
const pillarGeo = new [Link](0.1, 0.8, 0.1);
// Front pillars
const pillarFL = new [Link](pillarGeo, cageMat);
[Link](-0.45, 0.15 + 0.4, 0.4);
[Link](pillarFL);
const pillarFR = new [Link](pillarGeo, cageMat);
[Link](0.45, 0.15 + 0.4, 0.4);
[Link](pillarFR);
// Rear pillars
const pillarRL = new [Link](pillarGeo, cageMat);
[Link](-0.45, 0.15 + 0.4, -0.6);
[Link](pillarRL);
const pillarRR = new [Link](pillarGeo, cageMat);
[Link](0.45, 0.15 + 0.4, -0.6);
[Link](pillarRR);
// Top bars
const topBarGeo = new [Link](1.0, 0.1, 0.1);
const topBarL = new [Link](topBarGeo, cageMat);
[Link](-0.45, 0.15 + 0.8, -0.1);
[Link](topBarL);
const topBarR = new [Link](topBarGeo, cageMat);
[Link](0.45, 0.15 + 0.8, -0.1);
[Link](topBarR);
const topBarCrossGeo = new [Link](0.1, 0.1, 1.1);
const topBarF = new [Link](topBarCrossGeo, cageMat);
[Link](0, 0.15 + 0.8, 0.4);
[Link](topBarF);
const topBarRe = new [Link](topBarCrossGeo, cageMat);
[Link](0, 0.15 + 0.8, -0.6);
[Link](topBarRe);

// No windshield for buggy


break;

case "racer": // Default / Explicit Racer


default:
carColor = 0xa52523; // Red
cabinColor = 0xffffff;
wheelColor = 0x111111;
// Use default wheel dimensions set earlier
wheelRadius = 0.35;
wheelThickness = 0.25;
wheelXOffset = 0.65;
wheelZOffset = 0.9;

// Original Body
bodyGeo = new [Link](1.3, 0.5, 2.8);
bodyMat = new [Link]({
color: carColor,
roughness: 0.4,
metalness: 0.6,
});
bodyMesh = new [Link](bodyGeo, bodyMat);
[Link].y = 0.25;
[Link](bodyMesh);

// Original Cabin
cabinGeo = new [Link](0.9, 0.6, 1.2);
cabinMat = new [Link]({
color: cabinColor,
roughness: 0.6,
metalness: 0.4,
});
cabinMesh = new [Link](cabinGeo, cabinMat);
[Link].y = 0.5 + 0.3;
[Link].z = -0.4;
[Link](cabinMesh);

// Original Windshield
windshieldGeo = new [Link](0.85, 0.4, 0.1);
windshieldMesh = new [Link](
windshieldGeo,
defaultGlassMaterial
);
[Link].y = 0.5 + 0.3;
[Link].z = 1.2 / 2 - 0.4 + 0.1 / 2;
[Link].x = [Link] / 9;
[Link](windshieldMesh);
break;
}

// --- Common Car Setup (Applies to all types) ---


// Make sure body and cabin cast shadows if they exist
if (bodyMesh) [Link] = true;
if (cabinMesh) [Link] = true;

// Set final wheel properties based on selected type


[Link] = wheelRadius;
wheelY = wheelRadius; // Position wheels relative to ground

// Wheels (Geometry/Material/Placement - uses variables set in switch)


const wheelGeometry = new [Link](
wheelRadius,
wheelRadius,
wheelThickness,
wheelSegments
);
const wheelMaterial = new [Link]({
color: wheelColor || 0x111111,
roughness: 0.8,
metalness: 0.1,
}); // Use specific or default
const finalWheelPositions = [
{ x: -wheelXOffset, y: wheelY, z: wheelZOffset, isFront: true },
{ x: wheelXOffset, y: wheelY, z: wheelZOffset, isFront: true },
{ x: -wheelXOffset, y: wheelY, z: -wheelZOffset, isFront: false },
{ x: wheelXOffset, y: wheelY, z: -wheelZOffset, isFront: false },
];

[Link]((posData) => {
const wheelMesh = new [Link](wheelGeometry, wheelMaterial);
[Link](posData.x, posData.y, posData.z);
[Link] = "YXZ";
[Link].z = [Link] / 2; // Stand upright
[Link] = 0; // Initialize roll accumulator
[Link] = true;
[Link](wheelMesh);
[Link](wheelMesh);
if ([Link]) {
[Link](wheelMesh);
}
});

return carGroup;
}

function createBoundary(x, z, width, depth) {


/* Unchanged */
const boundaryGeo = new [Link](width, 2, depth);
const boundaryMat = new [Link]({
color: 0x777777,
roughness: 0.9,
metalness: 0.1,
});
const boundary = new [Link](boundaryGeo, boundaryMat);
[Link](x, 1, z);
[Link] = true;
[Link](boundary);
}

// --- EVENT HANDLERS for GAMEPLAY --- (Unchanged)


function handleKeyDown(event) {
/* ... same as before ... */
}
function handleKeyUp(event) {
/* ... same as before ... */
}
function onWindowResize() {
/* ... same as before ... */
}
// (Code for keydown/keyup/resize is identical to previous version, omitted
here for brevity)
function handleKeyDown(event) {
switch ([Link]) {
case "w":
case "ArrowUp":
[Link] = true;
break;
case "s":
case "ArrowDown":
[Link] = true;
break;
case "a":
case "ArrowLeft":
[Link] = true;
break;
case "d":
case "ArrowRight":
[Link] = true;
break;
}
}
function handleKeyUp(event) {
switch ([Link]) {
case "w":
case "ArrowUp":
[Link] = false;
break;
case "s":
case "ArrowDown":
[Link] = false;
break;
case "a":
case "ArrowLeft":
[Link] = false;
break;
case "d":
case "ArrowRight":
[Link] = false;
break;
}
}
function onWindowResize() {
if (camera && renderer) {
// Check if they exist (game might not be initialized yet)
[Link] = [Link] / [Link];
[Link]();
[Link]([Link], [Link]);
}
}

// --- GAME LOGIC UPDATE --- (Unchanged logic, uses the created 'car')
function updateCar(deltaTime) {
/* ... same as before ... */
}
// (Code for updateCar is identical to previous version, omitted here for
brevity)
function updateCar(deltaTime) {
// Physics
let acceleration = 0;
if ([Link]) {
acceleration = [Link];
} else if ([Link]) {
acceleration =
[Link] > 0.1
? -[Link]
: -[Link] * 0.7;
}
[Link] += acceleration * deltaTime;
if (![Link] && ![Link]) {
const frictionDirection = [Link]([Link]);
if (frictionDirection !== 0) {
const speedChange = [Link] * deltaTime;
[Link] -= frictionDirection * speedChange;
if ([Link]([Link]) !== frictionDirection) {
[Link] = 0;
}
}
}
[Link] = [Link](
-[Link] * 0.5,
[Link]([Link], [Link])
);

// Car Body Steering


let bodySteeringInput = 0;
if ([Link]) {
bodySteeringInput = [Link];
}
if ([Link]) {
bodySteeringInput = -[Link];
}
const speedFactor = [Link](
1,
[Link]([Link]) / ([Link] * 0.5) + 0.3
);
const bodySteeringAmount = bodySteeringInput * speedFactor * deltaTime;
if (
[Link]([Link]) > 0.01 ||
[Link] ||
[Link]
) {
[Link].y +=
bodySteeringAmount * ([Link] >= 0 ? 1 : -1);
}

// Movement
const localMoveZ = [Link] * deltaTime;
[Link](localMoveZ);

// Wheel Animations
if ([Link] && [Link]) {
const distanceMoved = localMoveZ;
const deltaWheelRotation = distanceMoved / [Link];
let targetSteerAngle = 0;
if ([Link]) {
targetSteerAngle = maxSteerAngle;
} else if ([Link]) {
targetSteerAngle = -maxSteerAngle;
}

[Link]((wheel) => {
const isFront = [Link](wheel);
[Link] -= deltaWheelRotation;
if (isFront) {
[Link].y = [Link](
[Link].y,
targetSteerAngle,
steerLerpFactor * deltaTime
);
} else {
[Link].y = 0;
}
[Link].x = [Link];
});
}

// Update UI
if ([Link] === "block") {
// Only update if visible
[Link]("speed").textContent = [Link](
[Link]
).toFixed(2);
}
}

// --- CAMERA UPDATE --- (Unchanged logic)


function updateCamera(deltaTime) {
/* ... same as before ... */
}
// (Code for updateCamera is identical to previous version, omitted here for
brevity)
function updateCamera(deltaTime) {
if (!orbitControls || !car) return; // Safety check if called before init
finishes
const targetPosition = [Link]().add(cameraLookAtOffset);
[Link](targetPosition, 5 * deltaTime);
const desiredCameraPosition = [Link]
.clone()
.add([Link]().applyQuaternion([Link]));
const distanceToTarget = [Link](
[Link]
);
const desiredDistance = [Link]();
const distanceThreshold = 0.1;
if (
[Link](distanceToTarget - desiredDistance) <
distanceThreshold * desiredDistance
) {
[Link](desiredCameraPosition, 4 * deltaTime);
}
}

// --- ANIMATION LOOP ---


function animate() {
// Only run if game objects exist (init has been called)
if (!renderer || !scene || !camera || !car) {
[Link]("Game not fully initialized, skipping animation frame.");
return; // Exit if game isn't ready
}
requestAnimationFrame(animate); // Request next frame *conditionally* or
always? Always is safer.
// Let's request always, but logic inside handles if ready.
// requestAnimationFrame(animate);

const deltaTime = [Link]();


updateCar(deltaTime);
updateCamera(deltaTime);

[Link](); // MUST be called after camera/target updates

[Link](scene, camera);
}

// --- UI Flow & Game Start ---


// 1. Show Splash Screen
[Link] = 1;
[Link] = "none";
[Link] = "none";

// 2. After a delay, hide splash, show car select


setTimeout(() => {
[Link] = 0;
// Use another timeout to ensure display:none happens *after* fade
setTimeout(() => {
[Link] = "none";
[Link] = "flex"; // Show car select screen
}, 500); // Match CSS transition duration
}, 2000); // Splash screen duration (2 seconds)

// 3. Handle Car Selection Clicks


[Link]((button) => {
[Link]("click", () => {
selectedCarType = [Link]("data-car-type");
[Link]("Selected car:", selectedCarType);

// Hide selection screen


[Link] = "none";

// --- Initialize and start the game ---


try {
init(selectedCarType); // Pass selection to init
} catch (error) {
[Link]("Error initializing game:", error);
// Optional: Show an error message to the user
[Link] =
'<p style="color:red; padding: 20px;">Error loading game. Please
check console.</p>';
}
});
});

// NOTE: We DO NOT call init() or animate() directly here anymore.


// init() is called via the car selection button click.
// animate() is called at the end of init().
</script>
</body>
</html>

You might also like