Procjam Summer 2018 has started so here I go. This will be a small project to create classic Pokemon style houses. (Guess what I might end up using it for)
This will be a two steps generator, the first thing to do is defining a logical model for the houses. We start by examining some examples to extract the structure and an initial set of variations:
Structure
- Blocks of 4 tiles
- Windows and walls can be placed at tiles, having one tile for padding at every side (available tiles = blocks – 1) * 2, this padding is used to project the shadows of the building.
- Doors can be placed at blocks, having one block for padding at every side.
- Distorted perspective, the “depth” of the building is represented as blocks of the ceiling.
- The last floor gets 2 less available tiles due to roof slant
Attributes:
- Structure
- Width: 6 blocks
- Height: 2 blocks
- Depth: 2 blocks
- Front
- 1st floor
- Windows: Tiles 1, 2, 3, 6, 7, 8, 9 and 10
- A door at block 3
- Wooden wall (1 to 10)
- 2nd floor
- Windows: Tiles 2, 3, 4, 7, 8 and 9
- Wooden wall (1 to 3)
- Brick wall (4 to 10)
- 1st floor
- Roof
- Slanted
- Tiled
Next example:
Front
- 2nd floor
- Bar border
Next Example:
Structure: The last floor gets all available tiles if the roof is flat
- Front, First floor
- Poke sign at block 3
- Roof
- Flat
Let’s confirm our model covers all wanted cases
- Structure
- Width: 8 blocks
- Height: 6 blocks
- Depth: 2 blocks
- Front
- 1st floor
- Windows: Tiles 6, 7, 10, 11
- Doors at blocks 2 and 4
- Mart sign at block 6
- Brick wall
- 2nd floor (…)
- 1st floor
- Roof
- Flat
- Tiled
Having this, we can start thinking on our higher level generator, its input will be the type of building and its size. Following are the types of buildings we’ll support: House, Gym, Mart. As for the sizes, they will be: Tiny, Small, Large, Huge.
The output of the high-level generator is a logical model of the building, which is used by the lower-level generator to produce a map of tiles. An example of the 1st output for a Large House would be:
{ width: 4, height: 2, depth: 2, floors: [ { wall: 'wood', windows: [4, 5], doors: [1] }, { wall: 'wood', windows: [2, 4, 5], border: true } ], roof: { type: 'slanted', material: 'tiles' } }
Let’sa go! We fire subl and produce this:
const Random = { chance(c) { return this.range(0, 100) <= c; }, range(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } }; const SIZE_RANGES = { tiny: { minw: 3, maxw: 4, minh: 1, maxh: 1, mind: 1, maxd: 1 }, small: { minw: 4, maxw: 5, minh: 1, maxh: 2, mind: 1, maxd: 1 }, large: { minw: 5, maxw: 6, minh: 2, maxh: 3, mind: 1, maxd: 2 }, huge: { minw: 5, maxw: 8, minh: 3, maxh: 5, mind: 2, maxd: 4 } }; const SLANTED_ROOF_CHANCES = { house: 90, gym: 20, mart: 10 }; const WINDOW_CHANCES = { house: 50, gym: 20, mart: 50 }; // https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array function shuffle(array) { var currentIndex = array.length, temporaryValue, randomIndex; // While there remain elements to shuffle... while (0 !== currentIndex) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; // And swap it with the current element. temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } const generator = { generate(type, size) { const model = { width: 0, height: 0, depth: 0, floors: [], roof: {}, type, size } // Size of the building const sizeRanges = SIZE_RANGES[size]; model.width = Random.range(sizeRanges.minw, sizeRanges.maxw); model.height = Random.range(sizeRanges.minh, sizeRanges.maxh); model.depth = Random.range(sizeRanges.mind, sizeRanges.maxd); // Type of roof model.roof.type = Random.chance(SLANTED_ROOF_CHANCES[type]) ? 'slanted' : 'flat'; for (let i = 0; i < model.height; i++) { model.floors[i] = this.generateFloor(model, i); } return model; }, generateFloor(model, floorNumber) { const floorModel = { wall: '', windows: [], features: [] } const useableBlocks = []; for (let i = 0; i < model.width - 2; i++){ useableBlocks[i] = true; } const useableTiles = []; for (let i = 0; i { if (!t) { return; } if (Random.chance(WINDOW_CHANCES[model.type])) { floorModel.windows.push(i); } }); return floorModel; }, getRandomSpace(array) { const availables = array.find(x => x === true); if (!availables) { return undefined; } const spaces = array.map((x, i) => ({x, i})).filter(x => x.x === true); return shuffle(spaces)[0].i; } } const model = generator.generate('mart', 'huge'); console.log(JSON.stringify(model));
Running it with nodejs gives us a variety of models already:
{ "width":6, "height":2, "depth":2, "floors":[ { "wall":"", "windows":[ 4, 7 ], "features":[ { "x":1, "type":"door" }, { "x":3, "type":"gymSign" } ] }, { "wall":"", "windows":[ 3, 9 ], "features":[ ] } ], "roof":{ "type":"flat" }, "type":"gym", "size":"large" }
{ "width":8, "height":5, "depth":3, "floors":[ { "wall":"", "windows":[ 0, 4, 5, 13 ], "features":[ { "x":3, "type":"door" }, { "x":0, "type":"martSign" } ] }, { "wall":"", "windows":[ 1, 3, 5, 6, 7, 9, 10, 11, 12, 13 ], "features":[ ] }, { "wall":"", "windows":[ 0, 1, 3, 4, 5, 7, 10, 12, 13 ], "features":[ ] }, { "wall":"", "windows":[ 0, 1, 5, 7, 8, 9, 11, 13 ], "features":[ ] }, { "wall":"", "windows":[ 0, 1, 4, 5, 8, 9 ], "features":[ ] } ], "roof":{ "type":"flat" }, "type":"mart", "size":"huge" }
Next step is using these models to create the maps of tiles.
One thought on “ProcJam Summer 2018 – Pokemon Building Generator – Part 1”