Статьи

3D Tetris с учебником Three.js — Часть 2

Вторая часть урока посвящена добавлению статических блоков. Это будет коротко, но очень важно.

Присоединены или отделены?

Подумай, как мы играем в тетрис. Когда блок движется, мы трансформируем и вращаем его свободно. Кубы, составляющие блок, четко связаны, и интуитивно понятно, что их представление в коде должно быть таким же. С другой стороны, когда мы пытаемся завершить срез (в 2D — строке) и нам это удается, кубы удаляются, и блок, который был их источником, не имеет значения в этой точке. На самом деле, это не должно иметь значения — некоторые блоки из блока могут быть удалены, а другие нет. Отслеживание происхождения коробки потребовало бы постоянного разделения и слияния геометрий и поверьте мне — это было бы безумным беспорядком. В оригинальном 2D тетрисе иногда цвет квадрата был индикатором исходного блока. Однако в 3D нам нужен быстрый способ показать ось Z, и цвет идеально подходит для этого.

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

Добавление статического блока

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

Tetris.staticBlocks = [];
Tetris.zColors = [
  0x6666ff, 0x66ffff, 0xcc68EE, 0x666633, 0x66ff66, 0x9966ff, 0x00ff66, 0x66EE33, 0x003399, 0x330099, 0xFFA500, 0x99ff00, 0xee1289, 0x71C671, 0x00BFFF, 0x666633, 0x669966, 0x9966ff
];
Tetris.addStaticBlock = function(x,y,z) {
  if(Tetris.staticBlocks[x] === undefined) Tetris.staticBlocks[x] = [];
  if(Tetris.staticBlocks[x][y] === undefined) Tetris.staticBlocks[x][y] = [];
 
  var mesh = THREE.SceneUtils.createMultiMaterialObject(new THREE.CubeGeometry( Tetris.blockSize, Tetris.blockSize, Tetris.blockSize), [
    new THREE.MeshBasicMaterial({color: 0x000000, shading: THREE.FlatShading, wireframe: true, transparent: true}),
    new THREE.MeshBasicMaterial({color: Tetris.zColors[z]})
  ] );
 
  mesh.position.x = (x - Tetris.boundingBoxConfig.splitX/2)*Tetris.blockSize + Tetris.blockSize/2;
  mesh.position.y = (y - Tetris.boundingBoxConfig.splitY/2)*Tetris.blockSize + Tetris.blockSize/2;
  mesh.position.z = (z - Tetris.boundingBoxConfig.splitZ/2)*Tetris.blockSize + Tetris.blockSize/2;
  mesh.overdraw = true;
 
  Tetris.scene.add(mesh);
  Tetris.staticBlocks[x][y][z] = mesh;
};

 

Здесь есть много чего объяснить.

Цвета и материалы

Tetris.zColors хранит список цветов, которые указывают положение куба на оси z. Мне бы хотелось, чтобы у вас был красивый куб, поэтому он должен иметь цвет и обведенную рамку. Я собираюсь использовать то, что не очень популярно в обучающих программах Three.js — multiMaterials. В Three.js есть функция SceneUtils, которая принимает геометрию и массив (обратите внимание на скобки []) материалов. Если вы посмотрите на источник Three.js:

  createMultiMaterialObject : function ( geometry, materials ) {
  var i, il = materials.length, group = new THREE.Object3D();
  for ( i = 0; i < il; i ++ ) {
    var object = new THREE.Mesh( geometry, materials[ i ] );
    group.add( object );
  }
  return group;
},

 

Это очень простой хак, который создает сетку для каждого материала. В чистом WebGL есть лучшие способы достичь того же результата (например, вызывать draw два раза, один раз с gl.LINES, а второй с gl.something), но обычно эта функция используется, например, для объединения текстур и материалов одновременно. Время — не разные виды рисования.

Положение в 3D пространстве

Теперь, почему, черт возьми, позиция выглядит так?

mesh.position.x = (x - Tetris.boundingBoxConfig.splitX/2)*Tetris.blockSize + Tetris.blockSize/2;

 Наш центр правления на init был помещен в (0,0,0) пункт. Это не очень хорошее место, так как это означает, что некоторые кубы будут иметь отрицательную позицию, а другие положительные. В нашем случае было бы лучше указать угол объекта. Более того, мы хотели бы думать о позициях наших блоков как о дискретных значениях от 1 до 6 или, по крайней мере, от 0 до 5. Three.js (и WebGL, OpenGL и все остальное) использует свои собственные единицы измерения, которые скорее относятся к метрам или пикселям. , Если вы помните, в конфиге мы ставим значение

Tetris.blockSize = boundingBoxConfig.width/boundingBoxConfig.splitX;

 это ответственно за обращение. Итак, как резюме:

// transform 0-5 to -3 - +2
(x - Tetris.boundingBoxConfig.splitX/2)
 // scale to Three.js units
*Tetris.blockSize
 // we specify cube center, not a corner - we have to shift position
 + Tetris.blockSize/2

Хороший тест

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

var i = 0, j = 0, k = 0, interval = setInterval(function() {if(i==6) {i=0;j++;} if(j==6) {j=0;k++;} if(k==6) {clearInterval(interval); return;} Tetris.addStaticBlock(i,j,k); i++;},30)

 

Следует оживить заполнение доски кубиками.

Ведение счета

Небольшая вспомогательная функция для ведения счета:

Tetris.currentPoints = 0;
Tetris.addPoints = function(n) {
  Tetris.currentPoints += n;
  Tetris.pointsDOM.innerHTML = Tetris.currentPoints;
  Cufon.replace('#points');
}

 

Вы также можете позвонить с консоли.

После этого урока вы должны:

  • Знайте разницу между подключенной и разделенной геометрией.
  • Понять основы материалов и как их смешивать.
  • Понять, что именно означает позиция в Three.js.

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