Привет всем. Сегодня я вам расскажу как легко вы можете создать 3d игру прямо в вашем браузере и сделать вы сможете это очень быстро, примерно за минут 30.
Three.js
Это библиотека которая позволяет создавать 3д графику, она довольно проста и мощна. Мы будем использовать только её и больше никаких других фреймворков. Чтобы её подключить вам нужно скачать файл из этого рипозитория в папке build/three.js. Я качаю файл three.min.js (точно такая же, но весит меньше) и просто подключаю.
<html> <head></head> <body style="margin:0px;"> <script src="three.min.js"></script> <script> //тут пишем код </script> </body> </html>
Обратите внимание что отступы у body стоят нулевые, это нужно чтобы экран не съезжал вправо.
scene, camera и renderer
Создание графики в данной библиотеке основано на трёх столпах. Первое Scene это весь наш мир, вся информация, которая содержится о нём. Второе Camera это объект, который обозначает с какого ракурса мы будем смотреть на этот мир. Третье Render, объект который обрабатывает камеру и сцену и выдаёт результат на экран. Давайте создадим эти три объекта для начала.
var scene = new THREE.Scene(); //камера содержит 4 аргумента: угол обзора в градусах, отношение сторон экрана, и растояние обработки минимальное и масимальное var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); camera.lookAt(new THREE.Vector3(0, 0, 0)); //указываем куда будет смотреть камера scene.add(camera); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); //указываем размеры рендера, которые соответсвую нашей камере document.body.appendChild(renderer.domElement); //добавляем DOM элемент рендара в html код
Также нам нужна функция анимации, которая будет покадрово обновлять сцену.
function animate() { requestAnimationFrame(animate);//вызываем функцию при каждом обновлении кадра renderer.render(scene, camera);//обновляем рендер } animate();//вызываем первый раз функцию
Автообновление размеров экрана
Размеры окна браузера могут меняться нам нужно поставить событие на изменение размеров и обновлять размеры камеры и рендера.
function setSize() { renderer.setSize(window.innerWidth, window.innerHeight);// устанавливаем размеры рендера camera.apect = window.innerWidth/window.innerHeight;//устанаваливает отношение сторон камеры camera.updateProjectionMatrix();//обновляем камеру } window.addEventListener("resize", setSize);//создаем событие на изменение размеров окна браущера
Освещение
Чтобы было что-товидно нам нужен свет. Я создам два типа света, первый свет Ambient, которая распределён равномерно по всей сцене и как бы имитирует солнце. И второй Point (точечный) имитирует свет лампочки.
var light = new THREE.AmbientLight(0xffffff, 0.2);//создаем равномерный свет белого цвета с мощностью 0.2 scene.add(light);//добавляем в сцену равномерный свет var light = new THREE.SpotLight(0xffffff);//создаем точечный свет белогоцвета light.position.set(0, 5, 0);//задаем координаты точечного света scene.add(light);//добавляем в сцену точечный свет
Создаём первый объект
Объекты в three.js добавляется путём задание геометрии объекта и материала. Для начала создадим пол коричневого цвета, это будет просто обычный параллелограмм. Поэтому геометрию возьмём BOX и просто растянем его под размеры параллелограмма, а материал возьмём Phong чтобы хорошо было видно отражение точечного света.
//создаём пол с геометрией куба и Phong материалом коричневого цвета, side указываем с обоих сторон материал var floor = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0xad7600, side: THREE.DoubleSide})); floor.scale.set(5, 0.1, 10); //устанавливаем нужные размеры floor.position.y = -0.5; //указываем координату чтобы он был под ногами scene.add(floor); // добавляем в сцену
Смотрим что получилось:

Как мы видим у нас под ногами пол коричневого цвета.
Управление
Теперь нам нужно осуществить управление. Сделаем его с помощью кнопок WASD. W и S будем ходить вперёд назад. A и D будем поворачивать камеру по вертикальной оси. Сначала будем отслеживать нажатие кнопок WASD и записывать в переменную какая кнопка нажата в данный момент
var keys = { w: false, a: false, s: false, d: false }; //переменная указывает какие кнопки нажаты сейчас function keyDown(e) { //если нажали какую-то кнопку запишем в переменную это if (e.code == "KeyW") { keys.w = true; } else if (e.code == "KeyA") { keys.a = true; } else if (e.code == "KeyS") { keys.s = true; } else if (e.code == "KeyD") { keys.d = true; } } function keyUp(e) { //если отпустили кукую-то кнопку запишем в переменную это if (e.code == "KeyW") { keys.w = false; } else if (e.code == "KeyA") { keys.a = false; } else if (e.code == "KeyS") { keys.s = false; } else if (e.code == "KeyD") { keys.d = false; } } window.addEventListener("keydown", keyDown);//событие нажатие кнопки window.addEventListener("keyup", keyUp);//событие отпускание кнопки
Теперь зная какая кнопка нажата в данный момент, мы можем в нашу функцию анимации добавлять координаты и поворот камеры. Вот как мы изменяем функцию animate
function animate() { var speed = 0.05;//скорость пердвежение if (keys.w && !keys.s) {//если нажата w и не нажата s //идем вперет в зависиости от поворота камеры position[0] += speed*Math.sin(rotate); position[1] += speed*Math.cos(rotate); } if (!keys.w && keys.s) {//если нажата s и не нажата w //идем назад в зависимости от поворота камеры position[0] -= speed*Math.sin(rotate); position[1] -= speed*Math.cos(rotate); } if (keys.a && !keys.d) {//если нажата a и не нажата d rotate += speed; //поворачиваем влево } if (!keys.a && keys.d) {//если нажата d и не нажата a rotate -= speed; //поворачиваем вправо } // добавляем ограничения по координатам чтобы мы не могли уйти бесконечно далеко if (position[0] > 2.3) { position[0] = 2.3 } if (position[0] < -2.3) { position[0] = -2.3 } if (position[1] > 4.8) { position[1] = 4.8 } if (position[1] < -4.8) { position[1] = -4.8 } //устанаваливаем координаты камеры camera.position.x = position[0]; camera.position.z = position[1]; //устанавливаем точку куда должна смотреть камера camera.lookAt(position[0]+5*Math.sin(rotate), 0, position[1]+5*Math.cos(rotate)) //эти две строчки уже и так были в той функции requestAnimationFrame(animate); renderer.render(scene, camera); }
Теперь мы можем передивигаться.
Заканчиваем
Теперь же просто для завершённости проекта добавим еще стены и потолок точно по такому же принципу как с полом и покрасим стены в зелёный и потолок в сиреневвый.
var wall1 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall1.scale.set(5, 1, 0.1); wall1.position.z = 5; scene.add(wall1); var wall2 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall2.scale.set(5, 1, 0.1); wall2.position.z = -5; scene.add(wall2); var wall3 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall3.scale.set(0.1, 1, 10); wall3.position.x = 2.5; scene.add(wall3); var wall4 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall4.scale.set(0.1, 1, 10); wall4.position.x = -2.5; scene.add(wall4); var ceil = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0xff00aa, side: THREE.DoubleSide})); ceil.scale.set(5, 0.1, 10); ceil.position.y = 0.5; scene.add(ceil);
Вот что у нас получилось:

Конечный код
Вот полностью код, который мы написали:
<html> <head></head> <body style="margin:0px;"> <script src="three.min.js"></script> <script> var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); camera.lookAt(new THREE.Vector3(0, 0, 0)); scene.add(camera); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); function setSize() { renderer.setSize(window.innerWidth, window.innerHeight); camera.apect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); } window.addEventListener("resize", setSize); var floor = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0xad7600, side: THREE.DoubleSide})); floor.scale.set(5, 0.1, 10); floor.position.y = -0.5; scene.add(floor); var wall1 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall1.scale.set(5, 1, 0.1); wall1.position.z = 5; scene.add(wall1); var wall2 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall2.scale.set(5, 1, 0.1); wall2.position.z = -5; scene.add(wall2); var wall3 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall3.scale.set(0.1, 1, 10); wall3.position.x = 2.5; scene.add(wall3); var wall4 = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0x00870e, side: THREE.DoubleSide})); wall4.scale.set(0.1, 1, 10); wall4.position.x = -2.5; scene.add(wall4); var ceil = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshPhongMaterial({color: 0xff00aa, side: THREE.DoubleSide})); ceil.scale.set(5, 0.1, 10); ceil.position.y = 0.5; scene.add(ceil); var light = new THREE.AmbientLight(0xffffff, 0.2); scene.add(light); var light = new THREE.SpotLight(0xffffff); light.position.set(0, 5, 0); scene.add(light); var keys = { w: false, a: false, s: false, d: false }; function keyDown(e) { if (e.code == "KeyW") { keys.w = true; } else if (e.code == "KeyA") { keys.a = true; } else if (e.code == "KeyS") { keys.s = true; } else if (e.code == "KeyD") { keys.d = true; } } function keyUp(e) { if (e.code == "KeyW") { keys.w = false; } else if (e.code == "KeyA") { keys.a = false; } else if (e.code == "KeyS") { keys.s = false; } else if (e.code == "KeyD") { keys.d = false; } } window.addEventListener("keydown", keyDown); window.addEventListener("keyup", keyUp); function animate() { var speed = 0.05; if (keys.w && !keys.s) { position[0] += speed*Math.sin(rotate); position[1] += speed*Math.cos(rotate); } if (!keys.w && keys.s) { position[0] -= speed*Math.sin(rotate); position[1] -= speed*Math.cos(rotate); } if (keys.a && !keys.d) { rotate += speed; } if (!keys.a && keys.d) { rotate -= speed; } if (position[0] > 2.3) { position[0] = 2.3 } if (position[0] < -2.3) { position[0] = -2.3 } if (position[1] > 4.8) { position[1] = 4.8 } if (position[1] < -4.8) { position[1] = -4.8 } camera.position.x = position[0]; camera.position.z = position[1]; camera.lookAt(position[0]+5*Math.sin(rotate), 0, position[1]+5*Math.cos(rotate)); requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); </script> </body> </html>
Заключение
Теперь вы знаете как легко создать 3д графику в браузере, даже если вы не хотели создавать игру. 3д графика может пригодиться в проектирование и еще много где. Также в данной библиотеке можно добавлять текстуры, 3д модели и прочее, можно еще сделать управление с помощью мыши и клавиатуры как в шутерах, например. Если вы хотите узнать как это сделать, я могу создать отдельные статьи про это если в голосовании будет много желающих, я сделаю такие статьи. В данной статье я показал лишь основы, чтобы вы знали с чего начать. Спасибо за внимание