Генератор космических кораблей из арматуры

    Доброго времени на вашей стороне планеты, Хабр.
    Сегодня на хабре прямо день космических кораблей, столько интересных статей про последнюю битву в EVE Online, ну а я в свободное время я продолжаю делать свою двухмерную космическую игру и после длительного перерыва взялся за генератор кораблей. Пусть корабли и не такие шикарные как в EVE, зато свои.



    Кому интересно как такой рендер на канве сделать, прошу под кат.

    Вступление


    Для начала нужно сказать для чего нужен такой генератор. Причин несколько:
    • Спрайты кораблей нужны, а рисовать я толком не умею, но компьютер умеет
    • Очень хотелось сделать в игре пилотам возможность создавать свои чертежи кораблей, без однотипного набора кораблей на рынке
    • Я всегда смотрел на красивые концепты различной техники которые делают художники, и мне хотелось хотя бы чуть-чуть приблизится к такому уровню
    • Ну и это просто интересно

    Что должен уметь генератор:
    • На основе JSON данных(основанных на vessel specification, о которой далее) генерить картинку корабля, на прозрачном фоне, для вставку в игру
    • Генерить красивый рендер, который можно скачать, залить, и запостить на где-нибудь на форумах
    • Выдавать конфиг корабля, который также можно скопипастить на форумы, и другой человек может поглядеть что там за дарвинский пепелац сорудили =)

    Теперь поподробней о этой vessel specification, которая видна на рендере сверху. Технически она описывает как именно должны собираться корабли и из чего. С точки зрения бэкстори это что-то вроде ГОСТ'а, который придуман чтобы стандартизировать производство кораблей коммерческими компаниями. С точки зрения геймплея это попытка сделать какой-то общий дизайн кораблей, который не позволит создать тайловый редактор (когда по клеточкам рисуются корабли). Конечно общий дизайн это довольно условно, потому что редактирование конфига позволяет менять очень немало.
    Из чего же составляются корабли по этому «ГОСТ'у»? Основой являются линии(line), линия это набор секций(section), которые располагаются параллельно друг другу. Секция — это компонент плюс два блока, которые скрепляют несколько секций в линию. Компонент же может быть много чем, это может быть просто скрепления, которые нужны только для увеличения прочности конструкции, не значительно увеличивающие её массу, или грузовым блоком, или блоком управления, или двигателем, или платформой для установки вооружения и так далее.
    Думаю я уже достаточно рассказал о том, что такое генератор кораблей, давайте теперь посмотрим как же он собственно генерит эти рендеры.

    Шаг 0 — Готовим холсты и краски


    Для начала нам нужно настроить три канвы, #backCanvs, #mainCanvas и #topCanvas.

    <canvas id="backCanvas"  width="640" height="480"></canvas>	
    <canvas id="mainCanvas"  width="640" height="480"></canvas>	
    <canvas id="topCanvas"  width="640" height="480"></canvas>
    

    Вот функция инициализации, в ней использован очень известный трюк, с установкой разрешения канвы, больше чем её визуальные размеры.

    //Вызываем функцию инициализации слоев
    var canvasSize = { width: Math.floor(window.innerWidth*3.5),
                       height: Math.floor(window.innerHeight*3.5) };
    var cssSize = { width: window.innerWidth, height: window.innerHeight };
    
    shipGen.layerInit(["#backCanvas", "#mainCanvas", "#topCanvas"], canvasSize, cssSize);
    //Сама функция
    shipGen.layerInit = function(canvases, canvasSize, cssSize) {
    	shipGen.config.canvasSize = canvasSize;
    	shipGen.config.cssSize = cssSize;
        //Если слои есть, очищаем их и удаляем
    	for(var i in shipGen.layers) {
    		shipGen.layers[i].clearRect(0, 0, canvasSize.width, canvasSize.height)
    	}
    	shipGen.layers = [];
    	for(var i in canvases) {
    		shipGen.layers.push($(canvases[i]).attr("width", canvasSize.width)
    		                .attr("height", canvasSize.height)
    		                .css("width", cssSize.width)
    		                .css("height", cssSize.height).get(0).getContext("2d"));
    	}
    }
    


    Шаг 0.5 — Готовим кисточки


    Теперь нужно сделать второе, и самое важное — конфиги.
    Как я уже говорил сам конфиг корабля записываем в JSON формате, который потом парсится в Javascript Object, но ещё есть настройки самой рисовалки:

    lines: [], //Сюда помещаются распарсеный JSON-конфиг
    config: { //Настройки для отрисовки
        factor: 15, //Маштабирование
        factorRandLight: 3, //Нужно для распределения лампочек
        angle: 0, //Угол поворота корабля
        canvasSize: {}, //Размеры канвы
        designName: "", //Название
        authorName: "" //Автор
    },
    counters: { //Различные счетчики, которые подсчитываются в процессе отрисовки или рядом
        heightShip: 0, //Длина корабля
    	hull: 0, //Показатель прочности корпуса
    	linesCount: 0, //Сколько всего линий
    	sectionsCount: 0, //Сколько всего секций
    	totalMass: 0, //Суммарная масса корабля
    },
    data: { //Собираем данные по конкретным компонентам
    	engines: [],
    	blocks: [],
    	pipes: [],
    	cargos: []
    }
    

    Конфиг корабля мы задаем так:

    try {
      if(localStorage["config"]) {
        shipGen.lines = JSON.parse(localStorage["config"]); 
      }
      else {
        shipGen.lines = JSON.parse($("#text").val()); 
      }
      
    }
    catch(e) {
      alert("Error parse config");
    }
    

    Привожу в пример небольшой конфг конфига корабля. Сначала описывается массив всех линий(в данном случае их три), для каждой линии указывается смещение следующей линии, и перечисляются секции, в данном случае их по две на линию.
    Скрытый текст
    [
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 8,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 10,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 8,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        }
    ]


    Всё готово, начинаем писать.

    Шаг 1 — Пишем звезды на первом холсте


    Не только звезды, но и небольшую туманность. Это делается двумя функциями:

    shipGen.render.nebula(Math.random(), canvasSize.width, canvasSize.height);
    shipGen.render.stars(Math.random(), canvasSize.width, canvasSize.height);
    

    Не вижу смысла приводить большие куски кода рисования фона, кто хочет может посмотреть в коде, а заголовок статьи всё-таки имеет подстроку «корабли», вот к ним и перейдем.
    Но тем не менее у нас уже получилось что-то такое:



    Шаг 2 — Пишем космический аппарат на втором холсте


    Здесь уже интересней, нам нужно отрисовать в каждой линии, каждую секцию, и блоки скрепляющий секции. Основная функция выглядит так, всё с подробными комментариями:

    shipGen.process = function() {
    	//Берем #mainCanvas
    	var ctx = shipGen.layers[1];
    	//Смещаем и поворачиваем контекст
    	ctx.translate(shipGen.config.canvasSize.width/2, shipGen.config.canvasSize.height/2);
    	ctx.rotate(shipGen.config.angle);
    	//Записываем количество линий
    	shipGen.counters.linesCount = shipGen.lines.length;
    	//Вспомогательная переменная, отмеряющая насколько мы сместились по X координате
    	//в процессе отрисовки линий
    	var lenShiftX = 0;
    	for (var i = 0; i < shipGen.counters.linesCount; i++) {
    
    		ctx.save();
    		//Берем данные о секциях
    		var sections = shipGen.lines[i].sections;
    		//Крутим цикл по каждому компоненту секции
    		for(var component in sections) {
    			//В процессе подсчитываем суммарное количество секций в кораблей
    	  		shipGen.counters.sectionsCount += 1  
    	  		//Берем набор параметров компонента, добавляем к нему некоторые служебные данные и вызываем отрисовку компонента
    	  		shipGen.selectComponent(sections[component], {lenShiftX: lenShiftX, blockTopW: sections[component].blockW2, blockBottomW: sections[component].blockW1 } );
    		}
    		ctx.restore();
    		lenShiftX += shipGen.lines[i].nextLineX;
    		//Смещаем отрисовку вправо, для того чтобы рядом нарисовать следующую паралелльную линию
    		ctx.translate(shipGen.lines[i].nextLineX, 0);
    	}
    }


    Функцией selectComponent отрисовываем в нужном месте сначала первый блок, потом вызываем функцию рисования конкретного компонента, потом рисуем закрывающий блок:

    ...
    shipGen.block(obj.blockW1, obj.blockH1);
    ...
    shipGen.components[obj.name](obj);
    ...
    shipGen.block(obj.blockW1, obj.blockH1);
    ...
    

    Каждый компонент рисуется своей функцией, банальными moveTo/LineTo/rect/fill/stroke. Просто представляем как должен выглядеть компонент и последовательно вызываем функции по нужным координатам. Вот несколько примеров:
    Pipe'ы и блоки:



    Двигатели:



    Грузовые блоки:



    Ну и так далее, можно сделать много разных компонентов, что и нужно сделать, но пока для тестов нового формата кораблей достаточно и трех.
    Финальный результат корабля, с конфигом:
    Скрытый текст
    [
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "4": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              },
              "4": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "4": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                }
            }
        }
    ]
    




    Шаг 3 — Пишем красивый текст на третьем холсте


    Остался предпоследний этап, нужно офоромить характеристики корабля в виде красивого текста. Ну хотя бы чуть-чуть красивого.
    И делаем это очень легко, просто меняем размеры шрифта, и смещаемся постепенно вниз по Y координате. Как раз таки используем данные из конфига об авторе, названии дизайна и другие. Кстати об авторе и названии дизайна, я сделал пока их статичными, потому что они будут вытащены при интеграции с игрой, а пока пусть будут Unnamed и Unknown.
    //Вызов рисования текста
    shipGen.render.text();
    //Сама функция
    shipGen.render.text = function() {
        var ctx = shipGen.layers[2];
        //Номер дизайна
        ctx.fillStyle = "#fff";
        ctx.strokeStyle = "#fff";
        ctx.font = "130pt Arial";
        ctx.fillText("Vessel Design " + shipGen.config.number, 220, 220);
        //Название формата
        ctx.font = "50pt Arial";
        ctx.fillText("Vessel specification of Tranquilla Community VSC-V3", 250, 320); 
        
        ctx.font = "40pt Arial";
        //Название дизайна
        ctx.fillText("Design name: " + shipGen.config.designName, 250, 420); 
        //Имя автора дизайна
        ctx.fillText("Author name: " + shipGen.config.authorName, 250, 520); 
        //Где мы делали этот дизайн
        ctx.fillText("Place: Tranq One IV Station 41 (1020; 1210)", 250, 620); 
    
        ctx.font = "35pt Arial";
        //Основные характеристики
        ctx.fillText("Mass: " + shipGen.counters.totalMass, 250, 750); 
        ctx.fillText("Hull: " + shipGen.counters.hull, 250, 800); 
        ctx.fillText("Lines count: " + shipGen.counters.linesCount , 250, 850); 
        ctx.fillText("Sections count: " + shipGen.counters.sectionsCount, 250, 900); 
        ctx.fillText("Block count: " + shipGen.data.blocks.length, 250, 950); 
        ctx.fillText("Pipe count: " + shipGen.data.pipes.length, 250, 1000);
    
        var lineY = 1150;
        //Характеристики компонентов
        for(var i in shipGen.data) {
          if(i == "blocks" || i == "pipes") continue;
          ctx.fillText(i.toString() + ":", 250, lineY - 50); 
          for (var v in shipGen.data[i]) {
            ctx.fillText(i.toString() + ": " + JSON.stringify(shipGen.data[i][v], "", 1).replace(/\"/g, ''), 250, lineY); 
            lineY += 50;
          }
          lineY+=100;
        }
    }
    

    Результат мы уже видели выше, привожу тоже самое для другого конфига.
    Скрытый текст
    [   {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 40,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 40,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
           {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 40,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        }
    ]
    




    Шаг 4 — Открываем выставку


    Последний этап, подключаем FileSaver.js, и пишем такую простую сохранялку, объединяя все слои.
    $("#save-render").click(function(){
        var canvasMerge = $("<canvas width='"+canvasSize.width+"' height='"+canvasSize.height+"'></canvas>").get(0);
        var ctxMerge = canvasMerge.getContext("2d");
    
        ctxMerge.fillStyle = "#000";
        ctxMerge.fillRect(0, 0, canvasSize.width, canvasSize.height);
        ctxMerge.drawImage($("#backCanvas").get(0), 0, 0);
        ctxMerge.drawImage($("#mainCanvas").get(0), 0, 0);
        ctxMerge.drawImage($("#topCanvas").get(0), 0, 0);
        canvasMerge.toBlob(function(blob) {
          saveAs(blob, "render.png");
        });
    });
    


    Заключение


    Для тех кто не хочет мучатся с ручным редактированием конфигов, а просто хочет посмотреть на кораблики, я здесь привожу несколько конфигов, которые я делал в процессе рарзработки. Чтобы поглядеть на них нужно нажать внизу на ссылку «Edit config», вставить в выбранный конфиг, и нажать также внизу «Apply».
    Громадная шестилинейка
    [   {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 40,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 40,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
           {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 40,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        }
    ]
    	




    Грузовой корабль
    [
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              },
              "4": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "4": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              },
              "4": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "4": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                }
            }
        },
            {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              },
              "4": {
                  "name": "pipe",
                  "blockW1": 10,
                  "blockH1": 10,
                  "blockW2": 10,
                  "blockH2": 10,
                  "width": 8,
                  "height": 80,
                  "color": {"r":20,"g":20,"b":20},
                  "shift": 0, 
                  "step": 35
              }
            }
        }
    ]
    




    Небольшой легкий кораблик
    [
        {
            "nextLineX": 100,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 8,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 100,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 10,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 100,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 10,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 8,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        }
    ]
    




    Тоже большой корабль, но плохо сделанный
    [
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "cargo",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "2": {
                    "name": "cargo",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "3": {
                    "name": "cargo",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                },
                "4": {
                    "name": "cargo",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 35,
                    "height": 35,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    }
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 15,
                    "height": 70,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 40,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 15,
                    "height": 70,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 15,
                    "height": 70,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 30,
                    "blockH2": 40,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 90,
                    "width": 15,
                    "height": 30,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        },
        {
            "nextLineX": 200,
            "sections": {
                "0": {
                    "name": "engine",
                    "blockW1": 0,
                    "blockH1": 0,
                    "blockW2": 20,
                    "blockH2": 10,
                    "width": 12,
                    "height": 8,
                    "color": {
                        "r": 25,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "widthLeft": 8,
                    "widthRight": 8
                },
                "1": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 50,
                    "blockH2": 60,
                    "width": 15,
                    "height": 70,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "2": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "3": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 10,
                    "blockH2": 10,
                    "width": 15,
                    "height": 40,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                },
                "4": {
                    "name": "pipe",
                    "blockW1": 40,
                    "blockH1": 10,
                    "blockW2": 40,
                    "blockH2": 10,
                    "width": 15,
                    "height": 80,
                    "color": {
                        "r": 20,
                        "g": 20,
                        "b": 20
                    },
                    "shift": 0,
                    "step": 35
                }
            }
        }
    ]
    




    Весь код доступен как всегда на гитхабе: github.com/MagistrAVSH/ship-gen
    Демо можно увидеть здесь: magistravsh.github.io/ship-gen
    В следующий раз надеюсь уже написать об интеграции редактора с игрой, думаю это будет интересно.
    Fly safe!

    Средняя зарплата в IT

    113 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 10 037 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 25

      +4
      Напоминает постройку космических кораблей из Civilization первой :-)
      Скрытый текст
      image
        0
        Да, и правда похоже :)
        +15
        Только я вижу на картинках одни лишь белые точки на чёрном фоне?
          –1
          Ещё раз просмотрел все картинки, все видно. Например на первую взглянуть — там вообще отлично видно.
            +4
            Наверное, у вас на мониторе яркость выкручена на максимум.
              0
              Да нет, специально сейчас поглядел. Может просто уже привык, сейчас что-нибудь придумаю.
                +2
                Посмотрел гистограмму изображения — у вас вся информация на картинке сосредоточена в 10% около чёрной части. Так что картинки реально почти чёрные. Раз вам всё прекрасно видно, предполагаю, что вы сидите в тёмной комнате с тёмной неконтрастной цветовой схемой.
                  +1
                  Вполне возможно, я сейчас сделал пару исправлений, стало вроде получше, куда получше:
                  Было


                  Стало

                    +14
                    Ох тыж… вот оно как должно выглядеть
                    image
                      +3
                      По моему, нужно хотя бы так.
                      Картинка


                      Корабль всё ещё тёмный, но все детали чётко видно.
                        +1
                        Яркость нужно выкрутить на максимум — только тогда видно корабль, а не белые точки.
                          +1
                          Всё равно почти ничего не разобрать.
                          У меня 2 экрана, на IPS ещё более-менее что-то различимо, а на стареньком TFT ничего не видно, кроме слабых красных оттенков.
                            0
                            Вот так всегда, раз пять проверишь всё перед тем как нажать «Опубликовать», все равно вылезет что-нибудь, чего не ожидаешь.
                            Ещё раз выкрутил цвета ярче, обновил гитхаб, в FF 26 проверил, одних только точек не увидел.
                              +4
                              Дело не в FF, дело в настройках вашего монитора или калибровке экрана в ОС.
                              Если хотите исправить положение, номрализуйте изображения по гистограммам в автоматическом режиме. Тогда у них будет нормальная яркость независимо от ваших настроек экрана.
                              +1
                              Странно, но «засериватель» коммента делает картинку куда более видимой.
                        +2
                        Попробуйте:

                        Скрытый текст
                      +2
                      Вы могли бы повысить яркость/контрастность результирующих изображений? С трудом их разобрал
                        +9
                        Интересный подход.
                        Относительно недавно тоже занимался постройкой подобных кораблей на связке JS+WebGL+NodeWebKit, хотя и в несколько ином ключе:
                        Видео


                        Описание и ссылки для скачивания или редактирования онлайн.
                          +1
                          Все кто использует Firefox видят только белые точки без кораблей, в остальных браузерах более менее можно хоть что-то различить.
                            +2
                            Скорее всего FireFox здесь ни при чём. Картинка реально тёмная, достаточно посмотреть на гистограмму. У меня на 3х разных браузерах картинка одинаковая, а вот на разных мониторах она немного отличается.
                            +2
                            А что за известный трюк с установкой разрешения канвы, больше чем её визуальные размеры? Для чего это? Только что бы вместить больше точек?
                              0
                              На самом деле этот трюк звучит несколько иначе, и предназначен для увеличения производительности, и наоборот cssSize > canvasSize, т.е. стилями задаем большие размеры, чем размеры канвы. У меня же наоборот, специально для того чтобы получить картинку с большим разрешением, при тех же визуальных размерах.
                              Вот здесь описано более подробно habrahabr.ru/company/microsoft/blog/141482/
                              На деле большое разрешение не всегда и нужно, но это уже надо подбирать под параметры игры, какие визуальные размеры кораблей будут там.
                              +1
                              > Генератор космических кораблей из арматуры

                              А что, матарские вон так и построены, из ржавой арматуры и обрезков трубопровода, и ничо, летают.
                                0
                                Ого, прикольные картинки для настройки яркости. Крутить нужно пока не появится силуэт?

                                Вам нужно не корабли моделировать, а уровни для angry birds. (не оскорбление) Если сделаете генератор случайного уровня, думаю вашей наработкой очень заинтересуются.
                                  0
                                  Генерить уровни для Angry Birds скучно как-то, банально слишком. Ну а с освещением ошибся конечно серьезно, не думал что такая проблема возникнет. В общем как я говорил, сколько не проверяй все перед публикацией, все равно что-нибудь да вылезет.
                                  А генератор мне все равно нравится, освещение подкрутил благодаря вашей критике, надо вкрутить в игру и смотреть как будет работать.

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое