Загрузка данных


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Жизнь после смерти — эскиз</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: #e8e8e8;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Courier New', Courier, monospace;
            padding: 20px;
        }

        .game-container {
            background: #fdfdfd;
            border-radius: 4px;
            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
            padding: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        canvas {
            display: block;
            background: #ffffff;
            border: 1px solid #ccc;
            border-radius: 2px;
            cursor: default;
            box-shadow: inset 0 0 20px rgba(0,0,0,0.02);
        }

        .hint {
            margin-top: 14px;
            font-size: 15px;
            color: #555;
            letter-spacing: 0.5px;
            text-align: center;
            user-select: none;
        }

        .hint kbd {
            background: #f0f0f0;
            border: 1px solid #bbb;
            border-radius: 4px;
            padding: 2px 8px;
            font-family: inherit;
            font-size: 13px;
            box-shadow: 0 1px 2px rgba(0,0,0,0.1);
        }

        .philosophy-note {
            margin-top: 10px;
            font-style: italic;
            color: #777;
            font-size: 13px;
            text-align: center;
            max-width: 500px;
            line-height: 1.5;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <canvas id="gameCanvas" width="900" height="500"></canvas>
        <div class="hint">
            <kbd>←</kbd> или <kbd>A</kbd> — идти влево &nbsp;|&nbsp; <kbd>→</kbd> или <kbd>D</kbd> — идти вправо
        </div>
        <div class="philosophy-note">
            «Шаг за шагом по белому листу, где пол — лишь линия, а ты — лишь контур. Может быть, это и есть начало пути?»
        </div>
    </div>

    <script>
        (function() {
            const canvas = document.getElementById('gameCanvas');
            const ctx = canvas.getContext('2d');

            // Параметры мира
            const GROUND_Y = 390;          // Y-координата поверхности пола
            const GROUND_LEFT = 100;        // Левая граница пола
            const GROUND_RIGHT = 800;       // Правая граница пола
            const PLAYER_START_X = 450;     // Начальная позиция человека
            const PLAYER_HEIGHT = 180;      // Высота человечка (от пола до макушки)
            const HEAD_RADIUS = 22;         // Радиус головы
            const BODY_LENGTH = 80;         // Длина туловища
            const LEG_LENGTH = 50;          // Длина ноги
            const ARM_LENGTH = 55;          // Длина руки
            const MOVE_SPEED = 4.5;         // Скорость движения

            // Состояние игрока
            let playerX = PLAYER_START_X;
            let isMovingLeft = false;
            let isMovingRight = false;
            let walkCycle = 0;              // Таймер для анимации ходьбы (в радианах)

            // Ограничения движения
            const MIN_X = GROUND_LEFT + 10;
            const MAX_X = GROUND_RIGHT - 10;

            // Обработчики клавиш
            window.addEventListener('keydown', (e) => {
                const key = e.key.toLowerCase();
                if (key === 'arrowleft' || key === 'a') {
                    isMovingLeft = true;
                    e.preventDefault();
                } else if (key === 'arrowright' || key === 'd') {
                    isMovingRight = true;
                    e.preventDefault();
                }
            });

            window.addEventListener('keyup', (e) => {
                const key = e.key.toLowerCase();
                if (key === 'arrowleft' || key === 'a') {
                    isMovingLeft = false;
                    e.preventDefault();
                } else if (key === 'arrowright' || key === 'd') {
                    isMovingRight = false;
                    e.preventDefault();
                }
            });

            // Чтобы клавиши не прокручивали страницу
            window.addEventListener('keydown', (e) => {
                if (['arrowleft', 'arrowright', 'a', 'd'].includes(e.key.toLowerCase())) {
                    e.preventDefault();
                }
            }, { passive: false });

            // Рисование пола — как карандашный штрих
            function drawGround() {
                ctx.save();
                // Тонкая линия пола
                ctx.beginPath();
                ctx.moveTo(GROUND_LEFT, GROUND_Y);
                ctx.lineTo(GROUND_RIGHT, GROUND_Y);
                ctx.strokeStyle = '#2c2c2c';
                ctx.lineWidth = 2.5;
                ctx.setLineDash([]);
                ctx.stroke();

                // Лёгкая текстура «листа» — едва заметные штрихи
                ctx.strokeStyle = '#f0f0f0';
                ctx.lineWidth = 1;
                for (let i = GROUND_LEFT; i < GROUND_RIGHT; i += 12) {
                    ctx.beginPath();
                    ctx.moveTo(i, GROUND_Y - 8);
                    ctx.lineTo(i + 6, GROUND_Y + 4);
                    ctx.stroke();
                }

                // Надпись-эскиз возле пола
                ctx.font = 'italic 12px "Courier New", Courier, monospace';
                ctx.fillStyle = '#aaa';
                ctx.fillText('— пол (возможно, метафора)', GROUND_RIGHT - 190, GROUND_Y + 25);
                ctx.restore();
            }

            // Рисование человечка — как набросок карандашом
            function drawPlayer() {
                ctx.save();

                // Немного смещаем человечка при ходьбе для эффекта «оживления»
                let bounce = 0;
                if (isMovingLeft || isMovingRight) {
                    bounce = Math.sin(walkCycle * 2) * 1.5;
                }

                const headY = GROUND_Y - PLAYER_HEIGHT + HEAD_RADIUS + bounce;
                const neckY = headY + HEAD_RADIUS + 2;
                const hipY = neckY + BODY_LENGTH;
                const footY = GROUND_Y;

                // --- Ноги (с анимацией шага) ---
                const legSwing = (isMovingLeft || isMovingRight) ? Math.sin(walkCycle) * 12 : 0;

                // Левая нога
                const leftLegAngle = legSwing;
                const leftKneeX = playerX - 5;
                const leftKneeY = hipY + LEG_LENGTH * 0.55;
                const leftFootX = playerX - 8 + leftLegAngle * 0.8;
                const leftFootY = footY;

                ctx.beginPath();
                ctx.moveTo(playerX - 4, hipY);
                ctx.lineTo(leftKneeX, leftKneeY);
                ctx.lineTo(leftFootX, leftFootY);
                ctx.strokeStyle = '#1a1a1a';
                ctx.lineWidth = 2.8;
                ctx.lineCap = 'round';
                ctx.lineJoin = 'round';
                ctx.stroke();

                // Правая нога
                const rightLegAngle = -legSwing;
                const rightKneeX = playerX + 5;
                const rightKneeY = hipY + LEG_LENGTH * 0.55;
                const rightFootX = playerX + 8 + rightLegAngle * 0.8;
                const rightFootY = footY;

                ctx.beginPath();
                ctx.moveTo(playerX + 4, hipY);
                ctx.lineTo(rightKneeX, rightKneeY);
                ctx.lineTo(rightFootX, rightFootY);
                ctx.stroke();
                // Линия ступней (небольшие чёрточки)
                ctx.beginPath();
                ctx.moveTo(leftFootX - 6, leftFootY);
                ctx.lineTo(leftFootX + 6, leftFootY);
                ctx.moveTo(rightFootX - 6, rightFootY);
                ctx.lineTo(rightFootX + 6, rightFootY);
                ctx.lineWidth = 1.8;
                ctx.stroke();

                // --- Туловище ---
                ctx.beginPath();
                ctx.moveTo(playerX, neckY);
                ctx.lineTo(playerX, hipY);
                ctx.strokeStyle = '#1a1a1a';
                ctx.lineWidth = 3.5;
                ctx.stroke();

                // --- Руки (слегка покачиваются при ходьбе) ---
                const armSwing = (isMovingLeft || isMovingRight) ? Math.sin(walkCycle + Math.PI) * 8 : 0;
                // Левая рука
                ctx.beginPath();
                ctx.moveTo(playerX - 1, neckY + 12);
                ctx.lineTo(playerX - 14 + armSwing * 0.6, neckY + 38);
                ctx.lineTo(playerX - 18 + armSwing * 0.9, neckY + 56);
                ctx.lineWidth = 2.2;
                ctx.stroke();

                // Правая рука
                ctx.beginPath();
                ctx.moveTo(playerX + 1, neckY + 12);
                ctx.lineTo(playerX + 14 - armSwing * 0.6, neckY + 38);
                ctx.lineTo(playerX + 18 - armSwing * 0.9, neckY + 56);
                ctx.stroke();

                // --- Голова ---
                ctx.beginPath();
                ctx.arc(playerX, headY, HEAD_RADIUS, 0, Math.PI * 2);
                ctx.strokeStyle = '#1a1a1a';
                ctx.lineWidth = 2.5;
                ctx.stroke();

                // Лёгкая тень под подбородком (для эскизности)
                ctx.beginPath();
                ctx.arc(playerX, headY + HEAD_RADIUS * 0.3, HEAD_RADIUS * 0.7, 0.1, Math.PI - 0.1);
                ctx.strokeStyle = '#999';
                ctx.lineWidth = 0.8;
                ctx.stroke();

                // Глаза — две точки, смотрящие по направлению движения
                const lookDirection = (isMovingLeft && !isMovingRight) ? -1 : (isMovingRight && !isMovingLeft) ? 1 : 0;
                const eyeOffsetX = lookDirection * 3;
                ctx.fillStyle = '#1a1a1a';
                ctx.beginPath();
                ctx.arc(playerX - 6 + eyeOffsetX, headY - 2, 2.2, 0, Math.PI * 2);
                ctx.fill();
                ctx.beginPath();
                ctx.arc(playerX + 6 + eyeOffsetX, headY - 2, 2.2, 0, Math.PI * 2);
                ctx.fill();

                // Рот — маленькая чёрточка
                ctx.beginPath();
                ctx.moveTo(playerX - 4, headY + 8);
                ctx.lineTo(playerX + 4, headY + 8);
                ctx.strokeStyle = '#1a1a1a';
                ctx.lineWidth = 1.2;
                ctx.stroke();

                ctx.restore();
            }

            // Обновление состояния
            function update() {
                // Определяем направление движения
                const movingLeft = isMovingLeft && !isMovingRight;
                const movingRight = isMovingRight && !isMovingLeft;

                if (movingLeft) {
                    playerX -= MOVE_SPEED;
                }
                if (movingRight) {
                    playerX += MOVE_SPEED;
                }

                // Ограничение по краям пола
                if (playerX < MIN_X) playerX = MIN_X;
                if (playerX > MAX_X) playerX = MAX_X;

                // Анимация ходьбы — увеличиваем фазу только при движении
                if (movingLeft || movingRight) {
                    walkCycle += 0.18;
                } else {
                    // Плавно возвращаем ноги в нейтральное положение
                    walkCycle *= 0.85;
                }
            }

            // Отрисовка всего
            function draw() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);

                // Белый лист — уже фон canvas, но добавим лёгкую текстуру
                ctx.fillStyle = '#ffffff';
                ctx.fillRect(0, 0, canvas.width, canvas.height);

                // Эффект «бумаги» — едва заметные разводы
                ctx.fillStyle = 'rgba(0,0,0,0.008)';
                for (let i = 0; i < 30; i++) {
                    ctx.beginPath();
                    ctx.arc(
                        100 + Math.random() * 700,
                        80 + Math.random() * 350,
                        Math.random() * 40 + 10,
                        0, Math.PI * 2
                    );
                    ctx.fill();
                }

                // Рисуем пол
                drawGround();

                // Рисуем человечка
                drawPlayer();

                // Подпись-философская заметка (лёгкий эскиз)
                ctx.font = 'italic 13px "Courier New", Courier, monospace';
                ctx.fillStyle = '#bbb';
                ctx.fillText('...и кто идёт по этой линии?', 280, 130);
            }

            // Игровой цикл
            function gameLoop() {
                update();
                draw();
                requestAnimationFrame(gameLoop);
            }

            // Запуск
            gameLoop();

            // Также поддержка касаний для мобильных (просто заглушка)
            // Можно добавить кнопки, но по условию — клавиатура.
        })();
    </script>
</body>
</html>