Вторая часть урока посвящена добавлению статических блоков. Это будет коротко, но очень важно.
- Часть 1: Введение и игровой цикл
- Часть 2: Статические блоки и ведение счета
- Часть 3: Добавление и перемещение блока
- Часть 4: обнаружение столкновений
- Часть 5: Аудио и оценка
Присоединены или отделены?
Подумай, как мы играем в тетрис. Когда блок движется, мы трансформируем и вращаем его свободно. Кубы, составляющие блок, четко связаны, и интуитивно понятно, что их представление в коде должно быть таким же. С другой стороны, когда мы пытаемся завершить срез (в 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
Если у вас возникли проблемы с любым из них, проверьте учебник еще раз или задайте вопрос в комментариях ниже.