Как-то для своих некоторых планов мне потребовалось сделать небольшую песочницу в 2D пространстве с базовыми возможностями:
1. Передвиж��ние по игровому миру
2. Физика при движении, столкновения
3. Создание блоков
4. Удаление блоков
Графическое исполнение меня не беспокоило, поэтому я решил оформить все в серых тонах, выглядит это так:

Для работы используется JavaScript, так как проект нужен для демонстрации, и затачивать его под платформы не было желания, да и подразумевался быстрый просмотр результата.
В качестве рендерера я использую PointJS.
Весь код песочницы я уместил в один файл, так как писать много и не подразумевалось.
Для этого я создал файл game.js, все исходные коды, появляющиеся в статье, находятся внутри этого файла и изложены по порядку. Итоговый вариант и живой запуск в конце статьи.
Первое, что требуется, инициализировать движок и вынести в переменные для быстрого доступа необходимые методы:
После инициализации нам потребуется создать единственный игровой цикл, внутри которого будет существовать наш игровой мир.
Внутри этого конструктора нам надо определить обязательный метод update:
Там, где указано // init, нам потребуется во внутренние переменные поместить игровой мир:
И самое главное — механика игрового цикла, и всё то, что заставит игровой мир «ожить»:
Это весь требуемый для игры код.
Результат: В браузере
Исходник: На GitHub
Для тех, кому удобнее и нагляднее смотреть видео-уроки, есть видео вариант:
1. Передвиж��ние по игровому миру
2. Физика при движении, столкновения
3. Создание блоков
4. Удаление блоков
Графическое исполнение меня не беспокоило, поэтому я решил оформить все в серых тонах, выглядит это так:

Подготовка
Для работы используется JavaScript, так как проект нужен для демонстрации, и затачивать его под платформы не было желания, да и подразумевался быстрый просмотр результата.
В качестве рендерера я использую PointJS.
Весь код песочницы я уместил в один файл, так как писать много и не подразумевалось.
Для этого я создал файл game.js, все исходные коды, появляющиеся в статье, находятся внутри этого файла и изложены по порядку. Итоговый вариант и живой запуск в конце статьи.
Первое, что требуется, инициализировать движок и вынести в переменные для быстрого доступа необходимые методы:
var pjs = new PointJS(640, 480, { // ширина, высота backgroundColor : '#4b4843' // цвет фона }); pjs.system.initFullPage(); // полнокранный режим (тут ширина и высота изменилась под экран) var game = pjs.game; // Менеджер игрового мира var point = pjs.vector.point; // Конструктор точки/вектора var camera = pjs.camera; // Управление камерой var brush = pjs.brush; // Методыпростого рисования var OOP = pjs.OOP; // Общие ООП методы var math = pjs.math; // Дополнительная математика var key = pjs.keyControl.initKeyControl(); // включим клавиатуру var mouse = pjs.mouseControl.initMouseControl(); // включим мышь var width = game.getWH().w; // возьмем ширину var height = game.getWH().h; // и высоту экрана в переменные var BW = 40, BH = 40; // размеры блоков, которые мы сможем создавать pjs.system.initFPSCheck(); // включить счетчик fps
После инициализации нам потребуется создать единственный игровой цикл, внутри которого будет существовать наш игровой мир.
game.newLoopFromConstructor('myGame', function () { // game loop });
Внутри этого конструктора нам надо определить обязательный метод update:
game.newLoopFromConstructor('myGame', function () { // init this.update = function () { // game loop } });
Там, где указано // init, нам потребуется во внутренние переменные поместить игровой мир:
var pPos; // переменная с первоначальной позицией нашего игрока var world = []; // массив с блоками, пока пуст // функцией ниже мы, на основе двумерного массива // восоздадим блоки в нужных позициях, опираться будем // на символы 0 и P, где первый - блок, второй - позиция игрока // сама же функция принимает ширину создаваемых объектов, // высоту, исходный массив, и функцию обработчик, которая срабатывает // на каждом символе. pjs.levels.forStringArray({w : BW, h : BH, source : [ '0000000000000000000000000000', '0000000000000000000000000000', '0000000000000000000000000000', '0000000000000000000000000000', '000000 00000000000000', '000 00000000000000', '000 P 000000000000000000', '000 000000000000000000', '0000000000000000000000000000', '0000000000000000000000000000' ]}, function (S, X, Y, W, H) { if (S === '0') { // если текущий символ равен нулю world.push(game.newRectObject({ // то мы создадим блок x : X, y : Y, // позицию укажем из параметров w : W, h : H, // ширину и высоту - тоже fillColor : '#bcbcbc' // цвет фона })); } else if (S === 'P') { // если же символ равен английской букве P pPos = point(X, Y); // то просто зафиксируем текущую позицию в переменную } }); // а теперь создадим самого персонажа, тоже обычный блок var pl = game.newRectObject({ x : pPos.x, y : pPos.y, // позицию берем из нашей переменной w : 30, h : 50, // ширина и высота fillColor : 'white' // цвет }); var speed = point(); // переменная со скоростями по осям Х и Y
И самое главное — механика игрового цикла, и всё то, что заставит игровой мир «ожить»:
this.update = function () { // управление с клавиатуры, с кнопками, думаю, понятно if (key.isDown('A')) speed.x = -2; else if (key.isDown('D')) speed.x = 2; else speed.x = 0; // если никакая клавиша не нажата, то обнуляем скорость // скорость движения по оси Y, если нажати W, то ставим -7 (движение вверх) if (speed.x < 5) speed.y += 0.5; // постоянно меняем скорость движения, имитируя гравитацию if (key.isPress('W')) speed.y = -7; // рисуем нашего персонажа pl.draw(); // распределим необъодимые массивы var collisionBlocks = []; // массив столкновений var drawBlocks = []; // массив для отрисовки var selBlocks = []; // массив выледенныех блоков // возьмем в переменные некоторые события var R = mouse.isPress('RIGHT'); // клик ЛКМ var L = mouse.isPress('LEFT'); // клик ПКМ var MP = mouse.getPosition(); // позиция мыши // позиция создаваемого блока // привяжем позицию мыши к сетке размером в один блок var createPos = point(BW * Math.floor(MP.x / BW), BH * Math.floor(MP.y / BH)); // теперь идем циклом по всем объектам OOP.forArr(world, function (w, idW) { // принимает массив и обработчик if (w.isInCameraStatic()) { // если блок в пределах видимости drawBlocks.push(w); // добавим его в массив для отрисовки // если блок близко к нашему игроку if (pl.getDistanceC(w.getPositionC()) < 80) { // то проверим, находится ли на нем мышь if (mouse.isInStatic(w.getStaticBox())) { selBlocks.push(w); // если да, то добавим его в массив выбранных блоков if (L) { // если левый клик случился, то world.splice(idW, 1); // удалим блок из массива } } } } }); // теперь пройдемся по тем блокам, что видно (которые попали в камерц) OOP.forArr(drawBlocks, function (d) { // изменяем прозрачность блока в зависимости от расстояния до игрока d.setAlpha(1 - pl.getDistanceC(d.getPositionC()) / 250); // получается, что, чем дальше блок - тем более он невидим // рисуем то, что получилось d.draw(); // если отрисованный блок близко к игроку, то добавим его к кандидатам // на столкновения if (pl.getDistanceC(d.getPositionC()) < 100) { collisionBlocks.push(d); } }); // теперь пробежимся по выделенным блокам OOP.forArr(selBlocks, function (s) { brush.drawRect({ // и просто отрисуем вокруг них x : s.x, y : s.y, w : s.w, h : s.h, strokeColor : '#ac5a5a', // красную рамку strokeWidth : 2 // шириной 2 пикселя }); }); // отключим временно возможность создавать блоки var canCreate = false; var dist = pl.getDistanceC(MP); // замерим дистанцию курсора до игрока if (!selBlocks.length && dist > 50 && dist < 100) { // если нет выбранных блоков canCreate = true; // вернем возможность создавать блоки brush.drawRect({ // и отрисуем прямоугольник так, где потенциальный x : createPos.x, y : createPos.y, // блок будет создан w : BW, h : BH, strokeColor : '#69ac5a', // зеленым strokeWidth : 2 }); } // при левом клике мыши и возможности создавать блоки if (L && canCreate) { world.push(game.newRectObject({ // создадим новый объект x : createPos.x, y : createPos.y, w : BW, h : BH, fillColor : '#e2e2e2' })); } // теперь физика столкновений, используя эту функцию, мы сможем // двигать наш объект pl (игрок) с скоростью speed, а в качестве // препятствий будет массив collisionBlocks pjs.vector.moveCollision(pl, collisionBlocks, speed); // камерой будем следить за нашим игроков camera.follow(pl, 10); // и отрисуем fps brush.drawTextS({ text : pjs.system.getFPS(), color : 'white', size : 50 }); };
Это весь требуемый для игры код.
Результат: В браузере
Исходник: На GitHub
Для тех, кому удобнее и нагляднее смотреть видео-уроки, есть видео вариант:
ВидеоУрок 2D песочница на JavaScript
