📜 ⬆️ ⬇️

Spacecraft Generator from Rebar

Good time on your side of the planet, Habr.
Today on Habré is the day of the spacecraft, there are so many interesting articles about the last battle in EVE Online, but in my free time I continue to do my two-dimensional space game and after a long break I took the generator of the ships. Let the ships and not as elegant as in EVE, but their own.



Who cares how to do such a render on the canvas, please under the cat.

Introduction


First you need to say why you need such a generator. There are several reasons:

What should be able to generator:

Now more about this vessel specification, which is visible on the render above. Technically, she describes exactly how the ships should be assembled and from what. From the point of view of backstory, this is something like GOST, which is invented to standardize the production of ships by commercial companies. From the point of view of gameplay, this is an attempt to make some kind of general design of ships, which will not allow you to create a tile editor (when ships are drawn in boxes). Of course, the overall design is rather arbitrary, because editing the config allows you to change a lot.
What are the ships based on this "GOST"? The basis is the line (line), the line is a set of sections (section), which are parallel to each other. A section is a component plus two blocks that hold several sections together in a line. A component can be a lot of things, it can be just fastenings that are needed only to increase the strength of a structure, not significantly increasing its mass, or a cargo block, or a control unit, or an engine, or a platform for installing weapons and so on.
I think I have already talked enough about what a ship generator is, now let's see how it actually generates these renders.
')

Step 0 - Preparing Canvases and Paints


First we need to configure three canvases, #backCanvs, #mainCanvas and #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> 

Here is the initialization function, it uses a very well-known trick, with setting the resolution of the canvas, more than its visual dimensions.

 //    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")); } } 


Step 0.5 - Cooking Tassels


Now you need to do the second, and most importantly - configs.
As I said before, the ship config itself is written in JSON format, which is then parsed in the Javascript Object, but there are still settings for the draftsman itself:

 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: [] } 

We set the ship config like this:

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

I give an example of a small config of the ship. First, an array of all lines is described (in this case there are three), the offset of the next line is indicated for each line, and the sections are listed, in this case there are two of them per line.
Hidden text
 [ { "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 } } } ] 


Everything is ready, we begin to write.

Step 1 - Write the stars on the first canvas


Not only the stars, but also a small nebula. This is done by two functions:

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

I see no reason to bring large pieces of code for drawing the background, who wants to look in the code, and the title of the article still has the substring "ships", so let's move on to them.
Nevertheless, we have already got something like this:



Step 2 - Writing a spacecraft on the second canvas


Here it is more interesting, we need to draw in each line, each section, and blocks the binding section. The main function looks like this, all with detailed comments:

 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); } } 


The selectComponent function draws the first block in the right place, then calls the paint function of the specific component, then draws the closing block:

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

Each component is drawn by its function, banal moveTo / LineTo / rect / fill / stroke. We simply represent how the component should look and call the functions on the required coordinates one by one. Here are some examples:
Pipe'y and blocks:



Engines:



Cargo blocks:



Well, and so on, you can do a lot of different components, which is what needs to be done, but so far three are enough for tests of ships.
The final result of the ship, with the config:
Hidden text
 [ { "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 } } } } ] 




Step 3 - Writing beautiful text on the third canvas


The penultimate stage remains, you need to decorate the characteristics of the ship in the form of a beautiful text. Well, at least a little beautiful.
And we do it very easily, just change the font size, and move gradually down the Y coordinate. We use the same data from the config about the author, the name of the design and others. Speaking of the author and the name of the design, I made them static so far, because they will be pulled out when integrating with the game, but for now let them be Unnamed and 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; } } 

We have already seen the result above; I cite the same for another config.
Hidden text
 [ { "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 } } } ] 




Step 4 - Opening the exhibition


The last stage, we connect FileSaver.js , and we write such a simple save, merging all the layers.
 $("#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"); }); }); 


Conclusion


For those who do not want to suffer with manual editing of configs, but just want to look at the ships, I bring here several configs that I did in the process of development. To look at them you need to click on the link “Edit config” below, paste into the selected config, and click “Apply” below.
Huge Six-Lineer
 [ { "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 } } } ] 




Cargo Ship
 [ { "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 } } } ] 




Small light boat
 [ { "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 } } } ] 




Also a big ship, but poorly made
 [ { "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 } } } ] 




All code is available as always on the githaba: github.com/MagistrAVSH/ship-gen You
can see the demo here: magistravsh.imtqy.com/ship-gen
Next time I hope to write about the editor’s integration with the game, I think it will be interesting.
Fly safe!

Source: https://habr.com/ru/post/210622/


All Articles