Большой Короткомордый Медведь, он же Матти, он же Матс, он же Мэтью, он же неугомонный сын №3, решил на старости лет научиться программированию. И, для начала, написать какую-нибудь игрушку. Ну что же еще писать, кроме игрушек, правда?

Текстовая игра «Матс или Мишка» (вариант «орел-решка») на Питоне его не особо порадовал: «Вот если б какие-нибудь автогонки»… Поскольку строгий папа обещал, что в собственноручно написанные игры можно будет играть без ограничений.

Строгий папа где стоял, там и сел. У него вообще-то не то, чтобы дохрена свободного времени, чтобы писать детям автогонки. И вообще… это ж сколько писанины-то. Для папы. И куда там семилетке, первый раз видящему настоящую программу (Лого-черепашки не в счет) разбираться в классах и методах. Пе-пе-пе-сец… Или нет?

Папа, как обычно, принялся чесать за ушами. Если автогонки 2D, то, пожалуй, можно сделать их довольно просто. Да и 3D, в общем-то тоже. Только перестаньте все время толкать меня под локоть, у меня своей работы полно. Займитесь чем-нибудь полезным. Трассу вон пока нарисуйте.

Не умеете? А Paint на что?

image

Через пару часов, сами понимаете, у папы было столько трасс, что хоть чемпионат Формулы-1 на них проводи. И от семилетки, и от четырехлетки, и даже от двухлетки. А также пожелание, чтобы гонять можно было на телефоне, на компьютере и вообще везде. Пожелание это пришлось изо всех сил поддержать, дабы папин компьютер не стал однажды игровой приставкой.

Наступило воскресенье. Папа тяжело вздохнул и, осаждаемый инженерно одаренными отпрысками, решил для начала собрать сцену. Из машинки и трассы. Для простоты — в браузере, дабы не учить детей сложным и развесистым фреймворкам и не объяснять людям, которые пока слово «функция» не знают способы загрузки пакетов из репозиториев.

  • Сначала у нас собралась сцена.
  • Потом начала вертеться машинка.
  • Через час она стала ездить и тормозить.
  • И, когда Большой Короткомордый уже начал зевать и перестал следить за синусами и косинусами (в количестве ровно двух штук), мы научились тормозить на песочке, толкать машину на травке, тонуть в озере и не выезжать за пределы игрового поля.

Начальная версия «автогонок», управляемая стрелками на клавиатуре и потому пока не совместимая с телефоном (но это мы поправим в следующие выходные) заняла у нас 114 строчек. И у папы есть ощущение, что она представляет собой идеальное учебное пособие по написанию простых и интересных программ на JavaScript для детей младшего школьного возраста.

        var current_rotation = 100.0;                                           // Текущий угол поворота машинки
        var speed = 0.0;                                                        // Текущая скорость машинки
        var x = 135;                                                            // Положение машинки на карте
        var y = 300;
        document.onkeydown = checkKey;                                          // Устанавливаем обработчик
                                                                                // клавиатурных событий
        function checkKey(e) {
            var car = document.getElementById("car");                           // Получаем div с машинкой
            e = e || window.event;
            if (e.keyCode == '38') {				                            // Кнопка вверх
                speed += 1;                                                     // увеличиваем скорость
            }
            else if (e.keyCode == '40') {                                       // Кнопка вниз
                speed -= 1;                                                     // уменьшаем скорость
            }
            else if (e.keyCode == '37') {                                       // Кнопка влево
               current_rotation -= 2 * speed;                                   // уменьшаем угол пропорционально скорости
            }
            else if (e.keyCode == '39') {                                       // Кнопка вправо
               current_rotation += 2 * speed;                                   // увеличиваем угол пропорционально скорости
            }

            if (current_rotation < 0)                                           // Приводим угол к диапазону 0..360 градусов
                current_rotation = 360 - current_rotation;
            else if (current_rotation > 360)
                current_rotation -= 360;

            if (speed > 12)					                                    // Приводим скорость к диапазону -1..12
               speed = 12;
            else if (speed < -1)
               speed = -1;

            car.style.transform = "rotate(" + current_rotation.toString() + "deg)"; // Поворачиваем картинку машинки
        }

        // Периодическое событие, каждые 100 миллисекунд
        var intervalId = setInterval(function() {
            // Вычисляем новые координаты. Ось Х направлена вниз, а картинка машинки изначально влево,
            // поэтому доворачиваем на 270 градусов
            x += Math.sin(degrees_to_radians(current_rotation + 270)) * speed;
            y -= Math.cos(degrees_to_radians(current_rotation + 270)) * speed;

            // Передвигаем машинку в новую позицию
            var car = document.getElementById("car");
            car.style.left = "" + x.toString() + "px";
            car.style.top = "" + y.toString() + "px";

            // Теперь надо узнать цвет пикселя под машинкой
            // Для этого рисуем трассу на виртуальный canvas
            var cvs = document.createElement('canvas'),
            img = document.getElementById("track")
            img.crossOrigin="anonymous";
            cvs.width = img.width; cvs.height = img.height
            var ctx = cvs.getContext("2d")
            ctx.drawImage(img,0,0,cvs.width,cvs.height)

            var x1 = x
            var y1 = y
            // Читаем цвет пикселя примерно под серединой машинки
            var data = ctx.getImageData(x1 + 30, y1 + 10, 1, 1).data;

            var red = data[0];
            var green = data[1];
            var blue = data[2];

            // Если пиксель "скорее желтый" (мы планируем делать разноцветные трассы)
            if ((red >= 128) && (green >= 128) && (blue < 80))
            {
                // То ограничиваем скорость на песке
                if (speed > 3) speed = 3;
            }
            // Если пиксель "скорее зеленый"
            else if ((red < 200) && (green >= 128) && (blue < 128))
                // То скорость быстро падает
                speed /= 2;
            // Если пиксель синий, или мы вылетели за границу трассы
            else if (((red < 80) && (green < 200) && (blue > 128)) ||
		             (x <= 0) || (y <= 0) || (x >= img.width) || (y >= img.height))
            {
                // То игра заканчивается
                document.getElementById("gameover").style.visibility = "visible";
                // Делаем видимой панельку Game Over
                document.getElementById("car").style.visibility = "hidden";
                // И через 2.5 секунды перезагружаем страницу
                setTimeout(function() {
                    window.location.reload();
                }, 2500);
            };

        }, 100);

        // Перевод градусов в радианы
        function degrees_to_radians(degrees)
        {
            var pi = Math.PI;
            return degrees * (pi/180);
        }

Теперь, после детального разбора и мелких правок собственными сыньими руками (замер времени, подсчет очков, замена трасс, благо у нас их дофига), на очереди задача посложнее: нам нужен соперник. Будем заставлять компьютер гонять машинку наперегонки с человеком. Осталось папе выбрать для этого еще один воскресный вечер.

Желающие погонять с нами могут гонять здесь.