Автор оригинала: Michael Curry.
На прошлой неделе я играл с идеей поколения города, я не хотел делать мою обычную старую 2D-вид, я действительно хотел бросить себя немного, поэтому я посмотрел на 3D. Я знал об библиотеке JavaScript для некоторое время, называемая Threy.js, и я пытался использовать ее раньше, но у меня никогда не было окончательной цели, поэтому я только прошел примеры, но на этот раз был Конечная цель, и я чувствую, что результат не все так плохо.
Это довольно известно, что для создания города есть много идей и алгоритмов, которые могут быть использованы, L-системы и космические колонизации для дорог, двоичные пространственные перегородки для построения интерьеров, каждый элемент, который вы создаете, необходимо как-то генерировать, и это Для чего эти алгоритмы. Для этого небольшого проекта я просто хотел получить быстрый прототип вверх и бежать, доказательство концепции, если вам нравится.
Я решил, что сохранить его базовым, у меня будут только три типа «плитки», плитка на строительстве, дорожная плитка и трава. Я также решил, что я собирался сделать только общий город, вы увидите дороги и форму/размер зданий, но вы не сможете войти в любую из зданий (хотя это может потенциально прийти позже).
Как только я решил об этом, пришло время решить, как все выложить их. Я, по сути, сгенерировал 2D-массив ценностей, которые могут быть «дороги», «здание» или «трава».
Вот код для генерации решетки:
var grid = []; for(var x = 0; x <= 12; x++) { grid[x] = []; for(var y = 0; y <= 12; y++) { if(x % 4 == 0 || y % 4 == 0) { grid[x][y] = new Road(x, y); } else { if(Math.random() > 0.5) { grid[x][y] = new Building(x, y, Math.ceil(Math.random() * 3)) } else { grid[x][y] = new Grass(x, y); } } } } return grid;
Итак, мы генерируем 12 массивов с 12 значений, это дает нам сетку, которая составляет 12 блоков на 12 блоков. Логика следует, что если X делится на 4 или Y делится на 4, то этот блок будет дорожным блоком. Это дает нам приятную сетку для дорог, с границей вокруг края нашего «город» и оставляет 3 х 3 участки земли для зданий или травы, чтобы быть помещены между ними. В то время как это не обязательно совпадает с тем, как города смотрят в реальную жизнь, он эмулирует шаблон сетки, который мы видим в крупных городах в хорошую степень для нашего прототипа. Здания/трава часть логики по сути, по сути, флип монет, есть (примерно) 50/50 шанс на оставшиеся блоки в нашем городе, являющихся зданиями/травой.
Следующим шагом является фактически нарисовать это на экран. Как вы можете видеть в фрагменте кода выше, я фактически называл конструкторами при назначении элементов массива. Я написал структуру данных для каждого типа блока. Каждый блок имел координаты в своих параметрах конструктора, чтобы решить, где блок должен быть размещен, единственное отличие было строительным блоком. Я также дал здание высота, это обеспечит бы, чтобы у нас были некоторые здания разных размеров и заставляют его выглядеть более реалистичными. Созданные здания все будут между одной и четырьмя «историями». Выявляли размеры/положение каждого блока, пришло время создать сетку.
Сетка – это то, что Threy.js может принимать и представлять на экран. Чтобы создать сетку, вам нужна пара вещей, материал и «геометрия». Геометрия диктует форму объекта, и материал диктует, как выглядит объект, какого цвета это цвет, или если у него есть текстура. Для всех блоков создание их было очень простым.
Строительство:
this.geometry = new THREE.BoxGeometry(1, height, 1); //Coloured a mid-grey this.material = new THREE.MeshLambertMaterial({ color: 0x888888 });
Дорога:
this.geometry = new THREE.BoxGeometry(1, 1, 1); //Coloured a dark-grey this.material = new THREE.MeshLambertMaterial({ color: 0x333333 });
Трава:
this.geometry = new THREE.BoxGeometry(1, 1, 1); //Coloured a bright green this.material = new THREE.MeshLambertMaterial({ color: 0x22FF32 });
Тогда для каждого блока все, что осталось сделать, было создать сетку:
this.mesh = new THREE.Mesh(this.geometry, this.material);
Наконец, на объекте сетки я просто должен был установить позицию:
this.mesh.position.x = x; this.mesh.position.y = 0; this.mesh.position.z = y;
Это несколько путает вас, вероятно, и это потому, что это просто прямо вверх плохой код. То, что я сделал, передается из 2D-массива в 3D-пространство, что означает, что наша сетка теперь сплющена, Z становится Y, и теперь мы должны заполнить с чем-то. Если вы представляете, что мы смотрим на доску, которая стоит прямо, с нашей осью Y, бегущей вверх/вниз по доске, и наша ось X работает горизонтально вдоль доски. Представьте себе сейчас, доска падает. Когда доска попадает на пол, ось Y больше не проходит вверх/вниз по доске, теперь это ось Z, которая делает это, оси Y теперь будет выдвигаться из платы.
Теперь, когда мы создали все это, пришло время на самом деле нарисовать его на экран. Я настроил три.js, создавая сцену и камеру:
var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); var renderer = new THREE.WebGLRenderer();
Создав их, мне нужно было создать рендер. Наконец, нам просто нужно настроить положение/вращение камеры, а размер рендерера и цвет фона и прикрепить его к нашему документу, чтобы мы на самом деле увидим это.
Камера:
camera.position.x = 4.5; camera.position.y = 10; camera.position.z = 20; camera.rotation.x = -0.5;
Рендер:
renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor( 0xeaeaff, 1 ); document.body.appendChild(renderer.domElement);
Я также настроил свет:
var light = new THREE.HemisphereLight(0xafafaf, 1); scene.add(light);
Теперь все, что осталось сделать, это добавить сетки на сцену:
for(var x = 0; x < grid.length; x++) { for(var y = 0; y < grid[x].length; y++) { var mesh = grid[x][y].mesh; scene.add(mesh); } }
И, наконец, оживить это:
function animate() { controls.update(); requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
Что дает нам хороший маленький результат:
Если вы следуете наряду с кодом в этой статье, вы не сможете вращать/увеличить результат. Чтобы добавить эту функциональность, посмотрите на Эта орбита контролирует пример Отказ
Вы можете найти интерактивный пример и полный код здесь , это должно дать вам еще один пример контроля орбиты в контексте приложения.
Это очень наивный и быстрый способ создания чего-то, что выглядит немного как город. Это совсем не очень продвинутся. Я буду расширяться по этой теме в моей книге, выпущенном в следующем году, используя P5.js, где вы сможете найти лучшие способы создания дорог и зданий. Надеюсь, вы смогли взять что-то из этой статьи, по крайней мере, у вас есть классная новая игрушка, где можно играть, где-то поехать и играть со случайными городами.
Есть много вещей, которые это можно было бы расширить, может быть, вы хотите создать большие города, или потенциально вы можете захотеть играть с плотностью зданий, что делает больше/большие здания порождения в середине, а меньше/меньшие здания появления края. В качестве альтернативы, вы могли бы сделать это так, чтобы город растет, здания становятся больше, или город становится все большее время, мне было интересно посмотреть, что люди придумывают 🙂