Postmortem: ArcherFire Duet of Aces – js13k 2018

ArcherFire: Duet of Aces is a game I made for the js13k 2018 contest, you can check it out here! (and the source code is here)

I decided to write one final piece of text about my second participation on the contest. I already described my journey in detail here and here, and also did a dissection of the source code, but I wanted to record some lessons learned which may come in handy for other people or myself in the future.

afdoa

The story

Whenever I participate in a game jam or contest, I ask myself what is my motivation:

  • Learn a new language or tool
  • Create something cool
  • Win the contest prize

This was the second time I participated in the contest; results from last year had been a bit disappointing (regarding the ranking given by the jurors), and I knew my development time this year was going to be very limited. Still, I aimed to rank high into the lists.

The theme for this year was OFFLINE, as mentioned in my first blog post, I started off with a vague idea of a space… thing… where you could collaborate with other players offline to advance through the game.

20180913_210742

On the first day of dev, I created some foundations work looking forward to having some kind of space sandbox thing with arcade elements (Similar to this entry, which ended up being one of the contest winners). However, given the constraints I had, I decided to follow my own advice from last year’s postmortem and create:

  • An arcade game bringing full action from the first second of gameplay
  • Complete music track

And that’s precisely what I ended up doing! Interestingly, and maybe because of what I set to do and my experience from last year, I didn’t have any issues with the 13KB limit so I didn’t have to cut around any corners. I incorporated the OFFLINE theme by allowing 2 players hot-seat offline play.

However, the results were even less encouraging this year. Despite receiving good feedback from the jurors, the game didn’t even end up in the top 100. (Last year it ended up #40 overall and ranked well in the Community and Social lists). Since this was the first year where some constraints were put on the prizes model (only top 100 entries would get prizes) that meant that effectively I lost the contest. There was a total of 270 entries, and mine ended up ranked #105.

Screen Shot 2018-10-18 at 6.35.57 PM

Unlike last year, this time I don’t really have some big factors I think led to this result. I contented myself in thinking that the jurying process is not perfect, and there may have been very good entries this year. Still, here’s what I think worked and didn’t work.

What went right

  • Seeded Procedural Generation: Managed to create an infinite, increasingly harder, top-down shooter level which was the same every time you played (So, in theory, the more you play the game the more likely you are to memorize the hazards and get farther)
  • Pseudo 3D Effect: Everyone loved how cool the ships looked when soaring around.
  • Straightforward action-packed: The game is pretty responsive and quick, with lots of action on screen. No need to explain anyone how to play because it’s obvious!
  • 2 players hot-seat mode: I couldn’t test this much because I have no friends, but I saw some people have fun and yell at each other afterward.
  • A solid, finished entry: No bugs, no feeling of being an “incomplete game”, it is what it is and it doesn’t break.
  • Music: One of the game highlights, and I didn’t do it myself 😀 it was all thanks to Ryan Malm. I just gave him a reference and some ideas, and he came up with the track. Thanks again!

What went wrong

  • Floaty Controls: Since my foundational work on a simple physics engine had support for acceleration, I stuck to the end with the idea of the ships being “hard to control” as a “challenging aspect” for the player. I knew it was frustrating, but I waged on the players trying harder to learn how to control it. In the end, it ended up being too hard and too different to the standard. A model where the ships had less inertia would have been much more playable and enjoyable. Maybe I need to start making easier games!
  • Balancing: Some people reported the game was a bit too hard. In part, this may be closely related to the controls, but also could have lessened the hit points of the enemies or made the difficulty curve less step.
  • Lack of theme: Yet another space shooter, no plot, maybe went too much into the arcade direction? So that aspect was pretty uninteresting. There were also too few types of enemies, mainly because of the time constraints I had.
  • Graphics too simple: I am not an artist, and the game had high-resolution vector art. Unlike last year where I could go around that by using shadows in the darkness, this year I went for colors and maybe it didn’t look too nice. One thing that could have worked would have been using WebGL filters to post-process the bare graphics, which has worked great for other games in the past.

The Future

This is not the end of the story for Duet of Aces… I plan to make it evolve into a more complete game, keeping the core design but adding better art, sound effects, more music, a complete plot and more importantly, adapting it to social media mobile platforms. More info soon!

Also, some things I want to do next year:

  • An easy game, rewarding players without requiring lots of skill.
  • Gorgeous visuals.

ArcherFire: Duet of Aces, js13k 2018 source code dissection

JS13k 2018 has gone by! this is the third of a series of posts detailing my journey, as well as the structure of the game I built. You can play it online here!

afdoa

Links to the full series: part1, part2, postmortem

The full source code of the game (github repo), uncompressed and unminified, is 43136 bytes for the single javascript file, plus 349 bytes for the html file. This includes a lot of comments and code that could be written in a more optimal way, which reflects that my biggest restriction this year was not the size, but the time I could invest in the project.

Screen Shot 2018-09-21 at 10.02.50 AM

Following is a summary of the 43136 bytes, which of course could be laid out in a much better, modular way, but were thrown together in a huge file for the sake of reducing overload from webpack for having multiple modules.

So, why do I think this may be relevant? Well, I believe this may be a bare minimum setup to have an arcade game up and running; understanding how these components work together to create the game experience might be helpful to create a more organized structure, without falling on the mistake of over-engineering (start small and simple!).

Also, having them all in a monolithic block creates an implicit graph of dependencies, where the more general purpose components are (generally) defined first, then other code uses them. Here’s a first stab at translating that to a component / class / object diagram.

Screen Shot 2018-09-21 at 10.50.36 AM

With each segment of the source code, I’m including a short description which may give you an idea of the types of components in play for the game, here we go!

  • Soundbox player (11879 bytes): Pasted from here, this is the code that interprets the song “script” and transforms it into a WAV stream.
  • Theme song (5148 bytes): Created by Ryan Malm, it’s exported from Soundbox online player.
  • SFXR player lib (2637 bytes): Minified form of the original source code here, this code allows playing SFXs based on a list of settings
  • Initialization function (466 bytes): Creation of the music player and the WAV stream from the song script.
  • Sound data for SFXR (484 bytes): 5 different sound effects represented by SFXR instructions (Explosion, Bullet, Missile, Super Explosion, Start Game Beep)
  • SFX playback functions (359 bytes): Preload Audio objects from SFXR sound data and allows easy playback.
  • pRNG functions (719 bytes): Functions to generate random numbers, including the seedable pRNG.
  • Keyboard input routine (533 bytes): Some hooks around keypress and keydown to allow polling for the state of a key or add a listener function.
  • Shape renderer (1765 bytes): Given a canvas rendering context, a set of “shape instructions”, a position and a scale value, translate the simplified instructions into Canvas 2D API operations.
  • Mob class (887 bytes): Model the basic things moving around in the world, every Mob has an appearance, speed, and position in the x and y axis. This class also includes functions to update the mob, destroy it and check collisions with other mobs, as well as the creation of explosions.
  • RAF function (401 bytes): Function that allows hooking other functions into an animation frame, doing the fundamental calculations for the elapsed time between frames which are used to (try to) keep things running at a constant speed regardless of where they are run.
  • Collision functions (177 bytes): Simple collision checks based on the Manhattan distance between two mobs and their size.
  • Game Loop (1952 bytes): Keeps tracks of different list of mobs, used for both updating them on each animation frame, as well as rendering them on screen. It also keeps tracks of the animation-based Timers
  • LCD-like number rendering (805 bytes): Allows drawing any number as an LCD-like display.
  • UI Rendering (1371 bytes): Depending on the current state of the game, might draw the loading message, or the title screen, or the HUD including the scores and the current wave, or the Game Over message.
  • Enemy Factory (2318 bytes): Contains the stats for the different kind of enemies, as well as function to create groups of them in a given initial setup. For example you can request the factory to create 5 mines in horizontal formation at y = 200
  • Player Ship class (2356 bytes): Extends Mob, has special checks on its update function to simulate inertia as well as handle the practical “3d” effect when moving around and keeping the player ships on-screen. Of course, it also includes functions to control the movement of the ship using the keyboard (the actual keys that are used are received as a parameter, which means there could potentially be more than 2 players on screen). Also allows firing missiles, and keeps track of a player’s score.
  • Enemy class (1208 bytes): Extends Mob, has a simple “reaction” model which allows it to potentially fire a bullet at the player each x milliseconds. The speed of the bullet on each axis is calculated using the arc-tangent between the target and the enemy. The target is selected from the list of players (the closest one)
  • Star class (304 bytes): Extends Mob refining the destroy class so that a new star is created at the top when a star is destroyed.
  • Planet class (366 bytes): Extends Mob defining a special render function which draws a circle filled with a gradient based on the 2 colors and rotation of the planet.
  • Explosion class (928 bytes): This class is similar to Mob, but explosions have no speed (just a position). It’s designed to progressively draw an explosion on screen, based on the “run time” and “size” parameters, the size and color of the circle to be drawn are determined by the “progress” of the animation (a value growing from 0 to 100, based on the accumulated render time and the total run time). When fading, it substracts from the drawn circle creating a growing “hole” inside of it.
  • Shape definitions (2487 bytes): A dictionary of lists of drawing instructions to be interpreted by the Shape Renderer, all Mobs have a reference to a shape definition, used in the game loop
  • Game Start function (683 bytes): Plays the theme song, creates the player ships and starts a timer for wave generation. Called by the title screen and the enter key handler (for restarting the game)
  • Enemy Wave Generation (1947 bytes): Several functions containing the logic to create a new “wave” of enemies. Levels are made of waves generated every 3 seconds, this component decides what enemies will be spawned on each wave, and under what behavior. Makes extensive use of the seeded pRNG, as well as the Enemy Factory. Also creates the planets.
  • Enter key handler (320 bytes): Used to control the game state from the title screen and game over status, into the “in game” status.
  • Game state machine control functions (628 bytes): Transitions into the different game states (title, game over and “in game”)

Coming up next, final postmortem and future of the game!

 

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.

Interaction++ 2018.4 and some indie dev marketing tips

Screen Shot 2018-04-28 at 8.18.24 PM

Interaction” is a video game development meetup. They organize conferences in Medellín (my hometown) where they invite local developers to share their experience with both fellow and wannabe developers.

20180428_100759

They are now an official Unity User Group, and they are planning to create bigger and better events. Interaction++ 2018.4 was the first of these events, where they had three talks instead of a single one as usual.

20180428_101541

The first talk, “Introduction to Indie Marketing”, was given by Jorge Castaño of Amazing Soul games. It was an ambitious talk and it went well over its time limit, but it was pretty interesting.

Its first topic, foundations of marketing, gave an overview of some elements involved in the marketing of the game along with some useful  tips:

  • Create a compelling synopsis/summary to convert visits into installs.
  • Have at hand your Unique Selling Points: What makes your game different. This may help change a first negative / neutral perception and make the player give a second chance to your game.
  • The biggest markets like Steam, Play Store and AppStore are extremely competitive and it’s extremely hard to be noticed, smaller stores can even provide help with marketing.
  • China is a very attractive market for mobile but the AppStore and Play Store are dead there. Tencent is the biggest store, but rules are different.
  • A clear target market is a must to create an effective marketing campaign.
  • Set a target to be in the top 200 of the store (it’s impossible to get to top 10 without +100.000USD daily marketing or a lot of luck)
  • A great target for a small company is 5000 USD / day for 1 month. It should cover the investment in the game.

They also dug into some of the tasks of a publisher:

  • Localization (not only language but also cultural) can drive a lot of installs. For example, Games in China must be properly localized, and including Dragons as enemies into the game may be perceived as offensive. For Japan, 80% of the players will only play the game if it’s in Japanese.
  • Customer Care: Retention, especially important for freemium games in mobile. Some publishers provide a  support team.
  • Acquisition: It’s only part of the publisher task. Invest money to get more players.
  • Marketing: Material such as trailers, icons, screenshots, they often have a specialized team.
  • QA / Testing
  • Monetization: Make the game make money.

They also had some recommendations for Indie Publishers:

  • Tiny Build
  • Indie Fund
  • Adult Swim Games
  • Devolver

Publishers will normally take 30% to 60% of revenue, and it’s currently hard for them to give money upfront.

Some advantages of using a Publisher:

  • Better distribution channels
  • They have useful contacts with the industry
  • Higher visibility
  • Cross promotion, they will leverage players in existing games.
  • They assume part of the risk of investment
  • Years of experience.

If you don’t have a publisher, you must cover all the above by yourself to a certain degree.

This includes creating a marketing campaign to generate interest of the game even before it is launched.

Some things that help here:

  • Create a plan for your campaign with weekly goals and channels to be used every week.
  • Use analytics to update your plan. What is working and what is not.
  • Make a list of the websites, streamers or youtubers you want to cover your game. Start with the smaller ones, that will eventually bring interest to the mid-sized and the bigger ones if you are lucky.
  • Create a presentation template to get in contact with writers

Sucess doesn’t depend on money alone, being aware of all the different tools at hand helps greatly.

20180428_113944

In the second talk the guys from Dreamhouse Studios shared their experiences building three VR products:

  • A training program for the Colombian Army FLIR program. (Infrared scanning)
  • A multiplayer tower defense Halloween game.
  • A training program for the local tram system

They mentioned their second game has provided them with some cash flow by renting it to be used in malls and events in the city. They shared some details of their development process.

20180428_123413

Finally, the third talk was about the upcoming optimizations to appear on Unity to leverage on its Entity Component System to increase its performance greatly. This, in order to allow Unity games to run in lower-end devices, as well as extending its capabilities to manage a LOT of objects moving and interacting on screen.

All in all, I recommend their conferences especially to students or people looking forward to starting into the video game development industry.

Javascript motion picture experiment, part 2

I wasn’t very happy with the result for my first take on the motion picture so I decided to revisit them. I did the following:

  • Remove the precision timing requirements by just calculating the film speed, position it correctly and have the shutter be triggered when the frame is passing through it.
  • Fix the shutter so that it’s not open/closed 50% of the time, instead it opens for 10 milliseconds and closes back, regardless of the desired frame rate.

 

Much better! if frame rate is high enough this looks pretty fluid, but may be too dark! (Note that the 1FPS example is skipping some frames, that’s an artifact of the animated GIF)

To compensate for the short shutter speed I added a white mask to simulate stronger lighting, then I increase the contrast of the film to make it look sharper under the increased lighting.

This also added a natural vignette effect to the movie. The final change I did was simulating a bit of imprecision on the shutter, making it randomly open a bit too late for some frames.

It actually looks much better when running directly (and at higher frame rates), things are happening just too quickly for the GIFs to get all important frames. I’ll clean up and push it to github soon for the curious, for the moment you can check it out online HERE.

I think I achieved what I was aiming for first, however for a real application it may be too tiring for the eyes… may be a hybrid approach (faking part of the visual effects) would be more appealing.

JavaScript motion picture experiment

While developing Curse of Wallachia, I was assaulted with a great curiosity: Would it be possible to emulate the way traditional film movies work using javascript?

The idea was pretty simple: Emulating the “film strip” as individual frames moving quickly (very quickly) on the screen, and then having an overlay area act as a shutter, synchronized with the film strip so that it showed a single frame at a time.

I decided to stream my adventure, which involved a lot of exploring the different options. You can check it out here and here

The goal was to make it so that the movie played at lower FPS than the actual game was running. I ended up doing the following calculations:

Given:

  • SHUTTER_HEIGHT in Pixels.
  • TARGET_FRAME_RATE in Frames per second

Then:

  • MILLIS_PER_FRAME = 1000 / FRAME_RATE
  • SHUTTER_SPEED = 1000 / TARGET_FRAME_RATE
    • in Milliseconds per shutter cycle
    • Each shutter cycle consists on it being opaque and then transparent for the same amount of time.
  • FILM_SPEED_MILLIS = SHUTTER_HEIGHT / SHUTTER_SPEED
    • in Pixels per millis
    • How quick should the film move so that a single frame is shown for each shutter cycle.
  • FILM_SPEED = FILM_SPEED_MILLIS * MILLIS_PER_FRAME
    • Converted to pixels per frame.
    • All sprites in the film strip move this distance on every frame.

Here are my observation after the first try:

  • It’s very hard to time the sprites correctly to move at this high speed and sync their position with the shutter. This is caused in part by the varying real frame rate and the high precision required for the timing.
  • Even if this was possible, having the shutter block the image 50% of the time reduces its brightness a lot (if I’m correct, they fixed this using stronger reflectors historically).
  • Ultimately, I missed the fact that the persistence of vision effect wouldn’t be achievable at low frame rate speeds so in order for this to work I’d have to work with higher frame rates, complicating the emulation even more.

I decided to give up this idea. I realize I may be doing something wrong so if someone wants to take on the challenge (or elaborate on why it’s not feasible) please post in the comments!

Later on I thought on alternatives to achieve my goal, I figured the only way to go was is faking both the Phi phenomenon and the Persistence of Vision effect as follows:

  • Keep the motion of the “film strip” using the same previous calculations.
  • Remove the shutter altogether, instead put a “still frame” sprite on top of the film strip. (This simulates the persistence of vision effect)
  • As the sprites from the film strip come close to a the “still frame” sprite, “burn” the sprite image into it. Since the film strip is moving quickly, this will simulate the phi phenomenon at lower speed (giving it the desired “retro” feel).

Here are the results:

 

I’m pretty happy with these results, admittedly I could have cheated and just put a blurb of pics as the film strip, since they are barely noticeable when over 8FPS… but this was fun and I think it feels more authentic 🙂

Finally, I made the still frame move around a bit when captured to simulate a bit of “jankiness” and imprecision.

cinema-janki

If you have followed the development of Curse of Wallachia, you may have an idea what I’m going to use this for.

“Voyage” is born, and how it handles land border tiles

Thank you all for your feedback regarding my last post, after much debating here’s my current mindset:

  • Current version of Expedition, while not covering the whole planned scope, is already a playable and potentially enjoyable game which requires some UX fixes to be shipped.
  • The gameplay ideas which composed the original scope for Expedition are worth implementing in a different format which can be appreciated by a broader audience.
  • I won’t be able to work extensively in the full version of expedition in the following months, thus it will likely be released in 2018.
  • The current version of Expedition can be released soon, freeing myself of the burden of having wasted trillions of dev time milliseconds.

That brings two games I’ll be playing in the following months…

Game 1: Release “Voyage”, the current version of Expedition

First thing I did was rolling back a lot of the changes made in the last months of development on 2012 for both Expedition and SERF when trying to modify the engine to run continuously and show animated sprites, thanks to the power of git (and a well done migration from SVN), I was able to roll back to about the status of the last public version (0.5)

Then after giving the game some playthroughs, I defined 3 macro areas to work in:

  • Minor and effective UX changes: Remove redundant data and interaction steps that were in place to allow future functionalities which will no longer be. Polish appearance without investing a lot of time on it.
  • Polish mini colonization facet: Remove clutter, simplifying the way settlements work while at the same time integrating them with the game winning conditions.
  • Balance game: Experiment with data to ensure the game can be won in around 5 voyages.

Upon examining the Java code I found out it isn’t THAT bad. There’s a nice structure, an engine which was unfortunately not meant to support animation from its onset.

engine

One of the things people suggested and I thought would have a strong impact was adding the “beaches” or land borders to the overworld. I toyed around with many ideas while I familiarized myself again with the code, and in the end decided to include it as part of the “rendering” process instead of it being part of the map model itself. At first I tried to do something similar to what I had done with Ananias walltops: Analyzing different scenarios and deriving rules based on them.

walltops

However, I thought that approach may not be needed in this case since this was simpler… doing that “2.5D” appearance for Ananias was sure a lot of work, but here it is an overhead view so I figured there may be a simpler solution. After some googling I found an old article which was pretty useful and in which I based my approach.

cleanerUI

What I’m doing is, for every visible water map cell I’m checking the surrounding cells to see if there’s land around. Each one of the 8 directions has a weight assigned as a power of 2 (with some additional tweaks for the ordinal directions), the sum of these weighted values determines what tile to draw over the ocean.

boolean[] mask = new boolean[]{
    upLand || leftLand || upLeftLand,
    upLand,
    upLand || rightLand || upRightLand,
    leftLand,
    rightLand,
    downLand || leftLand || downLeftLand,
    downLand,
    downLand || rightLand || downRightLand
};
int sum = 0;
for (int i = 0; i < mask.length; i++){
    if (mask[i])
        sum += POW_2[i];
}

There’s a map which maps values for this sum with base indexes on the tileset as follows:

POWER_MAP.put(128, 1);
POWER_MAP.put(224, 2);
POWER_MAP.put(32, 3);
POWER_MAP.put(148, 4);
POWER_MAP.put(41, 5);
POWER_MAP.put(4, 6);
POWER_MAP.put(7, 7);
POWER_MAP.put(1, 8);
POWER_MAP.put(47, 9);
POWER_MAP.put(151, 10);
POWER_MAP.put(233, 11);
POWER_MAP.put(244, 12);
POWER_MAP.put(239, 13);
POWER_MAP.put(191, 14);
POWER_MAP.put(247, 15);
POWER_MAP.put(253, 16);
POWER_MAP.put(255, 17);
POWER_MAP.put(5, 18);
POWER_MAP.put(132, 19);
POWER_MAP.put(160, 20);
POWER_MAP.put(33, 21);

 

beaches

Additionally, there are three variations for the beachfronts, what set to use is determined by using a hash function over the location of the cell on the world.

I also cleaned up the UI removing redundant info, changing the font for something more readable and relaying out everything to avoid wasted screen space. In the end I decided to support only the “Denzi” 32×32 tileset, which looked pretty good when I added the “beaches” and resized the viewport to 600×450.

I also worked on trying to add a full screen mode to the game. So far I have had partial success on this, I was able to scale part of the game but the engine works by having two layers: one where graphics are drawn (which I could successfully scale) and another one with UI components based on Swing which I think will be too hard to work with. I also found out the current way of finding the screen dimensions is troublesome on dual screens or scenarios where there are system menus taking part of the screen (although I think this could be fixed by using actual window measures instead of display size)

scaling

This is a hard one… I’m thinking on ditching fullscreen and make it work just windowed, although I recall some experiments on CastlevaniaRL back on… 2004-2005? which made full screen work without manually scaling (and which scaled Swing components as well)… I’ll have to dig a bit on it and see how it works in modern machines.

I’m planning for a release of this in around 2 weeks.

Game 2: Restarting work on the full version of Expedition

Expedition will play and look a little bit like a RTS, with miniature AI-controller units (and probably lots of them in some scenarios), following the player around an infinitely large world where Exploration is the main concern. It will NOT be a RTS, you will have direct control only over a single unit, and the game won’t focus on finding and exploiting resources on the world, nor placing building strategically or even tactical battles. In a sense it will be more of a toy, a miniature world you can explore with your miniature explorers.

expeditionMockup
Some initial ideas for the game, although my current vision would be a bit different with smaller looking units and a different art style.

Player created content will be a huge part of it, it will include an editor where players can build their own scenarios and worlds, themed in their favorite series or historical events.

It will feature seedable procedural world generation and most of the features defined in the current roadmap (which will soon be updated to remove some things that defintively won’t go)

Given these requirements, I am still not sure if Phaser is the way to go… I’m considering using Unity for it, but that’s a choice that may still wait for a bit.

Ananias introspection: Content Creation and Balance

This new series of blog posts will be based on /r/roguelikedev FAQ Fridays, a long standing series of discussions where roguelike developers share their techniques and experiences around a single topic.

FAQ Fridays Revisited # 6 Content Creation and Balance (original)

How do you decide what mobs/items/abilities/terrain/etc to add to your game? In any good roguelike content creation is inseparable from the concept of balance, so your methods of balancing content are certainly within the scope of this discussion.

Ananias has a fairly static collections of monster races. They were first added based on the original rogue monsters and with a single criteria on mind: each race should have an unique feature or ability.

Sometimes implementing an ability is costly and there’s the temptation of parameterizing it so it can be reused with slight variations, but I think it gives more character to the monsters having their distinctive skills. This means Ananias doesn’t have Ice, Fire, Water, Earth and Thunder dragons whose only distinction is the “type” of their attacks, I’d rather have a single Dragon monster distinguished by a strong ranged attack.

The uniqueness of an enemy race is not defined only by their active skills, their stats are also put in consideration. In this case they are assigned relative values first (i.e. orcs have high attack and medium hp, Lizardmen have extreme high attack and low hp). This is balanced so that races without cool active skills get some advantage stats-wise.

The dungeon is then split into 5 areas with increasing difficulty. Mob races are grouped thematically on these areas and then assigned final values for their stats by scaling their relative values to the difficulty level of the group they are in. In order to do this, I do a projection of the average stats for the player on each one of these areas and then I apply some simple general rules (for example, a strong monster may kill the player in 5 hits, a monster with low HP should be killed by the player in a single attack). These define ranges within which the stats of the final monster will be placed.

Items follow a similar model. For weapons and armor I started with a list of preexisting graphics (the artist made them on advance for a different game using his own creative criteria) then based on they appearance I assigned them relative values (high/mid/low) for damage and integrity (how likely they are to break), and then I grouped them on power tiers to define their final stats. They also get assigned a generation weight representing how likely they are to be added to the levels, this value is used in level generation along with the power tier to select the items to be added.

Player abilities are guided by the same principles of uniqueness. Ananias is strongly classed, and each class has unique passive skills that guide players into adapting different playstyles for each one. The process for player classes is similar as with the monster races, they are balanced based on their skills and relative values for their stats. (Except for the shepherd ;)).

On a final note, I’d like to say that while seeking to balance the different stats is worthwhile (to prevent your game from being utterly broken), it’s also to some point a futile exercise, since the variability of the game and the player’s choices create a lot of unpredictability, specially as journeys progress in games which provide long and open experiences. This is not necessarily a bad thing, and a little bit of unbalance is not always harmless since it allows the players to find strategies and challenges within the game.

 

A better build and deploy process for hybrid apps?

The current build and deploy process for Ananias takes a lot of time and requires lots of focus and bandwidth. Everything is done manually (and is thus prone to errors). Most of times I just can’t afford to release for all platforms, and I forget to announce thru all channels.

The cocoon.io parts are a bit cumbersome since they don’t have a CLI and it requires some back and forth, while also adding a dependency to an online service (for how long is it going to work?) I’m going to experiment with some native crosswalk builds to see if that step can be saved.

But probably what takes more time is the manual process to be done for each different storefront via its own web interface. Each one requires different info, and all uploads have to be started and monitored manually. It’s pretty uncommon for a store to supply a command line tool.

May be I should automatize parts of this? Someday I may do that, in any case it’d simplify packaging but wouldn’t make distribution any easier.

This is the current process executed for each new release of Ananias:

  • Mark the new version in the source code (client and server)
  • Generate a new JS bundle using browserify
  • Upload new build to web (ananiasgame.com/play) (About 15MB) (Only Standard edition)
  • Deploy new server version and restart (About 1MB) (including executing migration database scripts)
  • Use nwbuild to generate the executable packages for Windows, Mac and Linux
  • Rename the directories and zip them
  • Upload to itch.io via web interface (About 200MB )
  • Zip web directory
  • Update config on cocoon.io for next version
  • Upload web directory to cocoon.io (About 15MB)
  • Hit the build button on cocoon.io, wait for build to finish
  • Download APK packages from cocoon.io (About 150 MB)
  • Unzip package, copy release APKs (one per architecture, x86+arm7) to APK directory
  • Run script to sign and align APKs
  • Sign in to google play developers console
  • Upload both APK (About 100 MB)
  • Build change log from git commit messages
  • Summarize change log for Google Play
  • Upload both APKs to Amazon via web interface (About 100MB)
  • Upload to indiegamestand via web interface (About 200MB)
  • Upload to gamejolt via web interface (About 200MB) (Only Standard Edition)
  • Upload to IndieDB via web interface (About 200MB) (Only Standard Edition)
  • Set cocoon.io for iOS builds
  • Hit the build button, wait for build to finish
  • Download xcarchive (About 50MB)
  • Open xcarchive in xcode organizer
  • Submit to App Store
  • Wait for processing
  • Go to iTunes Connect
  • Go to Testflight / External Testing
  • Select new build to test
  • Wait for beta review
  • Go back to Testflight / External Testing
  • Set the new build as active.

The process should be executed twice, once for the Standard Edition and then again for the Fellowship edition.

THIS IS A LOT OF WORK. WHY CAN’T IT BE SIMPLER? This process takes out HOURS (Not exaggerating) of valuable and scarce development time for an Indie dev.

Why can’t I just simply update a single web endpoint, and have all the clients deal with the updating? they have to deal with it anyways, they just have to do it with their own, different store.

Also, the game is already multiplatform, if your platform has an HTML5 browser, like all modern platforms, you can play the game (Yes, you can even play the game on a PS4), then why go through all this?

I understand the advantages of having a downloaded app… people wants to feel they own the app, and they want to play their games while offline or in poor network conditions.

It would be awesome if the app could automatically check and download the updated scripts and required assets. This is not a new idea… some people has already tried but there are some obstacles:

  • Both Play Store and the AppStore are not very clear about their support of this model. You may be risking your app being taken down for not complying with the terms of service. They are also not MEANT to work like this, so you are always fighting against their nature.
  • For in-dev games, the player wouldn’t be notified a new version exists unless he runs the game or you implement some kind of push notifications.
  • The system has to work perfectly, if it doesn’t then your players will be stuck with an old version, or will be unable to play offline, or will end up with a malfunctioning app.
  • Another thing to consider is that this would prevent players from updating the web runtime in which the game runs, although I guess that may be fixed via a normal update.
  • If the player uninstalls the app or erases its data cache, they will have to redownload all assets.

There is a cordova plugin (cordova-app-loader) which adds support for “hot updates”. It seems to work but has some issues (based on what people reports). I don’t know of any similar tool for node-webkit / NW.js, I guess it’d have to be done manually.

Any thoughts?