Google Map Driving Game 2021 Instant
/* Main game container */ .game-container position: relative; width: 100%; height: 100%;
.ctrl-btn background: #2c3e44; border: none; color: white; font-size: 2rem; font-weight: bold; width: 70px; height: 70px; border-radius: 60px; cursor: pointer; transition: all 0.1s ease; box-shadow: 0 4px 0 #0f1a1f; font-family: monospace; display: flex; align-items: center; justify-content: center; touch-action: manipulation;
.stats-panel span color: #ffaa33; font-size: 1.5rem; margin-right: 6px; google map driving game
.info-text background: rgba(0,0,0,0.6); border-radius: 20px; padding: 8px 16px; font-size: 0.8rem; color: #ddd; font-family: monospace; pointer-events: auto;
// Game state variables let map; let carMarker; let destinationMarker; let currentPosition; // LatLng object let destinationPosition; let score = 0; let gameActive = true; let snapService; // for road snapping let lastSnapRequest = null; // Movement tuning const MOVE_STEP_METERS = 18; // each tap moves ~18 meters (smooth driving) const SNAP_RADIUS_METERS = 40; // snap to nearest road within 40m // UI elements const scoreEl = document.getElementById('scoreValue'); const distEl = document.getElementById('distValue'); // Helper: meters to readable string function formatDistance(meters) if (meters < 1000) return `$Math.floor(meters) m`; return `$(meters / 1000).toFixed(1) km`; // Update distance display & score (score based on distance covered from start) let totalDrivenMeters = 0; let lastPositionForDistance = null; function updateDistanceAndScore(newPos) if (!lastPositionForDistance // Snap to nearest road using Google Maps Roads API (snapToRoads) OR fallback to geometry library // Since Roads API requires extra key/permissions, we implement a clever snap using the 'geometry' library // and a hidden 'snap-to-road' using Directions API? simpler: we fetch the nearest point on a road segment by using // the `google.maps.geometry.poly.isLocationOnEdge`? Not robust. // Better: use Google Maps Roads API (snapToRoads) but that needs separate enablement. We'll simulate robust snapping // using a DirectionsService intermediate point? Not ideal. Instead implement a clean "clamp to road" by using // the `nearestRoads` trick: we'll call the Distance Matrix? No. // To make game functional and fun without external API key complexities, we implement an elegant "road snap" using // the `google.maps.RoadsService`? Actually Roads API requires additional API. For this demo to work without extra config, // we will use the Places Autocomplete or just rely on the fact the user starts on a road, and we manually adjust by fetching // the nearest road using the `SnapToRoads` from google maps? Simpler: we will use the `directionsService` to get the closest // point along a road? Too heavy. I'll implement a mock road snap that just corrects the car by a tiny offset to stay on // plausible roads — but for real driving game effect, we use a hybrid: On each move, we query the Roads API if available, // but to ensure the demo works out-of-box with a basic API key (Maps JS + Places), we implement a "Safe snap" that uses // the `nearestRoads` using Google's sample method: we can use the `google.maps.RoadsService`? It needs `roads` library. // I decide to build a custom Snap Helper: Using the DirectionsService to "snap" to the nearest point on a drivable path // between two nearby points. However, for simplicity and robust offline-ish feel, I'll implement a "stay on road" mechanism: // We store last valid snapped position. For each move, we compute next point based on heading, then use the `google.maps.geometry.poly` // to check if the point is near any road? That's heavy. // Instead, the most professional: I'll embed a fallback that uses the `PlacesService` to find nearby roads? Not perfect. // Given time, I'll make a function that uses Directions API to get a route from current position to a point ahead, // then extract the nearest polyline point, making the car snap exactly to the route path. That ensures the car follows roads! // This is elegant and works with standard Maps API key without extra Roads API. let directionsService; let currentRoutePath = []; // array of latLng for last computed route let currentHeading = 0; // in degrees // Initialize direction service and get initial road lock function initRoadSnapping() directionsService = new google.maps.DirectionsService(); // Given current position and desired target (next position candidate), snap to nearest road point using DirectionsService // by requesting a short route from current to a point ~50m ahead in desired direction, then extract first point. async function snapToRoad(currentLatLng, desiredNextLatLng) return new Promise((resolve) => // If no directionsService, resolve original if (!directionsService) resolve(desiredNextLatLng); return; // Create a small route request from current to desired point (max distance ~80m) const request = origin: currentLatLng, destination: desiredNextLatLng, travelMode: google.maps.TravelMode.DRIVING, provideRouteAlternatives: false, unitSystem: google.maps.UnitSystem.METRIC ; directionsService.route(request, (result, status) => if (status === google.maps.DirectionsStatus.OK && result.routes[0] && result.routes[0].overview_path.length > 0) const path = result.routes[0].overview_path; // find the point in the route closest to desiredNextLatLng (usually the last or near) let closestPoint = path[path.length-1]; let minDist = google.maps.geometry.spherical.computeDistanceBetween(closestPoint, desiredNextLatLng); for (let i=0; i<path.length; i++) const d = google.maps.geometry.spherical.computeDistanceBetween(path[i], desiredNextLatLng); if (d < minDist) minDist = d; closestPoint = path[i]; // Also store route for potential heading if (path.length >= 2) const idx = Math.min(path.length-1, 2); currentHeading = google.maps.geometry.spherical.computeHeading(currentLatLng, path[idx]); resolve(closestPoint); else // fallback: move straight but ensure no major deviation resolve(desiredNextLatLng); ); ); // Move car in a given direction (forward/backward/steer left/right) // direction: 'forward', 'backward', 'left', 'right' async function moveCar(direction) !currentPosition) return; let newLatLng; const stepMeters = MOVE_STEP_METERS; let heading = currentHeading; // Get current heading based on movement direction if (direction === 'forward') // maintain current heading else if (direction === 'backward') heading = (heading + 180) % 360; else if (direction === 'left') heading = (heading - 25) % 360; currentHeading = heading; // just rotate, no forward movement? For arcade feel, left/right rotates + small move? We'll do rotate + tiny forward to feel responsive. // but better: rotate without moving? Driving game: left/right turn while moving. So we combine: change heading and also move forward small step. // We'll call move again after rotation? Let's implement: for left/right, we rotate and then move forward by stepMeters*0.7 const rotatedPos = getPointAtDistance(currentPosition, heading, stepMeters * 0.7); newLatLng = await snapToRoad(currentPosition, rotatedPos); if (newLatLng) await executeMoveUpdate(newLatLng); return; else if (direction === 'right') heading = (heading + 25) % 360; currentHeading = heading; const rotatedPos = getPointAtDistance(currentPosition, heading, stepMeters * 0.7); newLatLng = await snapToRoad(currentPosition, rotatedPos); if (newLatLng) await executeMoveUpdate(newLatLng); return; // For forward & backward: compute displacement const displacementMeters = (direction === 'forward') ? stepMeters : -stepMeters; const rawNext = getPointAtDistance(currentPosition, heading, displacementMeters); newLatLng = await snapToRoad(currentPosition, rawNext); if (newLatLng) await executeMoveUpdate(newLatLng); // Helper: compute point from origin at given heading (degrees) and distance meters function getPointAtDistance(origin, headingDeg, distanceMeters) const radius = 6371000; // Earth radius in meters const lat1 = origin.lat() * Math.PI / 180; const lon1 = origin.lng() * Math.PI / 180; const brng = headingDeg * Math.PI / 180; const d = distanceMeters; const lat2 = Math.asin(Math.sin(lat1) * Math.cos(d/radius) + Math.cos(lat1) * Math.sin(d/radius) * Math.cos(brng)); const lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(d/radius) * Math.cos(lat1), Math.cos(d/radius) - Math.sin(lat1) * Math.sin(lat2)); return new google.maps.LatLng(lat2 * 180 / Math.PI, lon2 * 180 / Math.PI); // Execute move: update marker, map view, scoring async function executeMoveUpdate(newPosition) if (!newPosition) return; const oldPos = currentPosition; currentPosition = newPosition; carMarker.setPosition(currentPosition); // Update distance & score updateDistanceAndScore(currentPosition); // Smoothly center map on car (pan) map.panTo(currentPosition); // Rotate car marker icon based on heading const iconSymbol = path: 'M-12,0 L0,-18 L12,0 L0,18 Z', fillColor: '#ff4444', fillOpacity: 1, strokeWeight: 2, strokeColor: '#ffffff', scale: 1.2, rotation: currentHeading ; carMarker.setIcon(iconSymbol); // small haptic feedback on mobile? optional // Reset game: set random destination 0.5-2km away from start along drivable roads async function resetGame() !currentPosition) return; gameActive = true; score = 0; totalDrivenMeters = 0; scoreEl.innerText = "0"; lastPositionForDistance = currentPosition; // Generate random destination ~ 500m to 2500m from current position (drivable) const randomAngle = Math.random() * 360; const randomDistance = 600 + Math.random() * 1800; // meters const rawDest = getPointAtDistance(currentPosition, randomAngle, randomDistance); // snap destination to road using directions const snapDest = await snapToRoad(currentPosition, rawDest); destinationPosition = snapDest; if (destinationMarker) destinationMarker.setMap(null); destinationMarker = new google.maps.Marker( position: destinationPosition, map: map, icon: path: 'M0,-15 L10,10 L0,0 L-10,10 Z', fillColor: '#ffaa33', fillOpacity: 1, strokeWeight: 1, scale: 1, rotation: 0 , title: 'Destination', label: text: '🏁', color: 'white', fontSize: '16px', fontWeight: 'bold' ); // Show distance to dest const distRem = google.maps.geometry.spherical.computeDistanceBetween(currentPosition, destinationPosition); distEl.innerText = formatDistance(distRem); // reset heading to initial north-ish or current direction to destination? const initialHeading = google.maps.geometry.spherical.computeHeading(currentPosition, destinationPosition); currentHeading = initialHeading; // refresh car icon rotation const iconSymbol = path: 'M-12,0 L0,-18 L12,0 L0,18 Z', fillColor: '#ff4444', fillOpacity: 1, strokeWeight: 2, strokeColor: '#ffffff', scale: 1.2, rotation: currentHeading ; carMarker.setIcon(iconSymbol); // Manual drop destination on map click (optional fun) function setupMapClickListener() map.addListener('click', async (e) => if (!gameActive) return; const clicked = e.latLng; // snap clicked point to nearest road via directions from current pos? better simple snap const snapped = await snapToRoad(currentPosition, clicked); if (snapped) destinationPosition = snapped; if (destinationMarker) destinationMarker.setMap(null); destinationMarker = new google.maps.Marker( position: destinationPosition, map: map, icon: path: 'M0,-15 L10,10 L0,0 L-10,10 Z', fillColor: '#ffaa33', fillOpacity: 1, strokeWeight: 1, , label: text: '🎯', color: 'white', fontSize: '14px' ); const distRem = google.maps.geometry.spherical.computeDistanceBetween(currentPosition, destinationPosition); distEl.innerText = formatDistance(distRem); ); // Initialization entry point window.initMap = function() // Default starting location: Central Park, NYC (great roads) const startLocation = lat: 40.7812, lng: -73.9665 ; currentPosition = new google.maps.LatLng(startLocation.lat, startLocation.lng); map = new google.maps.Map(document.getElementById('map'), center: startLocation, zoom: 18, mapTypeId: google.maps.MapTypeId.ROADMAP, tilt: 0, streetViewControl: false, fullscreenControl: true, zoomControl: true, mapTypeControl: false ); // Custom car marker const carIcon = path: 'M-12,0 L0,-18 L12,0 L0,18 Z', fillColor: '#ff4444', fillOpacity: 1, strokeWeight: 2, strokeColor: '#ffffff', scale: 1.2, rotation: 0 ; carMarker = new google.maps.Marker( position: currentPosition, map: map, icon: carIcon, zIndex: 100, title: "Your Car" ); initRoadSnapping(); lastPositionForDistance = currentPosition; currentHeading = 90; // east facing initially // Setup destination after short delay setTimeout(async () => await resetGame(); , 500); setupMapClickListener(); // Add optional driving trail? (nice to have but optional) const trafficLayer = new google.maps.TrafficLayer(); trafficLayer.setMap(map); ; // Event listeners for buttons (and keyboard support) document.addEventListener('DOMContentLoaded', () => // Buttons document.getElementById('btn-fwd').addEventListener('click', () => moveCar('forward')); document.getElementById('btn-left').addEventListener('click', () => moveCar('left')); document.getElementById('btn-right').addEventListener('click', () => moveCar('right')); document.getElementById('btn-back').addEventListener('click', () => moveCar('backward')); document.getElementById('btn-reset').addEventListener('click', async () => if (currentPosition) await resetGame(); ); // Keyboard controls: Arrow keys window.addEventListener('keydown', (e) => if (!gameActive) return; switch(e.key) case 'ArrowUp': e.preventDefault(); moveCar('forward'); break; case 'ArrowDown': e.preventDefault(); moveCar('backward'); break; case 'ArrowLeft': e.preventDefault(); moveCar('left'); break; case 'ArrowRight': e.preventDefault(); moveCar('right'); break; case 'r': case 'R': e.preventDefault(); resetGame(); break; ); ); // Fallback if API key missing: show friendly note window.onload = function() ; </script> </head> <body> <div class="game-container"> <div id="map"></div> <div class="hud"> <div class="stats-panel"> 🚗 SCORE: <span id="scoreValue">0</span> | 📍 DIST: <span id="distValue">---</span> </div> <div class="controls-card"> <button class="ctrl-btn" id="btn-left" title="Steer Left">◀</button> <button class="ctrl-btn" id="btn-fwd" title="Drive Forward">▲</button> <button class="ctrl-btn" id="btn-back" title="Reverse">▼</button> <button class="ctrl-btn" id="btn-right" title="Steer Right">▶</button> <button class="ctrl-btn reset-btn" id="btn-reset" title="New Destination">⟳</button> </div> <div class="info-text"> 🎮 Arrow keys / Buttons | Click map to set destination 🏁 </div> </div> <div class="instruction"> 🚘 Drive on roads & reach the golden flag! </div> </div> </body> </html> /* Main game container */
/* small destination marker style */ .dest-label background: #ff4444; color: white; font-weight: bold; padding: 2px 8px; border-radius: 20px; font-size: 12px; white-space: nowrap; </style> <!-- Google Maps API (with Places & Geometry libraries for enhanced road snapping) --> <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places,geometry&callback=initMap" async defer></script> <script> // ============================================================ // GOOGLE MAPS DRIVING GAME (ROAD SNAPPING + STEERING) // ============================================================ // Note: Replace "YOUR_API_KEY" with a valid Google Maps API key // that has Maps JavaScript API, Places API, and Geometry library enabled. // ============================================================
@media (max-width: 700px) .ctrl-btn width: 55px; height: 55px; font-size: 1.6rem; .stats-panel font-size: 0.9rem; padding: 8px 18px; .stats-panel span font-size: 1.2rem; // Better: use Google Maps Roads API (snapToRoads)
.stats-panel background: rgba(0, 0, 0, 0.75); backdrop-filter: blur(8px); border-radius: 28px; padding: 12px 24px; color: white; font-weight: bold; font-size: 1.2rem; letter-spacing: 1px; border-left: 5px solid #ff9800; box-shadow: 0 4px 15px rgba(0,0,0,0.3); pointer-events: auto; font-family: monospace;