35845-vm/assets/js/simulator.js
Flatlogic Bot 866e0c05b7 step
2025-11-19 15:16:54 +00:00

184 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('simulationCanvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const launchButton = document.getElementById('launchButton');
const accelerationInput = document.getElementById('acceleration');
const burnTimeInput = document.getElementById('burnTime');
const angleInput = document.getElementById('angle');
const resultMessage = document.getElementById('result-message');
// --- Параметры симуляции ---
const GRAVITY = 9.8;
const PIXELS_PER_METER = 0.01;
const TIME_STEP = 1.0;
let rocket = {};
let simulationState = {};
function initializeState() {
return {
animationFrameId: null,
elapsedTime: 0,
acceleration: 0,
burnTime: 0,
angleRad: 0,
};
}
function initializeRocket() {
return {
x: 30,
y: canvas.height - 45,
width: 10,
height: 25,
x_meters: 0,
y_meters: 0,
vx: 0, // Скорость по X в м/с
vy: 0, // Скорость по Y в м/с
};
}
function drawEarth() {
ctx.fillStyle = '#2c5c23';
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height + canvas.height - 50, canvas.height, 0, Math.PI, true);
ctx.fill();
}
function drawRocket() {
ctx.save();
ctx.translate(rocket.x, rocket.y);
// Угол поворота ракеты зависит от вектора скорости
// Но до старта (скорость 0) она стоит вертикально
const angle = (rocket.vx === 0 && rocket.vy === 0)
? -Math.PI / 2
: Math.atan2(rocket.vy, rocket.vx);
// Поворачиваем ракету. Наша модель "смотрит" вправо, поэтому доп. вращение не нужно.
ctx.rotate(angle);
// Рисуем пламя, если двигатель работает
if (simulationState.elapsedTime > 0 && simulationState.elapsedTime <= simulationState.burnTime) {
ctx.fillStyle = `rgba(255, ${Math.random() * 150 + 100}, 0, 0.8)`;
ctx.beginPath();
const flameLength = rocket.height * (1.5 + Math.random() * 0.5);
ctx.moveTo(-rocket.width / 2, 0);
ctx.lineTo(-rocket.width / 2 - flameLength, rocket.width / 2);
ctx.lineTo(-rocket.width / 2 - flameLength, -rocket.width / 2);
ctx.closePath();
ctx.fill();
}
// Корпус ракеты (перерисовываем, чтобы был поверх пламени)
// Модель ракеты теперь "смотрит" вправо (по оси X)
ctx.fillStyle = '#d0d0d0';
ctx.beginPath();
ctx.moveTo(rocket.height / 2, 0);
ctx.lineTo(-rocket.height / 2, -rocket.width / 2);
ctx.lineTo(-rocket.height / 2, rocket.width / 2);
ctx.closePath();
ctx.fill();
ctx.restore();
}
function resetSimulation() {
if (simulationState.animationFrameId) {
cancelAnimationFrame(simulationState.animationFrameId);
}
simulationState = initializeState();
rocket = initializeRocket();
resultMessage.innerHTML = '&nbsp;';
resultMessage.className = 'alert alert-secondary';
clearCanvas();
drawEarth();
drawRocket();
}
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function animate() {
simulationState.elapsedTime += TIME_STEP;
// 1. Обновляем физику
// Если двигатель работает, применяем ускорение
if (simulationState.elapsedTime <= simulationState.burnTime) {
const currentAccelerationX = simulationState.acceleration * Math.cos(simulationState.angleRad);
const currentAccelerationY = simulationState.acceleration * Math.sin(simulationState.angleRad);
rocket.vx += currentAccelerationX * TIME_STEP;
rocket.vy += currentAccelerationY * TIME_STEP;
}
// Всегда применяем гравитацию (она действует на ось Y)
rocket.vy -= GRAVITY * TIME_STEP;
// Обновляем позицию в метрах
rocket.x_meters += rocket.vx * TIME_STEP;
rocket.y_meters += rocket.vy * TIME_STEP;
// 2. Обновляем позицию для отрисовки в пикселях
const groundY = canvas.height - 45;
rocket.x = 30 + (rocket.x_meters * PIXELS_PER_METER);
rocket.y = groundY - (rocket.y_meters * PIXELS_PER_METER);
// 3. Отрисовываем сцену
clearCanvas();
drawEarth();
drawRocket();
// 4. Проверяем условия завершения
const hasCrashed = rocket.y_meters < 0 && simulationState.elapsedTime > 1;
const outOfBounds = rocket.x > canvas.width + rocket.height;
if (hasCrashed || outOfBounds) {
rocket.y_meters = 0;
rocket.y = groundY;
clearCanvas();
drawEarth();
drawRocket();
resultMessage.innerHTML = outOfBounds
? "<strong>Неудача.</strong> Ракета улетела за пределы видимости."
: "<strong>Неудача.</strong> Ракета упала обратно на Землю.";
resultMessage.className = 'alert alert-danger';
cancelAnimationFrame(simulationState.animationFrameId);
simulationState.animationFrameId = null;
return;
}
simulationState.animationFrameId = requestAnimationFrame(animate);
}
launchButton.addEventListener('click', () => {
const accelValue = parseFloat(accelerationInput.value);
const burnTimeValue = parseFloat(burnTimeInput.value);
const angleValue = parseFloat(angleInput.value);
if (isNaN(accelValue) || isNaN(burnTimeValue) || isNaN(angleValue) || accelValue <= 0 || burnTimeValue <= 0 || angleValue < 0 || angleValue > 90) {
resultMessage.innerHTML = 'Пожалуйста, введите корректные значения (ускорение > 0, время > 0, угол 0-90).';
resultMessage.className = 'alert alert-warning';
return;
}
resetSimulation();
simulationState.acceleration = accelValue;
simulationState.burnTime = burnTimeValue;
simulationState.angleRad = angleValue * (Math.PI / 180);
resultMessage.innerHTML = "Запуск...";
resultMessage.className = 'alert alert-info';
if (!simulationState.animationFrameId) {
simulationState.animationFrameId = requestAnimationFrame(animate);
}
});
resetSimulation();
});