Friday 2 October 2015

Combat Balancing

In games the combat balance of the player vs the enemies is extremely important, as it defines how the entire experience feels. If done incorrectly it can make the game seem unfair, or simply dull. However if done correctly the game becomes an engaging experience that brings the player back time and time again.

With that in mind, how does one go about balancing your combat?

Well if you search about the subject online you see many sources discussing playtesting, spreadsheets and graphs. These are however tweaking mechanisms, the steps you run through to perfect an already built system. Without that system they are useless, merely introducing a layer of complexity that does not result in a finished product. So with that in mind, how does one build a combat system?

My technique for doing so is to think about the combat in terms of 'hits'. This is an abstracted version of health that refers to the number of times an entity must be hit before its considered dead. This value boils down the complex system of HP, attack, defense into what the player experiences, and allows you balance accordingly.

To figure out how many 'hits' an enemy should take you need to next decide on the flow of the game. Is it a fast paced game, where enemies die after only a few strikes? Or a longer turtling game where combat is a battle of attrition, with enemies requiring tens of strikes to be downed? Once you decide on the flow you can assign hit counts to enemies respectively. For a medium paced game a weak enemy could require 4 hits, and normal enemy 6 and a strong enemy 9.

We can further use the hits system to balance out our attacks. So a basic attack might deal 1 hit, a strong attack 2 hits, a special ability 4 and a ultimate ability 6. Another example using final fantasy attacks:

Basic attack = 1 hit
Fire = 2 hits
Attack, charged up = 3 hits
Fira = 4 hits
Firaga = 6 hits
Limit Break = 8 hits

So this way we figure out the relative power of skills when compared to one another.

So how do we figure out what Hp, Attack and Defense to give our entities so they obey our hit counts? At this point here is how our game entities look:

Weak: Hits=4, Hp=?, Attack=?, Defense=?
Normal: Hits=6, Hp=?, Attack=?, Defense=?
Strong: Hits=9, Hp=?, Attack=?, Defense=?

Now we can make a little formula to figure out relationship between the other stats:

Hp = calculateDamage(Attack, Defense) * Hits.
Which means the Hp will equal the damage formula (taking the attackers attack and defenders defense) times the number of hits that entity strength can take.

So to figure out the correct values for the formulas I like to start from deciding the amount of damage a 'hit' should entail. Lets say I pick a hit to be 10 damage. This makes the hp of our entities easy to figure out, 40, 60 and 90 respectively. We also now have an output value for the damage formula.

calculateDamage(Attack, Defense) = 10

Now the details of how you calculate the damage based on the inputs are entirely up to you. There are tons of examples of formulas floating around the internet to look at as an example, but you can read on to see what I use.

So in my game I determined that I want attack and defense to be a contest against one another. So when attack equals defense it should deal 10 damage (1 hit). If attack exceeds damage it should deal extra hits based on the amount it exceeds the defense by. If attack is less than defense it should deal only part of a hit, based on how far below the defense it is.

So here is my formula:

if (attack == defense)
    damage = 10
if (attack < defense)
    damage = 10 / ((defense - attack) * 0.5)
if (attack > defense)
    damage = 10 + (attack - defense) * 5

The values of attack and defense then only matter when compared to one another. I am basing the attack and defense value off the 'level' of the entity, so higher level things deal more damage and take less damage.

Anyway I hope this helps you plan out your own combat system!

Sunday 27 September 2015

Gameplay

Well it certainly has been a while since I posted anything on here. Work on Chronicles of Aether has been flying along, inching closer to a complete gameplay system that I can release as a playable demo.

Anyway here you can see what the game currently looks like:

Saturday 15 August 2015

The Dynamic Dungeon

This week I have been plotting out a series of ideas for how to add interactivity to the dungeon. Often the dungeon can feel flat and lifeless, an empty husk used simply as a container for enemies to wait to be slaughtered. I wish to dismiss this feeling and make the dungeon seem just as dynamic as its denizens. So to do that the player needs to feel that the dungeon itself can be interacted with, and the dungeon needs to change in meaningful ways in reaction. A few methods are outlined below:
  • Destructible Walls - Many roguelikes include the ability to dig through walls, allowing the player to tailor the dungeons layout to their needs. However this provides complications with many pieces of content where the entrance/exit are well defined, and the ability to enter at any point breaks it. This tends to be countered by 'undiggable' wall types.
  • Traps - A staple of roguelikes, traps activate some affect when triggered, often by being stepped onto. The most common traps are single tile direct damage traps, though more complicated setups are sometimes used. These can often feel cheap, as their short range nature require tricks to get the player to step onto them, like making them invisible.
  • Puzzles - An interconnected series of things that interact in some way, the a solution that provides some form of reward. An excellent method for adding interactivity to the dungeon. The issue being that generating a good, interesting puzzle can be very hard.
As interesting as these would be to add it was not what I started with. So the first task I completed towards dungeon interactivity is a thing I like to call 'Fields'.
Fields are a single tile area of some substance, that interact with other Fields (and the entities within them) in some fashion. For example a Fire that can be put out by pouring water onto it, turning the water into steam. These fields can be placed in the level by various methods, from an entities death (a spore releasing a cloud of poison gas on death), to a dungeon feature (smashing a fountain releasing a constant stream of water).

Like everything else in my game the fields are data driven, having a series of data points that define their properties. They are as follows:
  • Field name - This is the name of the field, used for checking if a neighbouring field is the same as this one
  • Tags - A list of tags that define the properties of the field, using for the interaction map (Examples are Hot, Cold, Gas, Poison, Explosive).
  • Stacks - A value representing the strength of the field.
  • Layer - The field 'layer' it exists on. Currently I have Ground and Air.
  • Duration Style - This defines its lifespan. I currently use Permanent, Fade (slowly reduces the stacks until the field disappears) and Collide (disappears on first collision with an entity).
  • Spread Style - This defines how the field spreads. I use Flow (flows out down the 'stacks' gradient), Adjacent (spreads to nearby tiles with stuff in them), Wander (randomly wanders around) and None (doesnt spread)
  • Field Interactions - A map of field name or tag to an interaction event. Currently supported interactions are Spawn (spawn a field of the specified type) or Propogate (applies some effect to every field tile joined to the src tile).
  • OnTurn - A list of effects to apply every turn. Examples are Damage and Healing. 

An example of spreading fire:











Water putting out fire and turning into steam:











Lightning hitting water and propogating through it:

Friday 7 August 2015

Interactivity Through Sound

For those interested in other devs writing on sound design you can read the fantastic set of articles by the developer of Cogmind here: Sound in Roguelikes, Sound Design, Ambient Sound

This week saw the introduction of sound into Chronicles of Aether.

Sound is immensly important in games as it provides one of the two main ways the computer can interact with our senses (the other being sight). With such a large portion of the players experience of a game relying on this it is a shame it tends to get neglected by developers so often.

In Chronicles of Aether sound is seperated into 3 main categories.
  1. Music. Each level has a background song that plays, picked to evoke an emotion that reflects the design of the level. For example a foreboding eerie track for a crypt, or a happy jaunty tune for a bright forest.
  2. Ambient sounds. Sounds that play due to some logic (such as repeat infinitely, or once every 10-20 seconds etc). These are used to give a further depth to the atmosphere building for the level. Examples are groans and echoing splashes for a dungeon, Birds and insects for a forest.
  3. Effect Sounds. These are sounds that play in reaction to some action an entity in the world has made. An example being the sound of a sword strike or a fireball explosion.
After implementing these sounds I had entities exclaiming when they saw an enemy, groaning when they were wounded, and a cacophany of other sounds as they bashed each other to death. Sitting back I looked at this scene a realised it felt contrived, all this noise that meant nothing to anyone but the player.
Then I had an idea. Why don't I add meaning to the sounds, something that the entities can act on? With this in mind I added a piece of data to each of the dynamic sounds. Entities close enough to hear the sound would have that data inserted into their ai, to process as they will. Suddenly the world came alive. Sounds of fighting would draw in nearby entities to investigate, enemies would shout out the location of the player, informing their allies of the players position and calling them forward. Wounded creatures could inform their healer allies that they needed assistance. And every piece of this interaction was perceivable to the player, as they could hear the creatures talk to one another.

Now I'll get quickly into how this is implemented:

  • Background and ambient sounds have no in game meaning other than to create atmosphere, and therefore the code playing them is very simple.
  • Sound effects have a few different configuration settings:
  • Max Volume. This is the maximum (before attenuation) volume.
  • Max Range. This is the range where attenuation will have brought the volume to zero.
  • Attentuation Offset. The range value to start attenuating from. Anything below this plays at max volume.
  • Data. The data to impart to the entities who hear it.  

Whenever a sound is played it gets all the entities within a sphere around the source defined by the maximum range. Then for each entity it tries to path towards it using AStar. If it can find a path with less steps than the maximum range that entity 'hears' the sound and gets the data.
It does the same for the player, to calculate the volume for the sound to be played at.

Saturday 1 August 2015

Abilities

Abilities in Chronicles of Aether form the core of what makes combat interesting. There need to be techniques to suit all combat styles, from the up close and personal face tanking barbarian to the glass cannon long range spellcaster, there should always be abilities that prove interesting to use.

So with this goal in mind abilities are built up from a pool of constituent parts, defined in some data files. The available parts are are follows:

  1. A Targetting style. I currently have Self, Enemy, Ally, Tile or Direction.
  2. A Movement style. I currently have Smite (takes no travel time to hit, ignores everything until it hits its target), Bolt (has a travel time, collides with first non-passable thing) and Ray (hits everything in a ray up to the first non-passable thing).
  3. An aoe style. Currently supports Cone and Circle.
  4. Some effects (applied to each affected tile). Currently supported are Damage, Healing, Status, Teleport (this is used for all effects that move an entity).
Some examples:
Fireball
  • Targetting: Direction
  • Movement: Bolt
  • AOE: circular
  • Effect: Damage (maybe even a status effect if you want to add some burning too)
Jump Attack
  • Targetting: Enemy
  • Movement: Smite
  • Effect: Damage, Teleport (with a flag saying it should play a leap animation rather than a teleport one)
Heal
  • Targetting: Self, Ally
  • Movement: Smite
  • Effect: Heal
Shown below are some examples of usable abilities currently in the game:





Thursday 23 July 2015

Populating the Dungeon

(A lot of the work in this post is based off ideas introduced in this excellent blog post. I highly recommend reading it.)

Using the algorithm outlined in my previous post I have a dungeon generating, ensuring the chosen rooms are included and filling in the blanks with random rooms. The random rooms can be given an irregular shape by running one of multiple algorithms on them (At the moment the supported ones are Overlapping Rectangles, Starburst, Cellular Automata, Chambers. Credit to Unangband/Andrew Doull.)

So the output of this algorithm will look something like so:


As you can see it contains 2 vaults (one with a downward stair), and the rooms themselves are made more interesting by running them through the Cellular Automata algorithm. I feel that this map is now interesting enough in structure to begin to place enemies and etc.


To give the map a coherent feel I decided to not go for the 'random' approach to enemy placement, instead opting for a faction system. The idea is that each level should have a Major faction that dominates it, with a big boss sitting in one of the rooms. Every other room will then be influenced by this faction, based on its distance from the boss room. Some other minor factions can be sprinkled throughout the map to give some variety to the encounters.
Furthermore to give the rooms the feel of being owned by a faction (other than just the monsters you encounter in that room), a set of 'Features' can be placed in the room, based on the faction that owns it (for example the 'Fungi' faction will have glowing mushrooms, and the 'Toad' faction will have pools of water).

How the algorithm works is thus:

  1. In the level definition a set of 'Major' factions and a set of 'Minor' factions are specified.
  2.  Once the level has been generated a single 'Major' faction is selected. The largest room in the dungeon is then assigned this faction.
  3. The furthest quarter of the rooms from this selected room are then assigned a random faction from the 'Minor' list.
  4. All the remaining rooms are assigned an 'Influence' value based on their distance from the central room (minor faction rooms are assigned a random influence value).

Then for each room

First we look to place the features. Features can be assigned an influence range to appear within, a placement location that determines where they are placed (either FurthestFromEntrance, Wall, Centre or Any) and a Coverage percent (this determines what percentage of the valid tiles to cover with this feature).
  1.  Seperate all floor tiles into 4 lists.
    • A list of tiles sorted by average distance from the entrances (for FurthestFromEntrance).
    • A list of the floor tiles that are adjacent to a wall (for Wall).
    • A list of all the floor tiles that are not adjacent to a wall (for Centre).
    • A list of all the floor tiles (for Any).
  2. For each feature defined in the feature list.
    • Check the feature is valid for this influence.
    • Get the percentage through this features range ( (influence-min) / (max-min) )
    • Multiply the coverage by this factor to get the coverage for this feature in this room.
    • Calculate the number of features to place. (NumValidTiles * CurrentCoverage)
    • Randomly pick tiles from the valid list and place the feature on this tile (remember to remove the tile from all the lists after using it).
This will leave a room filled with features for the faction. However there is no guarantee the new features havent blocked off the entrances, so to prevent this I try to path (using A*) between each pair of entrances, clearing features that block the route.

Now it is time to place the enemies. The faction definition contains a list of encounters (groups of monsters to spawn together), and the min/max influence they appear at. I then randomly pick a valid encounter for the rooms influence, and place the enemies on randomly selected floor tiles. (This part of the algorithm needs work, possibly by basing the number of spawned enemies off of the room size).

So at the end of all this you should be left with something like so:


This is the same level as before, but with the features added and enemies spawned. The major faction is the 'Fungi' faction and the 4 minor factions placed are all 'Toad' factions.

A quick picture of a dungeon being explored:


Another Before/After:





Friday 17 July 2015

Dungeon Generation

The focus this week has been on dungeon generation.

Up till this week I had been using a BSP to generate the level, but I was unsatisfied with this due to the lack of control I had over it. Furthermore a problem with the algorithms I could find posted online again had the same issue. So I set out to create a new algorithm with the following constraints:

1. Should allow control over the rooms to be included. For example 2 stair rooms, 1 large vault and 3 minor vaults.

2. Should fill the remaining space with random rooms.

3. Should connect everything up.

With these steps in mind I came up with this:

The algorithm is based around 'docking' rooms to one of the four corners of a space. The space is then divided into two parts (based on the room's size), and the algorithm is called recursively on the subparts. The strength of this is that you can specify a list of rooms to be included, dock those in the spaces they will fit, then fill the rest with random rooms.

Example:


Starting with:

###############

###############

###############

###############

###############

###############

###############


Placing a single room:
...
...
...

You pick the NE corner, making the grid look like:
###############

###########...#

###########...#

###########...#

###############

###############

###############


Then the remaining space gets seperated as follows:
1111111111#####

1111111111#...#

1111111111#...#

1111111111#...#

1111111111#####

111111111122222

111111111122222

Where 1 and 2 are the two different zones created.

You can then run the algorithm on zone 1 and zone 2.

Using offsets and random sizes for the random rooms (and picking random corners) the resulting grid gets filled with rooms.

You can then place the doors and connect the rooms via corridors (I use a reduced delauney triangulation to find the pairs of doors to connect, then connect them by using A* to find a path).

Examples of the final output:

##################################################
##################################################
####...............###############################
####.#############.#######.......#################
####.#tBBbt#tbbBt#.#######.......+.########....###
####.#bcTcb#bcTcb#.#######.......#.########....###
####.#B.c.b#B6c6B#.#######.......#.########....###
####.#b...b#B...b#.#######.......#.########....###
####.#b...b#B...b#.#######.......#.########....+.#
####.#b...B#B...b#.###############.########....#.#
####.#B...B#b...b#.###############.########....#.#
####.#t...t#t...t#.##############..########....#.#
####.###.#####.###.############....#############.#
####.#...........#.##########...##.#############.#
####.#t.........t#.########...####.############..#
####.######.######.########.######.#########.....#
####......#+#......########.######.#########.###.#
###########........................########..###.#
###########..##########.#+########.######...####.#
############............#.......##...##...######.#
############.####+###.###.......##.#.##.########.#
############.#......#.###.......##.#..#.########.#
#####...####.#......#.###.......##......########.#
#####.>.+....#......#.###.......##..#+##########.#
#####...#.####......#.###.......##.##.........##.#
#########.####......#.###.......#..##.........##.#
#########.###########.###.......#.###.........##.#
#########.###########.###########.###.........##.#
#########.###########.###########.###.........##.#
#########.###########.###########.###.........##.#
#########.###########.###########.##############.#
#########.###########.###########.##############.#
#########.###########.###########.##############.#
#########.###########.###########.##############.#
#########...#########.............##############.#
###########.#########..#########################.#
###.......#.#########..#.........####..........#.#
###.......#.#########..#.........####..........+.#
###.......#.##......#..#.........####..........#.#
###.......#.##......#..#.........####..........#.#
###.......+.##......#..#.........####..........#.#
###.......#.##......#..#.........####..........#.#
###.......#.##......#..#.........####..........#.#
###.......#.##......#..+.........####..........#.#
###.......#.##......+..#.........####..........#.#
###########.##......#..#.........####..........#.#
###########..########..#########################.#
###########......................................#
##################################################
##################################################

Drawbacks:
The resulting map can tend to look quite 'gridlike' (though greater ranges for padding and room size will reduce this tendency).

The map space is not used efficiently (though in most cases, you dont want to pack the map too tightly, so its not the biggest issue).

If the grid is too small the rooms may not be able to be placed (Can brute force by increasing the size of the grid then regenerating, repeat until everything fits).


The way I use it is to have a level description that defines 'required' rooms and 'optional' rooms (with some guide for how to pick the optional ones, like Pick 2 from this list). So when creating a level I create a list of rooms to be added, created from all the 'Required' rooms, all the rooms selected from 'Optional' and any rooms added by the game (like stairs connecting to the previous room). Then using the algorithm all these rooms can be added to the grid, and the remaining space filled in with random stuff.

This algorithm seems to fit all my criteria, and so far I have been very happy with it.

New Roguelike Game

Announcing a new roguelike game:

Full graphical tiles.
Mouse driven at the core (keyboards are so overrated :p)
Enemy AI using Behaviour Trees.
Cooldown based abilites (no mana / stamina)
Speed based turns (entities get a different number of turns depending on their speed).
Crossplatform support (Java with Libgdx, so should work with windows, mac, linux, and presumably android once I make the UI support it).
Interesting passive abilities, items and status effects.