Expedition: June 8 Update

Procedural Terrain

procgen

  • Created heightmap using Perlin noise, and apply to Unity Terrain object
  • Add the first layer of textures to the terrain based on height boundaries (ocean floor, grassland, and mountain).
  • Make the boundaries between terrain types fuzzier by fading, dithering and adding noise to the height thresholds.
  • Add forests by seeding with trees on the grassland band, and then growing them.
  • Add support to load towns into the terrain

Music / SFX

  • Added battle overlay so when you go to Combat mode it transitions smoothly into a more active track.
  • Added ambiance background noise for land exploration.

Graphics

  • Better textures for the Muisca towns, more responsive to changes on lighting

Fatigue System

  • Changed sleeping hours prompt to use a slider
  • Check time for the “sunny” modifier (only add during the day)
  • Add notifications when finished resting.
  • Added a “resting” label to the HUD shown while the expedition is resting.

Camera

  • Use scrollwheel or +- to change the zoom level
  • Remove automatic battle zoom

ProcJam Summer 2018 – Pokemon Building Generator – Part 1

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:

Screen Shot 2018-06-20 at 3.23.33 PM

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)
  • Roof
    • Slanted
    • Tiled

Next example:

Screen Shot 2018-06-20 at 3.34.38 PM

Front

  • 2nd floor
    • Bar border

Next Example:

Screen Shot 2018-06-20 at 3.43.19 PM

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

Screen Shot 2018-06-20 at 3.52.51 PM

  • 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 (…)
  • 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:

Screen Shot 2018-06-20 at 3.34.38 PM

{
  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.

Entries for Reddit Procedural Generation Monthly Challenge # 2 (Castles)

I participated on /r/proceduralgeneration January challenge: Castle Generator; based on the structure of the castles from the classic Ultima V game, a first component generates the desired structure (what rooms the castle should have and what sizes they should be).

A second component takes this specification and tries to place the desired rooms in a 32×32 tiles space. (Again following design patterns derived from the original game). Some of the spaces are placed in a semi-fixed way (the towers, the halls connecting the towers, the central room and the entrance halls to the castle), the rest of rooms are placed in the remaining space and then are grown randomly to occupy as much space as possible. Once they can grow no more, additional rooms will be allocated to fill the remaining space.

The layout is then validated vs the specs, to see if all the required rooms can be placed (including preferences such as rooms to be placed at the top, and minimum size considerations). If the second component is unable to place the rooms required by the first component, generation fails and a new layout is generated by the first component.

The rooms will then be connected to the central room following a recursive flooding algorithm which scans the walls of each room looking for close rooms, and then picks a random spot in the wall to place a door.

Then comes the third component which is in charge of adding the contents to each room based on its type. The central feature can be a courtyard or a main hall and follows specific parameters for generation defining what kind of features should be placed in the center, what kind of paths connect with the other features including the terrain, and many other parameters. Throne Rooms place the throne at the top with optional features such as carpet, columns and torches leading to it.

The rest of the rooms are split into a number of vertical and horizontal spaces, which can be 2 or 3 tiles big depending on the room size. Then, according to the type of the room, smaller features such as beds, tables and other furniture, are selected and placed on these spaces. Some rooms like the living quarters or the dining room have special rules to place for example long tables and chairs by the tables. Torch lighting is also included randomly in most rooms.

Links:

Here are pics of the results of some of other generators submitted, take a look here for more info!

A new challenge has been proposed! I’m wondering if I’ll be in 🙂

castle5castle7castle8castle12castle11castle10castle9castle6castle4castle2castle1

castle3

castle13

castle14