Рубрики
Без рубрики

Наивный город поколения

На прошлой неделе я играл с идеей поколения города, я не хотел делать мою обычную старую 2D-вид, я действительно хотел бросить себя немного, поэтому я посмотрел на 3D. Я знал о …

Автор оригинала: 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();

Что дает нам хороший маленький результат:

город-2

Если вы следуете наряду с кодом в этой статье, вы не сможете вращать/увеличить результат. Чтобы добавить эту функциональность, посмотрите на Эта орбита контролирует пример Отказ

Вы можете найти интерактивный пример и полный код здесь , это должно дать вам еще один пример контроля орбиты в контексте приложения.

Это очень наивный и быстрый способ создания чего-то, что выглядит немного как город. Это совсем не очень продвинутся. Я буду расширяться по этой теме в моей книге, выпущенном в следующем году, используя P5.js, где вы сможете найти лучшие способы создания дорог и зданий. Надеюсь, вы смогли взять что-то из этой статьи, по крайней мере, у вас есть классная новая игрушка, где можно играть, где-то поехать и играть со случайными городами.

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