Files
traktorrr/index.html
2024-09-04 16:09:41 +03:00

17 KiB
Raw Blame History

<html lang="uk"> <head> <style> body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; } canvas { display: block; } </style> </head>
Клавіатура Геймпад
<script> let controlType = 'keyboard'; document.querySelectorAll('input[name="controlType"]').forEach((elem) => { elem.addEventListener("change", function(event) { controlType = event.target.value; }); }); const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); let canvasWidth, canvasHeight; let tractor, leftTrail, rightTrail, obstacles, mudPattern; function resizeCanvas() { canvasWidth = window.innerWidth; canvasHeight = window.innerHeight; canvas.width = canvasWidth; canvas.height = canvasHeight; createMudTexture(); } window.addEventListener('resize', resizeCanvas); resizeCanvas(); let lMut = false; let rMut = false; function createMudTexture() { const textureCanvas = document.createElement('canvas'); textureCanvas.width = 200; textureCanvas.height = 200; const textureCtx = textureCanvas.getContext('2d'); textureCtx.fillStyle = '#8B4513'; textureCtx.fillRect(0, 0, 200, 200); for (let i = 0; i < 1000; i++) { textureCtx.fillStyle = `rgba(${Math.random() * 50 + 100}, ${Math.random() * 30 + 50}, ${Math.random() * 20}, ${Math.random() * 0.5})`; textureCtx.beginPath(); textureCtx.arc(Math.random() * 200, Math.random() * 200, Math.random() * 2, 0, Math.PI * 2); textureCtx.fill(); } mudPattern = ctx.createPattern(textureCanvas, 'repeat'); } function initGame() { tractor = { x: canvasWidth / 2, y: canvasHeight / 2, width: 60, height: 80, angle: 0, leftTrack: 0, rightTrack: 0, speed: 1.5, turnSpeed: 0.008, friction: 0.92, acceleration: 0.2, braking: 0.4, leftReverse: false, rightReverse: false, leftTrackOffset: 0, rightTrackOffset: 0, leftBrake: false, rightBrake: false }; leftTrail = []; rightTrail = []; obstacles = []; for (let i = 0; i < 10; i++) { obstacles.push({ x: Math.random() * canvasWidth, y: Math.random() * canvasHeight, radius: 15 }); } createMudTexture(); } initGame(); const maxTrailLength = 500; const trailLifetime = 5000; const trailInterval = 5; let frameCount = 0; function drawTractor() { ctx.save(); ctx.translate(tractor.x, tractor.y); ctx.rotate(tractor.angle); ctx.fillStyle = 'green'; ctx.fillRect(-tractor.width / 2, -tractor.height / 2, tractor.width, tractor.height); const trackHeight = tractor.height; const trackWidth = 7; const segmentHeight = 7; ctx.globalAlpha = 0.8; ctx.fillStyle = '#333'; for (let i = 0; i < trackHeight / segmentHeight; i++) { const alpha = Math.max(0.5, 1 - Math.abs((tractor.leftTrackOffset + i * segmentHeight) % 20) / 10); ctx.globalAlpha = alpha; ctx.fillRect( -tractor.width / 2 - trackWidth, -tractor.height / 2 + i * segmentHeight, trackWidth, segmentHeight - 2 ); } ctx.fillStyle = '#333'; for (let i = 0; i < trackHeight / segmentHeight; i++) { const alpha = Math.max(0.5, 1 - Math.abs((tractor.rightTrackOffset + i * segmentHeight) % 20) / 10); ctx.globalAlpha = alpha; ctx.fillRect( tractor.width / 2, -tractor.height / 2 + i * segmentHeight, trackWidth, segmentHeight - 2 ); } ctx.globalAlpha = 1; ctx.fillStyle = 'lightblue'; ctx.fillRect(-tractor.width / 4, -tractor.height / 4, tractor.width / 2, tractor.height / 2); ctx.beginPath(); ctx.moveTo(0, -tractor.height / 2 - 20); ctx.lineTo(10, -tractor.height / 2 - 10); ctx.lineTo(-10, -tractor.height / 2 - 10); ctx.closePath(); ctx.fillStyle = 'red'; ctx.fill(); ctx.restore(); } function drawObstacles() { ctx.fillStyle = 'orange'; for (let obstacle of obstacles) { ctx.beginPath(); ctx.arc(obstacle.x, obstacle.y, obstacle.radius, 0, Math.PI * 2); ctx.fill(); } } function drawMud() { ctx.fillStyle = mudPattern; ctx.fillRect(0, 0, canvasWidth, canvasHeight); } function drawTrail() { const currentTime = Date.now(); ctx.lineWidth = 3; ctx.strokeStyle = 'rgba(60, 30, 15, 0.5)'; for (let trail of [leftTrail, rightTrail]) { ctx.beginPath(); for (let i = 0; i < trail.length; i++) { const alpha = Math.max(0, 1 - (currentTime - trail[i].time) / trailLifetime); if (alpha > 0) { ctx.globalAlpha = alpha; if (i === 0) { ctx.moveTo(trail[i].x, trail[i].y); } else { ctx.lineTo(trail[i].x, trail[i].y); } } } ctx.stroke(); } ctx.globalAlpha = 1; const cutoffTime = currentTime - trailLifetime; leftTrail = leftTrail.filter(point => point.time > cutoffTime); rightTrail = rightTrail.filter(point => point.time > cutoffTime); } function updateTractor() { const leftSpeed = tractor.leftTrack * (tractor.leftReverse ? -1 : 1); const rightSpeed = tractor.rightTrack * (tractor.rightReverse ? -1 : 1); const forwardSpeed = (leftSpeed + rightSpeed) / 2; const turn = (rightSpeed - leftSpeed) * tractor.turnSpeed; const newX = tractor.x + Math.sin(tractor.angle) * forwardSpeed; const newY = tractor.y - Math.cos(tractor.angle) * forwardSpeed; let collision = false; for (let obstacle of obstacles) { const dx = newX - obstacle.x; const dy = newY - obstacle.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < tractor.width / 2 + obstacle.radius + 5) { collision = true; break; } } if (!collision) { tractor.x = newX; tractor.y = newY; tractor.angle += turn; } tractor.leftTrack *= tractor.friction; tractor.rightTrack *= tractor.friction; tractor.leftTrackOffset += leftSpeed; tractor.rightTrackOffset += rightSpeed; tractor.leftTrackOffset = tractor.leftTrackOffset % 20; tractor.rightTrackOffset = tractor.rightTrackOffset % 20; if ((tractor.leftBrake === false && tractor.rightBrake === true) || (tractor.leftBrake === true && tractor.rightBrake === false)) { tractor.turnSpeed = 0.015; } else { tractor.turnSpeed = 0.008; } if (frameCount % trailInterval === 0) { const currentTime = Date.now(); const leftTrackPos = { x: tractor.x + Math.sin(tractor.angle + Math.PI/2) * tractor.width/2 + Math.sin(tractor.angle) * (tractor.leftReverse ? tractor.height/2 : -tractor.height/2), y: tractor.y - Math.cos(tractor.angle + Math.PI/2) * tractor.width/2 - Math.cos(tractor.angle) * (tractor.leftReverse ? tractor.height/2 : -tractor.height/2), time: currentTime }; const rightTrackPos = { x: tractor.x + Math.sin(tractor.angle - Math.PI/2) * tractor.width/2 + Math.sin(tractor.angle) * (tractor.rightReverse ? tractor.height/2 : -tractor.height/2), y: tractor.y - Math.cos(tractor.angle - Math.PI/2) * tractor.width/2 - Math.cos(tractor.angle) * (tractor.rightReverse ? tractor.height/2 : -tractor.height/2), time: currentTime }; leftTrail.push(leftTrackPos); rightTrail.push(rightTrackPos); if (leftTrail.length > maxTrailLength) leftTrail.shift(); if (rightTrail.length > maxTrailLength) rightTrail.shift(); } tractor.x = Math.max(tractor.width / 2, Math.min(canvasWidth - tractor.width / 2, tractor.x)); tractor.y = Math.max(tractor.height / 2, Math.min(canvasHeight - tractor.height / 2, tractor.y)); } const keys = {}; document.addEventListener('keydown', (event) => { keys[event.key] = true; if (event.key === 'r' || event.key === 'R') { initGame(); } }); document.addEventListener('keyup', (event) => { keys[event.key] = false; if (event.key === 'q' || event.key === 'Q') { lMut = false; } if (event.key === 'e' || event.key === 'E') { rMut = false; } }); function handleKeyboardInput() { if (!keys['ArrowDown']) { tractor.leftBrake = false; } if (!keys['s']) { tractor.rightBrake = false; } if (keys['ArrowUp']) { tractor.leftBrake = false; tractor.leftTrack = Math.min(tractor.leftTrack + tractor.acceleration, tractor.speed); } else if (keys['ArrowDown']) { tractor.leftBrake = true; tractor.leftTrack = Math.max(tractor.leftTrack - tractor.braking, 0); } if (keys['w']) { tractor.rightBrake = false; tractor.rightTrack = Math.min(tractor.rightTrack + tractor.acceleration, tractor.speed); } else if (keys['s']) { tractor.rightBrake = true; tractor.rightTrack = Math.max(tractor.rightTrack - tractor.braking, 0); } if (keys['q']) { if (lMut == false) { tractor.leftReverse = !tractor.leftReverse; tractor.leftTrack = 0; lMut = true; } } if (keys['e']) { if (rMut == false) { tractor.rightReverse = !tractor.rightReverse; tractor.rightTrack = 0; rMut = true; } } } function handleInput() { if (controlType === 'gamepad') { handleGamepadInput(); } else { handleKeyboardInput(); } } function drawStatus() { ctx.font = '16px Arial'; ctx.fillStyle = 'white'; ctx.fillText(`Права вісь: ${tractor.leftReverse ? 'Реверс' : 'Вперед'} (${tractor.leftTrack.toFixed(2)})`, 10, 30); ctx.fillText(`Ліва вісь: ${tractor.rightReverse ? 'Реверс' : 'Вперед'} (${tractor.rightTrack.toFixed(2)})`, 10, 60); } function drawInstructions() { ctx.font = '14px Arial'; ctx.fillStyle = 'white'; ctx.fillText('Керування:', 10, canvasHeight - 180); ctx.fillText('W - рух лівої гусениці', 10, canvasHeight - 160); ctx.fillText('S - гальмування лівої гусениці', 10, canvasHeight - 140); ctx.fillText('↑ - рух правої гусениці', 10, canvasHeight - 120); ctx.fillText('↓ - гальмування правої гусениці', 10, canvasHeight - 100); ctx.fillText('Q - реверс лівої гусениці', 10, canvasHeight - 80); ctx.fillText('E - реверс правої гусениці', 10, canvasHeight - 60); ctx.fillText('R - рестарт гри', 10, canvasHeight - 40); ctx.fillText('Джойстик: Ліва вісь - ліва гусениця, Права вісь - права гусениця', 10, canvasHeight - 20); } let lastTime = 0; let fps = 0; function gameLoop(currentTime) { const deltaTime = currentTime - lastTime; lastTime = currentTime; fps = 1000 / deltaTime; handleInput(); updateTractor(); ctx.clearRect(0, 0, canvasWidth, canvasHeight); drawMud(); drawTrail(); drawObstacles(); drawTractor(); drawStatus(); drawInstructions(); ctx.font = '16px Arial'; ctx.fillStyle = 'white'; ctx.fillText(`FPS: ${fps.toFixed(2)}`, 10, 90); frameCount++; requestAnimationFrame(gameLoop); } let gamepad = null; function updateGamepadState() { const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []); gamepad = gamepads[0]; } function handleGamepadInput() { if (!gamepad) return; if (!gamepad.buttons[5].pressed) { lMut = false; } if (!gamepad.buttons[4].pressed) { rMut = false; } const leftAxisY = gamepad.axes[3]; const rightAxisY = gamepad.axes[1]; const deadzone = 0.1; if (Math.abs(leftAxisY) > deadzone) { if (leftAxisY < 0) { tractor.leftTrack = Math.min(tractor.leftTrack - leftAxisY * tractor.acceleration, tractor.speed); tractor.leftBrake = false; } else { tractor.leftTrack = Math.max(tractor.leftTrack - tractor.braking, 0); tractor.leftBrake = true; } } else { tractor.leftTrack = 0; tractor.leftBrake = false; } if (Math.abs(rightAxisY) > deadzone) { if (rightAxisY < 0) { tractor.rightTrack = Math.min(tractor.rightTrack - rightAxisY * tractor.acceleration, tractor.speed); tractor.rightBrake = false; } else { tractor.rightTrack = Math.max(tractor.rightTrack - tractor.braking, 0); tractor.rightBrake = true; } } else { tractor.rightTrack = 0; tractor.rightBrake = false; } if (gamepad.buttons[9].pressed) { initGame(); } if (gamepad.buttons[5].pressed && !lMut) { tractor.leftReverse = !tractor.leftReverse; tractor.leftTrack = 0; lMut = true; } if (gamepad.buttons[4].pressed && !rMut) { tractor.rightReverse = !tractor.rightReverse; tractor.rightTrack = 0; rMut = true; } } window.addEventListener("gamepadconnected", function(e) { console.log("Gamepad connected:", e.gamepad.id); gamepad = e.gamepad; }); window.addEventListener("gamepaddisconnected", function(e) { console.log("Gamepad disconnected:", e.gamepad.id); gamepad = null; }); requestAnimationFrame(gameLoop); setInterval(updateGamepadState, 100); </script> </html>