HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#Руководства</a></p>
1 <p><a>#Руководства</a></p>
2 <ul><li>11 окт 2019</li>
2 <ul><li>11 окт 2019</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><p>Современные браузеры позволяют создавать игры с полноценной графикой. Рассказываем, как написать простые гонки на JavaScript и HTML5.</p>
4 </ul><p>Современные браузеры позволяют создавать игры с полноценной графикой. Рассказываем, как написать простые гонки на JavaScript и HTML5.</p>
5 <p> vlada_maestro / shutterstock</p>
5 <p> vlada_maestro / shutterstock</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
7 <p>Сейчас браузеры дают JavaScript-разработчикам огромное количество возможностей для создания интересных сайтов. Раньше для этого использовался Flash - он был популярен, и на нём было создано бессчётное количество игр, плееров, необычных интерфейсов и так далее. Однако они уже не запустятся ни в одном современном браузере.</p>
7 <p>Сейчас браузеры дают JavaScript-разработчикам огромное количество возможностей для создания интересных сайтов. Раньше для этого использовался Flash - он был популярен, и на нём было создано бессчётное количество игр, плееров, необычных интерфейсов и так далее. Однако они уже не запустятся ни в одном современном браузере.</p>
8 <p>Дело в том, что технология Flash тяжеловесна, а также полна уязвимостей, поэтому от неё стали отказываться. Тем более что появилась альтернатива в виде<a>HTML5</a> - в этой версии появился элемент canvas.</p>
8 <p>Дело в том, что технология Flash тяжеловесна, а также полна уязвимостей, поэтому от неё стали отказываться. Тем более что появилась альтернатива в виде<a>HTML5</a> - в этой версии появился элемент canvas.</p>
9 <p>Canvas - это холст, на котором можно рисовать с помощью JS-команд. Его можно использовать для создания анимированных фонов, различных конструкторов и, самое главное, игр.</p>
9 <p>Canvas - это холст, на котором можно рисовать с помощью JS-команд. Его можно использовать для создания анимированных фонов, различных конструкторов и, самое главное, игр.</p>
10 <p>Из этой статьи вы узнаете, как создать браузерную игру на JavaScript и HTML5. Но прежде рекомендуем ознакомиться с <a>объектно-ориентированным программированием</a>в JS (достаточно понимать, что такое класс, метод и объект). Оно лучше всего подходит для создания игр, потому что позволяет работать с сущностями, а не с абстрактными данными. Однако есть и недостаток: ООП не поддерживается ни в одной из версий Internet Explorer.</p>
10 <p>Из этой статьи вы узнаете, как создать браузерную игру на JavaScript и HTML5. Но прежде рекомендуем ознакомиться с <a>объектно-ориентированным программированием</a>в JS (достаточно понимать, что такое класс, метод и объект). Оно лучше всего подходит для создания игр, потому что позволяет работать с сущностями, а не с абстрактными данными. Однако есть и недостаток: ООП не поддерживается ни в одной из версий Internet Explorer.</p>
11 <p>Для начала нужно создать страницу, на которой будет отображаться холст. Для этого потребуется совсем немного HTML:</p>
11 <p>Для начала нужно создать страницу, на которой будет отображаться холст. Для этого потребуется совсем немного HTML:</p>
12 &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;JS Game&lt;/title&gt; &lt;link rel="stylesheet" href="style.css"&gt; &lt;meta charset="utf-8"&gt; &lt;/head&gt; &lt;body&gt; &lt;div class="wrapper"&gt; &lt;canvas width="0" height="0" class="canvas" id="canvas"&gt;Ваш браузер не поддерживает JavaScript и HTML5!&lt;/canvas&gt; &lt;/div&gt; &lt;script src="game.js"&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt;<p>Теперь нужно добавить стили:</p>
12 &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;JS Game&lt;/title&gt; &lt;link rel="stylesheet" href="style.css"&gt; &lt;meta charset="utf-8"&gt; &lt;/head&gt; &lt;body&gt; &lt;div class="wrapper"&gt; &lt;canvas width="0" height="0" class="canvas" id="canvas"&gt;Ваш браузер не поддерживает JavaScript и HTML5!&lt;/canvas&gt; &lt;/div&gt; &lt;script src="game.js"&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt;<p>Теперь нужно добавить стили:</p>
13 body, html { width: 100%; height: 100%; padding: 0px; margin: 0px; overflow: hidden; } .wrapper { width: 100%; height: 100%; } .canvas { width: 100%; height: 100%; background: #000; }<p>Обратите внимание, что в HTML элементу canvas были заданы нулевые ширина и высота, в то время как в CSS указано 100%. В этом плане холст ведёт себя как изображение. У него есть фактическое и видимое разрешение.</p>
13 body, html { width: 100%; height: 100%; padding: 0px; margin: 0px; overflow: hidden; } .wrapper { width: 100%; height: 100%; } .canvas { width: 100%; height: 100%; background: #000; }<p>Обратите внимание, что в HTML элементу canvas были заданы нулевые ширина и высота, в то время как в CSS указано 100%. В этом плане холст ведёт себя как изображение. У него есть фактическое и видимое разрешение.</p>
14 <p>С помощью стилей меняется видимое разрешение. Однако при этом размеры картинки останутся прежними: она просто растянется или сожмётся. Поэтому фактические ширина и высота будут указаны позже - через скрипт.</p>
14 <p>С помощью стилей меняется видимое разрешение. Однако при этом размеры картинки останутся прежними: она просто растянется или сожмётся. Поэтому фактические ширина и высота будут указаны позже - через скрипт.</p>
15 <p>Для начала добавим заготовку скрипта для игры:</p>
15 <p>Для начала добавим заготовку скрипта для игры:</p>
16 var canvas = document.getElementById("canvas"); //Получение холста из DOM var ctx = canvas.getContext("2d"); //Получение контекста - через него можно работать с холстом var scale = 0.1; //Масштаб машин Resize(); // При загрузке страницы задаётся размер холста window.addEventListener("resize", Resize); //При изменении размеров окна будут меняться размеры холста window.addEventListener("keydown", function (e) { KeyDown(e); }); //Получение нажатий с клавиатуры var objects = []; //Массив игровых объектов var roads = []; //Массив с фонами var player = null; //Объект, которым управляет игрок, - тут будет указан номер объекта в массиве objects function Start() { timer = setInterval(Update, 1000 / 60); //Состояние игры будет обновляться 60 раз в секунду - при такой частоте обновление происходящего будет казаться очень плавным } function Stop() { clearInterval(timer); //Остановка обновления } function Update() //Обновление игры { Draw(); } function Draw() //Работа с графикой { ctx.clearRect(0, 0, canvas.width, canvas.height); //Очистка холста от предыдущего кадра } function KeyDown(e) { switch(e.keyCode) { case 37: //Влево break; case 39: //Вправо break; case 38: //Вверх break; case 40: //Вниз break; case 27: //Esc break; } } function Resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }<p>В этом скрипте есть всё, что необходимо для создания игры: данные (массивы), функции обновления, прорисовки и управления. Остаётся только дополнить это основной логикой. То есть указать, как именно объекты будут себя вести и как будут выводиться на холст.</p>
16 var canvas = document.getElementById("canvas"); //Получение холста из DOM var ctx = canvas.getContext("2d"); //Получение контекста - через него можно работать с холстом var scale = 0.1; //Масштаб машин Resize(); // При загрузке страницы задаётся размер холста window.addEventListener("resize", Resize); //При изменении размеров окна будут меняться размеры холста window.addEventListener("keydown", function (e) { KeyDown(e); }); //Получение нажатий с клавиатуры var objects = []; //Массив игровых объектов var roads = []; //Массив с фонами var player = null; //Объект, которым управляет игрок, - тут будет указан номер объекта в массиве objects function Start() { timer = setInterval(Update, 1000 / 60); //Состояние игры будет обновляться 60 раз в секунду - при такой частоте обновление происходящего будет казаться очень плавным } function Stop() { clearInterval(timer); //Остановка обновления } function Update() //Обновление игры { Draw(); } function Draw() //Работа с графикой { ctx.clearRect(0, 0, canvas.width, canvas.height); //Очистка холста от предыдущего кадра } function KeyDown(e) { switch(e.keyCode) { case 37: //Влево break; case 39: //Вправо break; case 38: //Вверх break; case 40: //Вниз break; case 27: //Esc break; } } function Resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }<p>В этом скрипте есть всё, что необходимо для создания игры: данные (массивы), функции обновления, прорисовки и управления. Остаётся только дополнить это основной логикой. То есть указать, как именно объекты будут себя вести и как будут выводиться на холст.</p>
17 <p>Во время вызова функции<em>Update()</em>будут меняться состояния игровых объектов. После этого они отрисовываются на canvas с помощью функции<em>Draw()</em>. То есть на самом деле мы не двигаем объекты на холсте - мы рисуем их один раз, потом меняем координаты, стираем старое изображение и выводим объекты с новыми координатами. Всё это происходит так быстро, что создаётся иллюзия движения.</p>
17 <p>Во время вызова функции<em>Update()</em>будут меняться состояния игровых объектов. После этого они отрисовываются на canvas с помощью функции<em>Draw()</em>. То есть на самом деле мы не двигаем объекты на холсте - мы рисуем их один раз, потом меняем координаты, стираем старое изображение и выводим объекты с новыми координатами. Всё это происходит так быстро, что создаётся иллюзия движения.</p>
18 <p>Рассмотрим это на примере дороги.</p>
18 <p>Рассмотрим это на примере дороги.</p>
19 <p>На холсте выводится вот такое изображение и постепенно двигается вниз. Сразу же следом будет выводиться ещё одна такая же картинка, благодаря чему создастся ощущение бесконечной дороги.</p>
19 <p>На холсте выводится вот такое изображение и постепенно двигается вниз. Сразу же следом будет выводиться ещё одна такая же картинка, благодаря чему создастся ощущение бесконечной дороги.</p>
20 <p>Для этого создадим класс<em><strong>Road</strong></em>:</p>
20 <p>Для этого создадим класс<em><strong>Road</strong></em>:</p>
21 class Road { constructor(image, y) { this.x = 0; this.y = y; this.image = new Image(); this.image.src = image; } Update(road) { this.y += speed; //При обновлении изображение смещается вниз if(this.y &gt; window.innerHeight) //Если изображение ушло за край холста, то меняем положение { this.y = road.y - this.image.height + speed; //Новое положение указывается с учётом второго фона } } }<p>В массив с фонами добавляются два объекта класса<em><strong>Road</strong></em>:</p>
21 class Road { constructor(image, y) { this.x = 0; this.y = y; this.image = new Image(); this.image.src = image; } Update(road) { this.y += speed; //При обновлении изображение смещается вниз if(this.y &gt; window.innerHeight) //Если изображение ушло за край холста, то меняем положение { this.y = road.y - this.image.height + speed; //Новое положение указывается с учётом второго фона } } }<p>В массив с фонами добавляются два объекта класса<em><strong>Road</strong></em>:</p>
22 var roads = [ new Road("images/road.jpg", 0), new Road("images/road.jpg", 626) ]; //Массив с фонами<p>Теперь можно изменить функцию<em>Update()</em>, чтобы положение изображений менялось с каждым кадром.</p>
22 var roads = [ new Road("images/road.jpg", 0), new Road("images/road.jpg", 626) ]; //Массив с фонами<p>Теперь можно изменить функцию<em>Update()</em>, чтобы положение изображений менялось с каждым кадром.</p>
23 function Update() //Обновление игры { roads[0].Update(roads[1]); roads[1].Update(roads[0]); Draw(); }<p>Остаётся только добавить вывод этих изображений:</p>
23 function Update() //Обновление игры { roads[0].Update(roads[1]); roads[1].Update(roads[0]); Draw(); }<p>Остаётся только добавить вывод этих изображений:</p>
24 function Draw() //Работа с графикой { ctx.clearRect(0, 0, canvas.width, canvas.height); //Очистка холста от предыдущего кадра for(var i = 0; i &lt; roads.length; i++) { ctx.drawImage ( roads[i].image, //Изображение для отрисовки 0, //Начальное положение по оси X на изображении 0, //Начальное положение по оси Y на изображении roads[i].image.width, //Ширина изображения roads[i].image.height, //Высота изображения roads[i].x, //Положение по оси X на холсте roads[i].y, //Положение по оси Y на холсте canvas.width, //Ширина изображения на холсте canvas.width //Так как ширина и высота фона одинаковые, в качестве высоты указывается ширина ); } }<p>Теперь можно посмотреть, как это работает в игре:</p>
24 function Draw() //Работа с графикой { ctx.clearRect(0, 0, canvas.width, canvas.height); //Очистка холста от предыдущего кадра for(var i = 0; i &lt; roads.length; i++) { ctx.drawImage ( roads[i].image, //Изображение для отрисовки 0, //Начальное положение по оси X на изображении 0, //Начальное положение по оси Y на изображении roads[i].image.width, //Ширина изображения roads[i].image.height, //Высота изображения roads[i].x, //Положение по оси X на холсте roads[i].y, //Положение по оси Y на холсте canvas.width, //Ширина изображения на холсте canvas.width //Так как ширина и высота фона одинаковые, в качестве высоты указывается ширина ); } }<p>Теперь можно посмотреть, как это работает в игре:</p>
25 <p>Пора добавить игрока и NPC. Для этого нужно написать класс<em><strong>Car</strong></em>. В нём будет метод<em>Move()</em>, с помощью которого игрок управляет своим автомобилем. Движение NPC будет осуществляться с помощью<em>Update(),</em>в котором просто меняется координата<em>Y</em>.</p>
25 <p>Пора добавить игрока и NPC. Для этого нужно написать класс<em><strong>Car</strong></em>. В нём будет метод<em>Move()</em>, с помощью которого игрок управляет своим автомобилем. Движение NPC будет осуществляться с помощью<em>Update(),</em>в котором просто меняется координата<em>Y</em>.</p>
26 class Car { constructor(image, x, y) { this.x = x; this.y = y; this.image = new Image(); this.image.src = image; } Update() { this.y += speed; } Move(v, d) { if(v == "x") //Перемещение по оси X { this.x += d; //Смещение //Если при смещении объект выходит за края холста, то изменения откатываются if(this.x + this.image.width * scale &gt; canvas.width) { this.x -= d; } if(this.x &lt; 0) { this.x = 0; } } else //Перемещение по оси Y { this.y += d; if(this.y + this.image.height * scale &gt; canvas.height) { this.y -= d; } if(this.y &lt; 0) { this.y = 0; } } } }<p>Создадим первый объект, чтобы проверить.</p>
26 class Car { constructor(image, x, y) { this.x = x; this.y = y; this.image = new Image(); this.image.src = image; } Update() { this.y += speed; } Move(v, d) { if(v == "x") //Перемещение по оси X { this.x += d; //Смещение //Если при смещении объект выходит за края холста, то изменения откатываются if(this.x + this.image.width * scale &gt; canvas.width) { this.x -= d; } if(this.x &lt; 0) { this.x = 0; } } else //Перемещение по оси Y { this.y += d; if(this.y + this.image.height * scale &gt; canvas.height) { this.y -= d; } if(this.y &lt; 0) { this.y = 0; } } } }<p>Создадим первый объект, чтобы проверить.</p>
27 var objects = [ new Car("images/car.png", 15, 10) ]; //Массив игровых объектов var player = 0; //номер объекта, которым управляет игрок<p>Теперь в функцию<em>Draw()</em>нужно добавить команду отрисовки автомобилей.</p>
27 var objects = [ new Car("images/car.png", 15, 10) ]; //Массив игровых объектов var player = 0; //номер объекта, которым управляет игрок<p>Теперь в функцию<em>Draw()</em>нужно добавить команду отрисовки автомобилей.</p>
28 for(var i = 0; i &lt; objects.length; i++) { ctx.drawImage ( objects[i].image, //Изображение для отрисовки 0, //Начальное положение по оси X на изображении 0, //Начальное положение по оси Y на изображении objects[i].image.width, //Ширина изображения objects[i].image.height, //Высота изображения objects[i].x, //Положение по оси X на холсте objects[i].y, //Положение по оси Y на холсте objects[i].image.width * scale, //Ширина изображения на холсте, умноженная на масштаб objects[i].image.height * scale //Высота изображения на холсте, умноженная на масштаб ); }<p>В функцию<em>KeyDown()</em>, которая вызывается при нажатии на клавиатуру, нужно добавить вызов метода<em>Move()</em>.</p>
28 for(var i = 0; i &lt; objects.length; i++) { ctx.drawImage ( objects[i].image, //Изображение для отрисовки 0, //Начальное положение по оси X на изображении 0, //Начальное положение по оси Y на изображении objects[i].image.width, //Ширина изображения objects[i].image.height, //Высота изображения objects[i].x, //Положение по оси X на холсте objects[i].y, //Положение по оси Y на холсте objects[i].image.width * scale, //Ширина изображения на холсте, умноженная на масштаб objects[i].image.height * scale //Высота изображения на холсте, умноженная на масштаб ); }<p>В функцию<em>KeyDown()</em>, которая вызывается при нажатии на клавиатуру, нужно добавить вызов метода<em>Move()</em>.</p>
29 function KeyDown(e) { switch(e.keyCode) { case 37: //Влево objects[player].Move("x", -speed); break; case 39: //Вправо objects[player].Move("x", speed); break; case 38: //Вверх objects[player].Move("y", -speed); break; case 40: //Вниз objects[player].Move("y", speed); break; case 27: //Esc if(timer == null) { Start(); } else { Stop(); } break; } }<p>Теперь можно проверить отрисовку и управление.</p>
29 function KeyDown(e) { switch(e.keyCode) { case 37: //Влево objects[player].Move("x", -speed); break; case 39: //Вправо objects[player].Move("x", speed); break; case 38: //Вверх objects[player].Move("y", -speed); break; case 40: //Вниз objects[player].Move("y", speed); break; case 27: //Esc if(timer == null) { Start(); } else { Stop(); } break; } }<p>Теперь можно проверить отрисовку и управление.</p>
30 <p>Следующий шаг - добавление машин. Они будут создаваться на ходу и удаляться, когда зайдут за край.</p>
30 <p>Следующий шаг - добавление машин. Они будут создаваться на ходу и удаляться, когда зайдут за край.</p>
31 <p>Для этого понадобится функция генерации случайных чисел:</p>
31 <p>Для этого понадобится функция генерации случайных чисел:</p>
32 function RandomInteger(min, max) { let rand = min - 0.5 + Math.random() * (max - min + 1); return Math.round(rand); }<p>С её помощью в функции<em>Update()</em>с определённой вероятностью будет создаваться объект и добавляться в массив<em><strong>objects</strong></em>:</p>
32 function RandomInteger(min, max) { let rand = min - 0.5 + Math.random() * (max - min + 1); return Math.round(rand); }<p>С её помощью в функции<em>Update()</em>с определённой вероятностью будет создаваться объект и добавляться в массив<em><strong>objects</strong></em>:</p>
33 if(RandomInteger(0, 10000) &gt; 9700) { objects.push(new Car("images/car_red.png", RandomInteger(30, canvas.width - 50), RandomInteger(250, 400) * -1)); }<p>Теперь можно увидеть, как новые машины появляются вверху экрана:</p>
33 if(RandomInteger(0, 10000) &gt; 9700) { objects.push(new Car("images/car_red.png", RandomInteger(30, canvas.width - 50), RandomInteger(250, 400) * -1)); }<p>Теперь можно увидеть, как новые машины появляются вверху экрана:</p>
34 <p>При столкновении пока ничего не происходит, но это будет исправлено позже. Сначала нужно убедиться, что объекты, которые пропали из виду, удаляются. Это нужно, чтобы не забивать оперативную память.</p>
34 <p>При столкновении пока ничего не происходит, но это будет исправлено позже. Сначала нужно убедиться, что объекты, которые пропали из виду, удаляются. Это нужно, чтобы не забивать оперативную память.</p>
35 <p>В класс<em><strong>Car</strong></em>добавляем поле<em>dead</em>со значением<em>false</em>, а потом меняем его в методе<em>Update()</em>:</p>
35 <p>В класс<em><strong>Car</strong></em>добавляем поле<em>dead</em>со значением<em>false</em>, а потом меняем его в методе<em>Update()</em>:</p>
36 if(this.y &gt; canvas.height + 50) { this.dead = true; }<p>Теперь нужно изменить функцию обновления игры, заменив там код, связанный с объектами:</p>
36 if(this.y &gt; canvas.height + 50) { this.dead = true; }<p>Теперь нужно изменить функцию обновления игры, заменив там код, связанный с объектами:</p>
37 var hasDead = false; for(var i = 0; i &lt; objects.length; i++) { if(i != player) { objects[i].Update(); if(objects[i].dead) { hasDead = true; } } } if(hasDead) { objects.shift(); }<p>Если не удалять объекты, то игра начнёт тормозить компьютер, когда будет сгенерировано слишком много машин.</p>
37 var hasDead = false; for(var i = 0; i &lt; objects.length; i++) { if(i != player) { objects[i].Update(); if(objects[i].dead) { hasDead = true; } } } if(hasDead) { objects.shift(); }<p>Если не удалять объекты, то игра начнёт тормозить компьютер, когда будет сгенерировано слишком много машин.</p>
38 <p>Теперь можно приступить к реализации коллизии<em>(англ. collision - столкновение).</em>Для это нужно написать для класса<em><strong>Car</strong></em>метод<em>Collide()</em>, в котором будут проверяться координаты машин:</p>
38 <p>Теперь можно приступить к реализации коллизии<em>(англ. collision - столкновение).</em>Для это нужно написать для класса<em><strong>Car</strong></em>метод<em>Collide()</em>, в котором будут проверяться координаты машин:</p>
39 Collide(car) { var hit = false; if(this.y &lt; car.y + car.image.height * scale &amp;&amp; this.y + this.image.height * scale &gt; car.y) //Если объекты находятся на одной линии по горизонтали { if(this.x + this.image.width * scale &gt; car.x &amp;&amp; this.x &lt; car.x + car.image.width * scale) //Если объекты находятся на одной линии по вертикали { hit = true; } } return hit; }<p>Теперь нужно в функцию<em>Update()</em>добавить проверку коллизии:</p>
39 Collide(car) { var hit = false; if(this.y &lt; car.y + car.image.height * scale &amp;&amp; this.y + this.image.height * scale &gt; car.y) //Если объекты находятся на одной линии по горизонтали { if(this.x + this.image.width * scale &gt; car.x &amp;&amp; this.x &lt; car.x + car.image.width * scale) //Если объекты находятся на одной линии по вертикали { hit = true; } } return hit; }<p>Теперь нужно в функцию<em>Update()</em>добавить проверку коллизии:</p>
40 var hit = false; for(var i = 0; i &lt; objects.length; i++) { if(i != player) { hit = objects[player].Collide(objects[i]); if(hit) { alert("Вы врезались!"); Stop(); break; } } }<p>Вот что будет в игре:</p>
40 var hit = false; for(var i = 0; i &lt; objects.length; i++) { if(i != player) { hit = objects[player].Collide(objects[i]); if(hit) { alert("Вы врезались!"); Stop(); break; } } }<p>Вот что будет в игре:</p>
41 <p>В момент коллизии можно добавить любую логику:</p>
41 <p>В момент коллизии можно добавить любую логику:</p>
42 <ul><li>включение анимации;</li>
42 <ul><li>включение анимации;</li>
43 <li>добавление эффекта;</li>
43 <li>добавление эффекта;</li>
44 <li>удаление объекта;</li>
44 <li>удаление объекта;</li>
45 <li>изменение здоровья и так далее.</li>
45 <li>изменение здоровья и так далее.</li>
46 </ul><p>Всё это остаётся на усмотрение разработчика.</p>
46 </ul><p>Всё это остаётся на усмотрение разработчика.</p>
47 <p>Получилась довольно простая игра, но этого вполне достаточно, чтобы разобраться, как в JS можно работать с графикой и как в целом создаются игры. Изображения и полный код игры можно найти в <a>репозитории на GitHub</a>.</p>
47 <p>Получилась довольно простая игра, но этого вполне достаточно, чтобы разобраться, как в JS можно работать с графикой и как в целом создаются игры. Изображения и полный код игры можно найти в <a>репозитории на GitHub</a>.</p>
48 <p>Использование canvas хорошо подходит для работы с графикой: он даёт большие возможности и не сильно загружает браузер. Также сейчас разработчикам доступна библиотека WebGL (<a>примеры</a>и <a>использование</a>), с помощью которой можно значительно повысить производительность и работать с 3D (canvas так не может).</p>
48 <p>Использование canvas хорошо подходит для работы с графикой: он даёт большие возможности и не сильно загружает браузер. Также сейчас разработчикам доступна библиотека WebGL (<a>примеры</a>и <a>использование</a>), с помощью которой можно значительно повысить производительность и работать с 3D (canvas так не может).</p>
49 <p>Разобраться в WebGL может быть непросто - вероятно, вместо этого многим интереснее попробовать<a>движок Unity</a>, который умеет компилировать проекты для их запуска в браузере.</p>
49 <p>Разобраться в WebGL может быть непросто - вероятно, вместо этого многим интереснее попробовать<a>движок Unity</a>, который умеет компилировать проекты для их запуска в браузере.</p>
50 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>
50 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>