Фронтенд-гигант: CSS может создавать 3D-игры

внешний интерфейс CSS
Фронтенд-гигант: CSS может создавать 3D-игры

предисловие

Случайно соприкоснулся с 3D-свойствами CSS, и родился своего рода дизайн.3D игрыидея.

Все учащиеся, изучавшие свойства css3D, должны знатьperspective,perspective-origin,transform-style: preserve-3dТри значения атрибута, Они составляют трехмерный мир CSS.

В то же время существуютtransformсвойства для перемещения, масштабирования, поворота и растяжения 3D-узлов.

Значение атрибута очень простое и редко используется в нашей обычной веб-разработке.

Можете ли вы создавать 3D-игры с этими свойствами CSS3D?

Конечно, это возможно.

Даже с единственной песочницей естьмой МирТакой шедевр.

Сегодня я познакомлю вас с совершенно новым 3D-приключением, которого вы никогда раньше не видели.

Без лишних слов давайте посмотрим на эффект:index.gif

Вот пробный адресИграть на ПК

мы собираемся закончить этолабиринт битва, необходимо выполнить следующие шаги:

  1. Создайте трехмерный мир
  2. Написать функцию для 3D-камеры
  3. Создайте трехмерный лабиринт
  4. Создайте игрока, который может свободно двигаться
  5. Найти подсказку кратчайшего пути в лабиринте

Сначала рассмотрим некоторые предварительные условия.

Знания и концепции, необходимые для создания CSS3D-игры

Система координат CSS3D

В css3D мы должны сначала прояснить концепцию,3DСистема координат.
использоватьлевая система координат, вытяните левую руку, большой и указательный пальцыLОстальные пальцы располагаются перпендикулярно указательному пальцу, как показано на рисунке:

WechatIMG315.png

Большой палец — это ось X, указательный палец — ось Y, а остальные пальцы — ось Z.
Это система координат в CSS3D.

Перспективные свойства

perspectiveДля свойства перспективы в css.

Что означает этот атрибут Мы можем рассматривать наши глаза как точку наблюдения, а расстояние от глаз до целевого объекта — это расстояние просмотра, которое является упомянутым здесь атрибутом перспективы.

все знают,"Перспектива" + "2D" = "3D".

perspective: 1200px;
-webkit-perspective:  1200px;

3D камера

В разработке 3D-игр будет концепция камеры, то есть то, что видит человеческий глаз, это то, что видит камера.
В движении сцены в игре большая часть приходится на перемещение камеры.
Например, в гоночной игре камера движется вместе с автомобилем, поэтому по пути мы можем видеть пейзажи.
Здесь мы будем использовать CSS для реализации псевдо 3D-камеры.

Преобразование свойств

В CSS3D мы переводим, поворачиваем, растягиваем и масштабируем 3D-блок, используяtransformАтрибуты.

  • translateX переводит ось X
  • translateY переводит ось Y
  • translateZ переводит ось Z
  • rotateX вращать ось X
  • rotateY вращает ось Y
  • rotateZ вращает ось Z
  • rotate3d(x,y,z,deg) на сколько градусов повернуть оси X, Y, Z

Уведомление:
Есть разница между "перевести, а затем повернуть" и "повернуть, а затем перевести"
Угол поворота – это значение угла.

Здесь все еще есть студенты, которым непонятно, вы можете обратиться к этой статье Ю Фэя [Наггетс.Талант/пост/699769…] Поставляется с демонстрацией

матричное преобразование

Матричные преобразования используются в процессе прохождения игры.
В js получить узелtransformатрибут, у вас получится матрица, вот я ее распечатаю, выглядит это так:

var _ground = document.getElementsByClassName("ground")[0];
var bg_style = document.defaultView.getComputedStyle(_ground, null).transform;
console.log("矩阵变换---->>>",bg_style)

WechatIMG307.png

Итак, как мы используем матрицу для управления преобразованием?

В линейных преобразованиях мы все используем умножение матриц.
CSS3D использует матрицу 4*4 для 3D-преобразования.
Следующие матрицы представлены двумерными массивами.
Напримерmatrix3d(1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1)Он может быть представлен как двумерный массив:

[
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1]
]

Для перевода, даже если исходную матрицу состояния умножить на следующую матрицу, dx, dy, dz — направления движения x, y, z соответственно.

[
    [1, 0, 0, dx],
    [0, 1, 0, dy],
    [0, 0, 1, dz],
    [0, 0, 0, 1]
]

Поверните 𝞱 вокруг оси X, то есть умножьте на следующую матрицу.

[
    [1, 0, 0, 0],
    [0, cos𝞱, sin𝞱, 0],
    [0, -sin𝞱, cos𝞱, 0],
    [0, 0, 0, 1]
]

Поверните 𝞱 вокруг оси Y, то есть умножьте на следующую матрицу.

[
    [cos𝞱, 0, -sin𝞱, 0],
    [0, 1, 0, 0],
    [sin𝞱, 0, cos𝞱, 0],
    [0, 0, 0, 1]
]

Поверните 𝞱 вокруг оси Z, то есть умножьте на следующую матрицу.

[
    [cos𝞱, sin𝞱, 0, 0],
    [-sin𝞱, cos𝞱, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1]
]

Другие знания конкретной матрицы обсуждаются здесь, и все желающие могут изучить их самостоятельно.
Нам нужно только очень простое приложение вращения здесь.

Начните создавать трехмерный мир

Давайте сначала создадим интерфейс пользовательского интерфейса.

  • камера див
  • горизонт дел
  • шахматная доска div
  • Плеер div (здесь куб)

Уведомление
Куб сначала вращается, а затем перемещается, что должно быть самым простым методом.
Плоскость поворачивается на 180 градусов и ±90 градусов вокруг оси X и оси Y, и ей нужно только сместить ось Z.
Здесь все поймут.

Давайте сначала посмотрим на часть html:

    <div class="camera">
        <!-- 地面 -->
        <div class="ground">
            <div class="box">
                <div class="box-con">
                    <div class="wall">z</div>
                    <div class="wall">z</div>
                    <div class="wall">y</div>
                    <div class="wall">y</div>
                    <div class="wall">x</div>
                    <div class="wall">x</div>
                    <div class="linex"></div>
                    <div class="liney"></div>
                    <div class="linez"></div>
                </div>
                <!-- 棋盘 -->
                <div class="pan"></div>
            </div>
        </div>
    </div>

очень простая планировка, гдеlinex,liney,linezЭто вспомогательная линия оси координат, которую я провел.
Красная линия — ось X, зеленая линия — ось Y, а синяя линия — ось Z. Далее давайте взглянем на основной код CSS куба.

...
  .box-con{
      width: 50px;
      height: 50px;
      transform-style: preserve-3d;
      transform-origin: 50% 50%;
      transform: translateZ(25px) ;
      transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
  }
  .wall{
      width: 100%;
      height: 100%;
      border: 1px solid #fdd894;
      background-color: #fb7922;
    
  }
  .wall:nth-child(1) {
      transform: translateZ(25px);
  }
  .wall:nth-child(2) {
      transform: rotateX(180deg) translateZ(25px);
  }
  .wall:nth-child(3) {
      transform: rotateX(90deg) translateZ(25px);
  }
  .wall:nth-child(4) {
      transform: rotateX(-90deg) translateZ(25px);
  }
  .wall:nth-child(5) {
      transform: rotateY(90deg) translateZ(25px);
  }
  .wall:nth-child(6) {
      transform: rotateY(-90deg) translateZ(25px);
  }

Кажется глупым вставлять кучу кода CSS.
Другой CSS не будет вставлен сюда, заинтересованные студенты могут загрузить его напрямую.исходный кодПроверять. Построение интерфейса завершено, как показано на рисунке:

WechatIMG308.png

Дальше основное событие, идём писать js код, чтобы продолжить доделывать нашу игру.

Выполните функцию 3D-камеры

Камеры незаменимы в 3D-разработке.Использование функции камеры позволяет не только просматривать 3D-модель мира, но и реализовывать множество интересных функций в реальном времени.

Какие функции нужны 3d камере?

Самый простой, вверх-вниз, влево-вправо может обозревать карту на 360 градусов без тупиков, при этом нужно увеличивать и уменьшать масштаб.

Взаимодействие с мышью

Двигайте мышью влево и вправо, чтобы вращать для просмотра карты; Двигайте мышь вверх и вниз, чтобы наблюдать за картой вверх и вниз; Колесо мыши может увеличивать и уменьшать масштаб.

1. Слушайте события мыши

Прежде всего, нам нужно записать положение мыши, прослушивая события мыши, чтобы судить о том, что камера смотрит вверх, вниз, влево и вправо.

    /** 鼠标上次位置 */
    var lastX = 0, lastY = 0;
      /** 控制一次滑动 */
    var isDown = false;
      /** 监听鼠标按下 */
    document.addEventListener("mousedown", (e) => {
        lastX = e.clientX;
        lastY = e.clientY;
        isDown = true;
    });
        /** 监听鼠标移动 */
    document.addEventListener("mousemove", (e) => {
        if (!isDown) return;
        let _offsetX = e.clientX - lastX;
        let _offsetY = e.clientY - lastY;
        lastX = e.clientX;
        lastY = e.clientY;
        //判断方向
        var dirH = 1, dirV = 1;
        if (_offsetX < 0) {
            dirH = -1;
        }
        if (_offsetY > 0) {
            dirV = -1;
        }
    });
    document.addEventListener("mouseup", (e) => {
        isDown = false;
    });

2. Определите камеру вверх, вниз, влево и вправо

использоватьperspective-originдля установки линии обзора камеры вверх и вниз.
использоватьtransformПовернуть ось Z для просмотра на 360 градусов влево и вправо.

/** 监听鼠标移动 */
    document.addEventListener("mousemove", (e) => {
        if (!isDown) return;
        let _offsetX = e.clientX - lastX;
        let _offsetY = e.clientY - lastY;
        lastX = e.clientX;
        lastY = e.clientY;
        var bg_style = document.defaultView.getComputedStyle(_ground, null).transform;
        var camera_style = document.defaultView.getComputedStyle(_camera, null).perspectiveOrigin;
        var matrix4 = new Matrix4();
        var _cy = +camera_style.split(' ')[1].split('px')[0];
        var str = bg_style.split("matrix3d(")[1].split(")")[0].split(",");
        var oldMartrix4 = str.map((item) => +item);
        var dirH = 1, dirV = 1;
        if (_offsetX < 0) {
            dirH = -1;
        }
        if (_offsetY > 0) {
            dirV = -1;
        }
        //每次移动旋转角度
        var angleZ = 2 * dirH;
        var newMartri4 = matrix4.set(Math.cos(angleZ * Math.PI / 180), -Math.sin(angleZ * Math.PI / 180), 0, 0, Math.sin(angleZ * Math.PI / 180), Math.cos(angleZ * Math.PI / 180), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
        var new_mar = null;
        if (Math.abs(_offsetX) > Math.abs(_offsetY)) {
            new_mar = matrix4.multiplyMatrices(oldMartrix4, newMartri4);
        } else {
            _camera.style.perspectiveOrigin = `500px ${_cy + 10 * dirV}px`;
        }
        new_mar && (_ground.style.transform = `matrix3d(${new_mar.join(',')})`);
    });

Здесь используется матричный метод для поворота оси Z, матричный классMatrix4Это класс методов, который я написал временно, всего два метода, один задает двумерный массивmatrix4.set, умножение матрицmatrix4.multiplyMatrices.
В конце статьи есть адреса исходного кода, поэтому я не буду их здесь повторять.

3. Колесо монитора увеличивает и уменьшает масштаб

Здесь основано наperspectiveдля установки расстояния просмотра.

//监听滚轮
document.addEventListener('mousewheel', (e) => {
    var per = document.defaultView.getComputedStyle(_camera, null).perspective;
    let newper = (+per.split("px")[0] + Math.floor(e.deltaY / 10)) + "px";
    _camera.style.perspective = newper
}, false);

Уведомление:
Свойство "perspective-origin" имеет только два значения X и Y, и это не может быть та же камера, что и u3D.
Здесь я использовал хитрое вращение горизонта, чтобы добиться того же эффекта.
Колесо прокрутки немного неудобно для увеличения и уменьшения масштаба, и оно все еще сильно отличается от 3D-движка.

После завершения вы можете увидеть следующую сцену, и вы можете наблюдать за нашей картой в любое время.

index1.gif

Таким образом, 3D-камера завершена.Если интересно, можете спуститься и написать ее сами, все равно очень интересно.

рисование доски лабиринта

Нарисовать карту-сетку проще всего, здесь я использую массив 15*15.
"0” представляет собой дорогу, по которой можно пройти, “1” представляет собой препятствие.

var grid = [
    0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
    0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0,
    1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
    0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
    0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
    0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
    0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
    1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
    1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0,
    0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1,
    0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
    1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
    1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
    0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0
];

Затем мы проходим по этому массиву и получаем карту.
Напишите метод для создания сетки карты, возвращающий как массив сетки, так и массив узлов.
здесьblockэто сборка, созданная в html, это куб.
Затем добавьте его на шахматную доску, клонировав узел.

/** 棋盘 */
function pan() {
    const con = document.getElementsByClassName("pan")[0];
    const block = document.getElementsByClassName("block")[0];
    let elArr = [];
    grid.forEach((item, index) => {
        let r = Math.floor(index / 15);
        let c = index % 15;
        const gezi = document.createElement("div");
        gezi.classList = "pan-item"
        // gezi.innerHTML = `${r},${c}`
        con.appendChild(gezi);
        var newBlock = block.cloneNode(true);
        //障碍物
        if (item == 1) {
            gezi.appendChild(newBlock);
            blockArr.push(c + "-" + r);
        }
        elArr.push(gezi);
    });
    const panArr = arrTrans(15, grid);
    return { elArr, panArr };
}
const panData = pan();

Как видите, наш интерфейс стал таким.

WechatIMG310.png

Далее нам нужно управлять движением игрока.

управлять движением игрока

через вверх и внизw s a dклавиши для управления движением игрока.
использоватьtransformдля перемещения и поворота коробки игрока.

Слушайте события клавиатуры

Прослушивая события клавиатурыonkeydownсудитьkeyзначение вверх и вниз.

document.onkeydown = function (e) {
    /** 移动物体 */
    move(e.key);
}

сделать перемещение

В смещении используйтеtranslateДля перемещения ось Z всегда обращена к нашей камере, поэтому нам нужно перемещать только оси X и Y.
Объявите переменную для записи текущей позиции.
При этом необходимо зафиксировать последнее преобразованиеtransformПри значении , здесь мы не продолжаем преобразование матрицы.

/** 当前位置 */
var position = { x: 0, y: 0 };
/** 记录上次变换值 */
var lastTransform = {
    translateX: '0px',
    translateY: '0px',
    translateZ: '25px',
    rotateX: '0deg',
    rotateY: '0deg',
    rotateZ: '0deg'
};

Каждую сетку можно рассматривать как нижний индекс двумерного массива, и каждый раз мы перемещаем сетку на расстояние.

 switch (key) {
    case 'w':
        position.y++;
        lastTransform.translateY = position.y * 50 + 'px';
        break;
    case 's':
        position.y--;
        lastTransform.translateY = position.y * 50 + 'px';
        break;
    case 'a':
        position.x++;
        lastTransform.translateX = position.x * 50 + 'px';
        break;
    case 'd':
        position.x--;
        lastTransform.translateX = position.x * 50 + 'px';
        break;
}
//赋值样式
for (let item in lastTransform) {
    strTransfrom += item + '(' + lastTransform[item] + ') ';
}
target.style.transform = strTransfrom;

На этом этапе наш ящик игрока можно перемещать.

Уведомление
Перевод в css3D можно увидеть как мировые координаты.
Так что нам нужно заботиться только об осях X и Y. И нам не нужно перемещать ось Z. Даже если мы вращаемся.

Вращение во время движения

В CSS3D 3D вращение отличается от других 3D движков.В общем,таких как u3D и threejs,после каждого вращения оно будет перекалибровано в мировые координаты,условно говоря Легко подсчитать, на сколько градусов нужно повернуть вокруг какой оси.

Однако я также недооценил вращение CSS3D.
Я думал, что будет легко прокручивать куб вверх, вниз, влево и вправо, но это не так.

Вращение в CSS3D включает в себя кватернионы и карданные блокировки.

Например, мы вращаем поле нашего игрока. как показано на рисунке:

WechatIMG311.pngВо-первых, первая сетка (0,0) поворачивается на 90 градусов вверх и вокруг оси X, чтобы достичь (1,0); если ее повернуть на 90 градусов влево вокруг оси Y, она может достичь (0,1); Тогда мы можем получить правило следующим образом:

WechatIMG312.png

Как показано на рисунке, нет проблем в простом вращении вверх-вниз, влево-вправо вокруг оси, но чтобы повернуться к красной сетке, есть два разных способа перемещения.После достижения красной сетки будет два возможности ротации. Это приводит к ошибке вращения.

В то же время, хотя это правило трудно найти, его можно записать.Неправильно поворачивать блок в CSS3D в соответствии с этим правилом.

Потом кто-то это сказал, это чепуха?

После авторского эксперимента я нашел некоторые правила, продолжаем спускаться по этому правилу.

  • При вращении оси X смотрите одновременно на градус текущей оси Z, ось Z нечетно кратна 90 градусам, вращайте ось Y, в противном случае вращайте ось X.
  • При вращении оси Y смотрите одновременно на градус текущей оси Z, ось Z нечетно кратна 90 градусам, вращайте ось X, в противном случае вращайте ось Z.
  • При вращении оси Z продолжайте вращать ось Z.

Таким образом, наше направление вращения фиксируется.

if (nextRotateDir[0] == "X") {
    if (Math.floor(Math.abs(lastRotate.lastRotateZ) / 90) % 2 == 1) {
        lastTransform[`rotateY`] = (lastRotate[`lastRotateY`] + 90 * dir) + 'deg';
    } else {
        lastTransform[`rotateX`] = (lastRotate[`lastRotateX`] - 90 * dir) + 'deg';
    }
}
if (nextRotateDir[0] == "Y") {
    if (Math.floor(Math.abs(Math.abs(lastRotate.lastRotateZ)) / 90) % 2 == 1) {
        lastTransform[`rotateX`] = (lastRotate[`lastRotateX`] + 90 * dir) + 'deg';
    } else {
        lastTransform[`rotateZ`] = (lastRotate[`lastRotateZ`] + 90 * dir) + 'deg';
    }
}
if (nextRotateDir[0] == "Z") {
    lastTransform[`rotate${nextRotateDir[0]}`] = (lastRotate[`lastRotate${nextRotateDir[0]}`] - 90 * dir) + 'deg';
}

Однако на этом еще не все, в этом способе поворота еще есть яма, то есть я не знаю, то ли поворачивать на 90 градусов, то ли на -90 градусов.
Это не простое сложение и вычитание вверх, вниз, влево и вправо.

Направление вращения правильное, но я не знаю, как рассчитать угол поворота.

Конкретный код можно посмотретьисходный код.

яйцо время

⚠️⚠️⚠️ При этом будет сопровождаться "Универсальный замок», то есть ось Z совпадает с осью X. Хахахаха~
⚠️⚠️⚠️ Автор еще не решил ее, и я надеюсь, что всесильные нетизены смогут высказаться и помочь~
⚠️⚠️⚠️ Автор обновит позже.Хахахаха, большая яма.

Ну, эта проблема не затрагивает наш проект. Переходим к тому, как найти кратчайший путь и дать подсказки.

расчет кратчайшего пути

Как рассчитать кратчайший путь из одной точки в другую в лабиринте? Здесь я использую обход в ширину (BFS) алгоритм расчета кратчайшего пути.

Давайте подумаем о:

  1. Найдите кратчайший путь в двумерном массиве
  2. Кратчайший путь каждой сетки — это только четыре соседние сетки.
  3. Затем просто рекурсивно найдите кратчайшее расстояние каждой сетки, пока не найдете конечную точку.

Здесь нам нужно использовать "очередь«Функция «первым пришел – первым вышел».

Сначала посмотрим на картинку:

WechatIMG313.png

Ясно, что можно найти кратчайший путь.

Уведомление
Используйте два массива длины 4 для представления смещений нижних индексов, которые необходимо добавить к соседним сеткам.
Каждый раз, когда вы присоединяетесь к команде, вам нужно определить, присоединились ли вы уже к команде.
Каждый раз, когда вы покидаете команду, вам нужно судить, является ли это конечной точкой.
Необходимо записать родительский узел текущей поставленной в очередь цели, чтобы облегчить получение кратчайшего пути.

Давайте посмотрим на код:

//春初路径
var stack = [];
/**
 * BFS 实现寻路
 * @param {*} grid 
 * @param {*} start {x: 0,y: 0}
 * @param {*} end {x: 3,y: 3}
 */
function getShortPath(grid, start, end, a) {
    let maxL_x = grid.length;
    let maxL_y = grid[0].length;
    let queue = new Queue();
    //最短步数
    let step = 0;
    //上左下右
    let dx = [1, 0, -1, 0];
    let dy = [0, 1, 0, -1];
    //加入第一个元素
    queue.enqueue(start);
    //存储一个一样的用来排查是否遍历过
    let mem = new Array(maxL_x);
    for (let n = 0; n < maxL_x; n++) {
        mem[n] = new Array(maxL_y);
        mem[n].fill(100);
    }
    while (!queue.isEmpty()) {
        let p = [];
        for (let i = queue.size(); i > 0; i--) {
            let preTraget = queue.dequeue();
            p.push(preTraget);
            //找到目标
            if (preTraget.x == end.x && preTraget.y == end.y) {
                stack.push(p);
                return step;
            }
            //遍历四个相邻格子
            for (let j = 0; j < 4; j++) {
                let nextX = preTraget.x + dx[j];
                let nextY = preTraget.y + dy[j];

                if (nextX < maxL_x && nextX >= 0 && nextY < maxL_y && nextY >= 0) {
                    let nextTraget = { x: nextX, y: nextY };
                    if (grid[nextX][nextY] == a && a < mem[nextX][nextY]) {
                        queue.enqueue({ ...nextTraget, f: { x: preTraget.x, y: preTraget.y } });
                        mem[nextX][nextY] = a;
                    }
                }
            }
        }
        stack.push(p);
        step++;
    }
}
/* 找出一条最短路径**/
function recall(end) {
    let path = [];
    let front = { x: end.x, y: end.y };
    while (stack.length) {
        let item = stack.pop();
        for (let i = 0; i < item.length; i++) {
            if (!item[i].f) break;
            if (item[i].x == front.x && item[i].y == front.y) {
                path.push({ x: item[i].x, y: item[i].y });
                front.x = item[i].f.x;
                front.y = item[i].f.y;
                break;
            }
        }
    }
    return path;
}

Таким образом, мы можем найти кратчайший путь и получить кратчайшее количество шагов.
Затем мы продолжаем обход нашего исходного массива (то есть исходного массива шахматной доски).
Нажмите на подсказку, чтобы осветить путь.

var step = getShortPath(panArr, { x: 0, y: 0 }, { x: 14, y: 14 }, 0);
console.log("最短距离----", step);
_perstep.innerHTML = `请在<span>${step}</span>步内走到终点`;
var path = recall({ x: 14, y: 14 });
console.log("路径---", path);
/** 提示 */
var tipCount = 0;
_tip.addEventListener("click", () => {
    console.log("9999", tipCount)
    elArr.forEach((item, index) => {
        let r = Math.floor(index / 15);
        let c = index % 15;
        path.forEach((_item, i) => {
            if (_item.x == r && _item.y == c) {
                // console.log("ooo",_item)
                if (tipCount % 2 == 0)
                    item.classList = "pan-item pan-path";
                else
                    item.classList = "pan-item";
            }
        })
    });
    tipCount++;
});

Таким образом, мы можем получить подсказку, как показано на рисунке:

WechatIMG314.png

Вы закончили Эй, разве это не удивительно~

конец

Конечно, в этой моей маленькой игре еще есть место для улучшения. Например:

  • Вы можете увеличить реквизит, а подобрать можно уменьшить количество пройденных шагов
  • Можно добавить уровни конфигурации
  • Также можно добавить функцию прыжка
  • ...

Оказывается, есть еще много вещей, которые может CSS3D, и как его использовать, зависит от того, насколько богато ваше воображение.

Ха-ха-ха, я действительно хочу использовать CSS3D, чтобы написать "мой Мир«Поигравшись, проблема с производительностью может быть немного большой.

Все примеры в этой статье испытаны на ПК.

Пробный адрес

Адрес источника

Приветствую всех на поправки, мастерство автора пока неглубоко, если есть что не так прошу поправить!

Статья поверхностная, надеюсь, вы не стесняетесь комментировать и ставить лайки~

Примечание: Данная статья является кропотливой авторской работой, перепечатки должны быть заявлены