FormulaProc – ProcJam

I’m creating a procedurally generating Formula One-style racing show! Check it out at https://formulaproc.com, here’s the story so far.

The Story So Far

After many years of wanting to, I jumped into ProcJam for 2021. There was always a reason not to do it, since it’s in December and I’ve commonly run out of jam-power by the time it happens. That was no exception this time, worsened by the fact that it happened almost back to back with the work I did for the Cultural Heritage Game Jam with Muyscamuy.

However, I decided to get off my head this idea that had been haunting me for years of making a spectator-sport kind of thing powered by procgen; not a game, but a kind of show around which people could build stories and create fandoms. It has had different shapes in my brain: soccer, cycling… but my renewed interest in Formula One this year made me think on a good balance between the level of abstraction vs. the amount of excitement that could be provided visually with such a simulation.

The marathon “Special Event” of Gold Medal Challenge’92 for the NES, another possible source of inspiration.

I had been talking for some weeks with Simernio and Gecko about this idea, and while we plan to have something more structured and elaborate in the future, I figured it could be a good idea to put the idea to the test at a smaller scale.

A clear inspiration was the F1 Visualized channel: “Formula 1 results and stats visualized using pixel art. Rewatch the latest races in minutes, without missing any part of it. Keep up with any of the drivers to see the race unfold from their perspective.“.

However, their visualization is much higher level (understandably so, since their idea is to compress a long F1 race into something you can watch much quicker), I wanted to create a somewhat abstract visualization of a race, not seeking to be a representation of the race itself but rather of how the race unfolded, leaving the players to fill the gap in usual roguelike fashion.

The main work in the implementation of the simulation happened from December 6 to 12 (when the jam ended). The jam encourages post-jam work for people not to stress (ha!), but we all know the entire point of a jam is to generate stress so that you produce something complete by a fixed end date, so I ignored that and pushed forward to have something solid by the end of it.

But wait, is this procgen?

I figure this may not be considered a “normal” procedural content generator; for procjam you’d more often expect something that creates things such as maps, level layouts, buildings, worlds, or maybe even something that awkwardly tries to come up with a written narrative or story.

What am I creating here? my intent is to create some facts, events that happened, and around which you can build stories and maybe even a community. And the main tools I’m using for this are physics simulation and artificial intelligence (both at a very, very simple level)

I shared the question in the procjam discord: can something like this be consider procedural generation? There was an interesting discussion about it, in general most people agreed the complexity or level of detail of the algorithm used to generate the content shouldn’t affect whether something is procgen or not.

“PCG is the algorithmic creation of content with limited or indirect user input”

from “Procedural Content Generation in Games”

As always, nothing is completely defined in the field of procedural content generation; but I would say that as long as you are generating content relying on algorithms and there is (pseudo) randomness involved (and you can be genuinely surprised by the results as the author), it IS procgen, and don’t let them tell you otherwise.

How it works

At the core of everything is an abstraction of the circuit based on its corners and the speed needed to transverse them safely; this is derived from the telemetry data of the drivers of recent races and is a bit loose because I’m not looking for a super accurate simulation but rather something that feels good enough.

The circuits are one-dimensional, and the racing is simulated based on both the stats of the cars affecting their acceleration, braking deceleration, gearbox efficiency, max speed, and the drivers having different reaction times, gearbox proficiency, and profile risk. There is a qualifying mode where the cars don’t interact and it’s all up to the performance of the driver and the car, and a race mode that adds more variables into the mix with the interaction between the drivers.

In order to provide the illusion of speed, some bars are placed on the track every 300m. In addition to this, an important aspect of the theming of each circuit is the name of the curves/corners, which are also placed as bars and provide visual feedback of the places where the cars brake.

I also added a kind of “minimap”, scaled-out version of the track, to make it easier to have a glimpse at the status of the entire race especially when the lead cars were too quick and left slower cars behind.

FormulaProc.com

The jam was just the start… while I delivered a complete simulation that could be parameterized with track data and would provide different results every time, I decided to have a single so-called “exhibition race”, happening in the Spa-Francorchamps circuit, be the published “results” of the jam.

But my plan was to build a complete series of this. I thought it would be cool to have the fantasy races happen on par with the real Formula One races, but the F1 2021 season had just finished (Go Max!), so I decided to start with the rebuild of the 2021 season, incrementally building the simulation quality along with the visuals and stuff based on the feedback of a hopefully growing community.

I set up a YouTube channel, a WordPress website, and a Twitter account for it, and started recording some more videos. So far I’ve done 4 races and posted about them in the official newssite. It still hasn’t had a lot of reach but that’s fine because every video is sligthly better than the last one, as I add new stuff to make the races more exciting!

The Portugal race, for instance, added non-critical accidents, allowing for much more varied stories that still felt believable, and the next race will feature some awesome soundworks by QuietGecko.

There is still a long way to go to make this feel more engaging; one of the things I’m already experimenting with is giving the drivers some visual shape so that people can relate to them; Simernio had the idea of using Unreal’s Metahuman Creator which has already yield some interesting results; for now we are just testing but it might be a good idea to make full use of it and render the characters in a fully controlled environment, as well as including more appropriate racing attires.

Biskup T., driver for the Queenscape team in 2021

As always, I’ll keep working on this until I run out of steam… hopefully when we catch up with the F1 2022 season we’ll have something more engaging and fun! in the meantime make sure to follow and subscribe 🙂

Backpack Monsters – js13k 2019

Js13kGames is a JavaScript coding competition for HTML5 Game Developers. The fun part of the compo is the file size limit set to 13 kilobytes. The competition started on 13th August and ended on 13th September 2019. The theme for 2019 was back.

I managed to participate in the competition for the third time in a row, this year I created a monster-catching game. You can play online here or here. Source code is here.

image (3)

The game itself is pretty simple, you walk around the map using the cardinal directions, and when you land on a spot you may find a monster and catch it. Both moving and attempting to catch a monster will consume your action points, which replenish automatically every 20 seconds to a maximum of 40. It works in both mobile and desktop browsers.

The organizers were encouraging the participants to integrate with coil.com and XRP tip bot to monetize their entries, I implemented a simple bonus for coil subscribers (they get a 25% bonus on the speed for action points to recover). So far I have received 1.08 XBR (That’s Ripple cryptocurrency,  converting to about 0.3 USD today)

The Story

What was my motivation this year? I was really hesitant to participate since I’ve been busy with other projects and just participating for the sake of it was not enough of a reason. Certainly, after last year’s results, I didn’t care much about “winning” the contest.

So, in the end my motivation ended up being merely to create an interesting procedural generator, to hone my procgen skills. I had just tried to participate in “Advanced Topics in Procedural Content Generation” online summer school provided by the IT University of Copenhagen and New York University, where I was able to watch some talks and gain some insights, but failed to participate in the course project.

The weeks before the challenge started I had an idea for a procedurally generated sports event (that thing has been in my head for some time now), but it was not very compatible with the theme they announced (“Back”). For most of the month, I couldn’t come up with something fun and interesting, that only managed to happen in the last week. So I started late and only managed to invest about 5 days on it, but the last days were super intense.

Being a pokemon fan (and a somewhat proud genwunner), I always wanted to create a monster-catching game (well, I kind of already did). Some years ago I was even working on a Pokemon Go-like game, but it never lifted off due to missing some original designs for them monsters.

So, I thought I could take another stab at a similar project, using procgen to fill the gap. I started by analyzing some pokemon anatomies and coming up with their structure and even used tracery.io to have an idea of how this “taxonomy” was looking (I had wanted to use it for something, for years :P). Sadly I didn’t include these descriptions in my final entry. (then again, I dunno how big the JS lib for tracery is)

js13k0

js13k1

Then, I started setting up the project, reusing the structure from my previous entries. In keeping my idea of NOT using pixel art on new projects, I thought of reusing the vector rendering routines I had used in these previous entries, however, the shapes I was planning to use were going to be more complex (I was initially thinking on trying to replicate the look of the original pokemon, like this one), so instead of just coding them by hand I used some online SVG editors and then manually translated the SVG instructions into my rendering routine.

However, as I needed to produce more complex shapes (mainly supporting bezier curves), I found out it would be more straightforward to generate SVG objects rather than extending the “vector script” I had in place, which by coincidence was pretty similar already to the SVG path definitions. This became more important as I looked for ways to get an artist to help me with content for the game since I would have had to translate SVG into my own script language, either manually or automatically.

So I ditched rendering paths and shapes into the canvas and instead started experimenting with generating SVG components from code and overlaying them using CSS.

js13k5

As the deadline drew closer, it became obvious that I would not be able to replicate the appearance of the reference I had for the monsters in a convincing way. I looked around a lot and finally found something in Etsy that I could use as a reference for my generator. (sadly, the store and the item itself seem to be gone now. Good timing for me tho!)

unnamed

These designs had several advantages:

  • They were portrayed as a symmetrical front view
  • They were more “symbolic”, without relying on individual poses for personality.
  • They were “inspired” by the original Pokemon designs, so not a direct knock-off.
  • They were already provided in SVG format.

I bought it, thinking I could at least cut and paste the different body parts and use them as individual SVGs, but my skills with both Illustrator and Inkscape proved not to be enough for it. Luckily, js13k colleague Rybar came to the rescue and helped me with the task, probably having to redo a lot of SVG work following the references.

Now with the individual SVG assets provided by Ryan, I could finally advance in the actual generator, having, at last, some progress to show.

The generator works by picking a random “anatomy” which determines the body parts slots the monster will have, and their location. then it proceeds to select a random type of body part for each slot and positions them together using some anchor points.

It also picks a random “pastel” color for the monster, I did some research and found out an easy way to generate them was using the HSL model, and having both Saturation and Lighting at 70%, and picking a random Hue. Luckily, CSS supports defining colors with the HSLA notation so this was pretty easy to include.

I also build a simple, syllable-swapping based name generator for the monsters. It generates names using two to three syllables, picking from a random list of consonants, vowels and a possible third consonant, for each syllable.

It’s also worth noting that this generator is seeded, which means the “races” of monsters that are generated are always the same, and all players will get the same set! For this, I reused the simple (on implementation!) but effective Park-Miller LCG

Now that I was finally generating some content, I focused on creating an actual game around the generator. I knew from the onset the game would not be deep, but there had to be a game!

I created a world represented by a 10×10 grid of locations and added a simple name generator for them (reusing the monster name generator but also adding some flavor with location types and colors). Also added the energy system to move around and catch the monsters, to serve as a barrier for content consumption.

In the world model, I placed lists of monsters for each location, considering their “rarity” so that very rare monsters can only be found in a single location, and so on. The idea being that players could share their findings, creating something of a social aspect to the game (Hey, what monster are you missing? I found Lesense in the Temple of Serkekol!).

I added a simple model to keep track of the monsters you had captured, and a view where you could see them.

I had managed to have a playable game one day before the deadline, so I could focus on enhancing the UX and testing. Needless to say, this ended up consuming a lot of energies but the end result was very good.

I managed to include a music track (thanks again to Rybar for creating it!) and even modified it in the tracker to make it longer (first time I make something useful using a tracker). We used SoundBox again (here‘s the final version of the track)

sb

I also made the game public and asked around for suggestions to improve it, one of them was adding more variations to make the monsters feel more unique. I fired Inkscape and drew a lot of horns, antenna, mouths and some tails, and also defined an additional anatomy with no “body” (just a head with things attached to it). The structure that was in place made it very easy to add new content.

Also, in order to add some “depth” into their appearance, I was suggested to do some shading into the bodies, which I managed to do with the help of Prinfexita, a fellow indie dev from Colombia. She drew the shadow shapes and I added support for them into the generator (including producing a darker version of the body color)

I also did a LOT of tweaks on the user interface, to make navigation easier for the players and provide a cleaner experience, especially in mobile.

What went right

  • I was able to create a finished, stable and polished experience
  • I learned a bit more about SVG
  • I created something technically different than last year entries.
  • The monsters look cute.
  • I was able to collaborate again with Ryan Malm, for the first time with graphics and for the second time with music.

Inadvertently, I think I managed to do just what I set to do from last year’s postmortem:

  • An easy game, rewarding players without requiring lots of skill.
  • Gorgeous visuals. (Maybe??? :))

What went wrong

Nothing really went wrong, however, there are some core things I wish I had managed to include to make my entry more fun:

  • A scrolling map view of the world, since right now it relies a lot on the player imagination
  • Deeper gameplay mechanics for catching the monsters, maybe collecting some items in the world that would help you lure them.
  • Allow trading monsters with other players.
  • More body parts, especially Wings and tails
  • Scaling variations for the monster’s appearances.

But all in all, I’m very happy with what I was able to create in the short time frame.

However, I got pretty sick and weak for two days after the deadline, I believe I pushed too hard. Would be better to avoid crunch by starting earlier.

The Future

I never intended the game to be more than just a collecting game, so things like a plotline or a combat system were willingly ignored.

Some other things that would have been nice to have, as possible points of expansion:

  • A more in-deep generation of the monster anatomies beyond randomly selecting and swapping parts.
  • Generate monster descriptions and stories.

 

 

 

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