Saturday, 8 October 2016

We're currently rebuilding the entire matchmaking, menus and party system of Awesomenauts. This rework is called Galactron. This is a huge operation: especially the matchmaking part is highly complex. We've done a bunch of betas for it so far and last week we let it run live for everyone for 48 hours. After that we turned it off again to analyse the results, read player feedback and fix bugs. Today I'd like to discuss the most interesting results.

I plan to do an overview of how Galactron works in a future blogpost, but today I'm just going to focus on what we've encountered in last week's beta.

Ragequitters and the removal of latejoin

This is probably the hottest topic in Galactron. Latejoin was probably the most hated feature of the old Awesomenauts so we've removed it altogether. In Galactron players are never put in a match that's already in progress. We've also added a rejoin option so if you're kicked out of a match due to a network error you can get right back into it. Finally, if someone leaves a public match, he can only rejoin that same match and can't get into another. A lot of players like this, but there's also a vocal group who is strongly against the removal of latejoin and the increased punishment for ragequitting.

I think there's no way to make both groups happy: they just want something different out of Awesomenauts. I've seen some people suggest we should make two gamemodes: one where latejoin and ragequit are allowed and one where they aren't. However, this is not an option because our playerbase is not big enough to split it like that. If we had the playercounts of League of Legends we could try things like that, but with our current playercounts we can't afford to split the community. Matchmaking in both versions would be worse if we did.

Instead of talking about opinions, let's have a look at cold hard facts. A common complaint about the removal of latejoin is that if someone leaves a matches and doesn't rejoin, the match remains 3vs2 until the end, whereas in the past the player would be replaced by someone else. This replacement didn't always happen though: in the old matchmaking matches weren't all 3vs3 all the time either. We gather metrics on this, so let's have a look:

As you can see the percentage of time that matches are 3vs3 is actually higher in Galactron than in the old matchmaking. In other words: while it may feel lame that leavers aren't replaced by latejoiners anymore, the overall result of Galactron is that you're playing 3vs3 more often. So while I can imagine that ragequitters dislike the new punishment they get for leaving, the complaint that matches remain incomplete more because leavers aren't replaced turns out to be unjustified.

High ping

The other major complaint about Galactron is high ping. While Galactron is supposed to make the average ping lower, players complain that ping is higher. Again, let's look at the metrics to see whether they're right:

Here it turns out that the stats agree with the complaining players: average ping has indeed increased significantly in Galactron. This is a big problem that we need to solve. It's also a big surprise to us: Galactron contains a lot of clever tech to match players better, why isn't that working?

We've been analysing this extensively these past few days and have a bunch of theories, but we don't have the final answer yet. One thing we've noticed is that it seems like Galactron doesn't get enough ping data. While players are waiting for matchmaking the game pings all other waiting players. We expected this would give us ping data on at least 75% of all possible connections, but in practice this turns out to often be around 50%. This means that Galactron doesn't have enough information to make a good matchup. Figuring out why this happens is our highest priority now.

Another possibility is that Galactron may care about skill too much right now. Galactron needs to find the best match-up based on a bunch of criteria around ping and skill. It can't satisfy them all perfectly all the time so it tries to find a balance. We can tweak how important each criterium is and I think we might have made skill too important right now.

A final issue we're looking into is how to handle people with horrible internet. High ping can happen when you're playing against someone on the other side of the globe, but sometimes your opponent just has such a horrible internet connection that he has high ping to everyone, even to people near him. No matter who Galactron matches such players to, the connection will always suck. We haven't decided how to handle that yet. One option is to make such players only play against others with crappy internet and keep them out of the regular matchmaking, but that might give odd results in the leaderboards as they would always be playing against the same people. We'll have to observe this group more once the other issues are fixed.

Skill matching

This is an aspect where Galactron seems highly successful: matches are very balanced. There are still situations where players of differing skill play against each other, but this is now usually caused by premades in which the players don't have the same skill. We can't split premades so this can't be solved. What we can do however is balance such premades against another similar premade, or against other players with similar skill patterns. Galactron turns out to be able to do so quite often. We also see premades play against other premades much more often now than before.

It would have been nice to compare this to our snowballing stats, but unfortunately those weren't recorded in the Galactron live beta due to an oversight. We normally collect stats about how often one team has much more kills than the other team, and how often the losing team didn't destroy any turrets. It would be interesting to see those stats for Galactron, so it's a pity they weren't recorded during the last beta.

Waiting times

I've seen some complaints regarding long waiting times in Galactron. Galactron performs a matchmaking round once every 5 minutes. That means that if you start searching for a match just before the round starts you'll get a match really quickly, while if you start searching just after you'll have to wait the full 5 minutes. On average this means a waiting time of 2.5 minutes. That isn't that much longer than before: in the old matchmaking it might take up to 4 minutes before a match stars, and longer if it isn't full yet at that point.

It's easy enough to reduce the waiting time: we can just switch from 5 minutes per matchmaking round to 4 minutes. However, we're not sure we should: a shorter waiting time will also mean lower match quality. There's a balance we need to strike here between speed and quality, and for the moment we think 5 minutes is fine. If we keep seeing a lot of complaints about this we'll reconsider and might change it to 4 minutes at some point. Feel free to share your opinion on this in the comments below.

Crashes

There have been some reports on crashes in Galactron. We've investigated most of these and so far they seem to be caused by driver issues and by broken Steam downloads. The latter can usually be fixed by running Steam's Verify Integrity Of Steam Cache.

As for the driver issues: my theory is that the crashes that we have data on are caused by alt+tab. Unfortunately our crashdumps don't contain a history that tells us whether alt+tab happened just before, so we can't tell for sure.

If alt+tab crashes than your videocard drivers are problematic. The solution is really simple: run in Borderless Window instead of Full Screen. With Borderless Window the game still covers the entire screen, it's just a different way of asking Windows to do so. Borderless Window is much more stable when it comes to alt+tab, and it's also much faster at it. In general we advice never running Full Screen unless Borderless Window gives you problems. I think we might rename these options to reflect this, since I expect some people might not understand what Borderless Window means.

We're still investigating some of the crash reports. If we encounter any that are caused by Galactron, we'll fix them of course.

Bugs

Besides the things mentioned above there were also a bunch of random bugs. Most of those we've already fixed by now, or are working on fixing. Some of the most notable ones:

Global chat didn't work half the time.

One menu screen around the tutorial didn't accept any button input, making it impossible to progress if you got to this screen (ARGH!).

Some menu screens didn't support controllers properly; only keyboard+mouse worked there.

So there you have it, the current status of Galactron. If you have any further questions, complaints or suggestions on Galactron then please do post them below in the comments. We hope to do another beta 1.5 weeks from now. Hopefully that one will be good enough that we won't have to turn Galactron off again, but we'll have to see how it goes. Most important is that Galactron needs to be really good before it goes out of beta.

Sunday, 2 October 2016

When designing weapons, there are many ways of differentiating them and spicing them up. To help anyone who is working on that, I would today like to give a big list of weapon parameters that one can play with. Most can be applied to ranged, melee and special attacks, but obviously not all can be used for everything in every genre. I hope this list can be used as an aid, regardless of genre. Note that I only focus on gameplay here: graphics are also an important part of the feel of a weapon, but his post is entirely about gameplay.

I made most of this list a couple of years ago when I was thinking about creating an open world game with randomly generated enemies. As a first step I made a long list of weapon properties and side effects that could be combined to create attacks for those enemies. As with many of my game ideas I doubt I'll ever get around to actually making this game, but I figured this list might also be useful to others, so I've extended it into this blogpost.

Most of this list is inspired by what Ronimo designers Fabian and Jasper made for Awesomenauts. Awesomenauts currently has 27 characters, each with 3 different attacks. Each character also has 18 unique upgrades with several tiers each. In total that's over 1000 upgrades! Many are simple upgrades that modify things like damage, cooldown, stun or range, but some are very unique, like the ones that upgrade the "AI" of Leon's clone. I'm really impressed by the creativity that went into many of those and put many into this list.

This list is by no means complete: you can come up with new ideas and variations forever. If you feel I overlooked some cool things, then please post them below and I will add them.

Basic parameters

Melee, ranged or area: for example, is it a sword, a gun, or an area effect around the character?

Range: how far does the attack reach? Note that this is not only important for guns, but also for things like the length of a sword

Effect timing: how long after you pressed the attack button does the damage actually apply? In case of a large sword movement, it might take a second before the sword actually hits. This gives the opponent time to get away, but also gives the attack itself a more powerful and impactful feel.

How often you can hit

Attack speed / cooldown: how fast an attack can be repeated. The term "attack speed" is usually used for weapons that can be used several times per second, while "cooldown" is generally used for things that take several seconds before you can use them again.

Ammo: weapons might have a limited amount of bullets. If they run out, you need to pick up more bullets, switch to another weapon or wait for bullets to regenerate.

Ammo clips / reloading: even if you still have enough ammo, you might have to pause shooting once in a while to reload. Parameters are how often you can shoot before you need to reload, how long reloading takes and whether you can reload before the ammo clip is empty. A special variation here is the Active Reload made famous by Gears of War: if you press a button with a very specific timing, the reloading is super fast or might give you a small buff.

Mana: similar to ammo, but mana usually automatically regenerates over time and is shared between several weapons.

Interrupt immunity: can an enemy interrupt this attack through a counter-attack or stun, or will this attack always finish once started?

Controls

Charge by holding a button: the weapon doesn't fire until you let go of the button, and it charges as long as you keep it pressed. Charging might make you throw further, or deal more damage or have some other effect. An important choice here is whether you can still move while charging.

Other charges: the weapon gains strength in some other way. Maybe every time you hit an enemy with your basic attack your special attack grows stronger, or maybe it becomes stronger over time. Once you use the weapon the charge is usually lost and the weapon needs to be charged again.

Holding versus button bashing: when doing consecutive slashes or shots, do you need to hold the button pressed, or bash the button all the time? This will also influence the number of attacks per second: if a new attack doesn't start until you press again, there will usually be a couple of frames in between attacks even when the player presses the button really quickly.

Combo's: specific combinations of attacks or button presses create stronger or different attacks. To make this more challenging this usually requires a specific timing for the button presses, but it can also be as simple as every third hit being stronger.

Cross-weapon influences: in this case performing one attack adds an effect to the next usage of a different attack. For example, maybe after using your special skill the next gunshot is charged with stunning electricity.

Timing: when do you need to press the button? For example, do you do extra damage if you press again just when the last slash has its impact?

Basic impact

Damage

Heal

DoT / HoT: Damage Over Time and Heal Over Time. These are controlled by a couple of parameters: how long it lasts, the total amount, and how often it applies (10 damage once per second isn't the same as 1 damage 10 times per second!). An extra setting is whether DoT stacks or not: if you get hit again while a previous DoT is still happening, do they add up or not? You can also have different DoT types: in Awesomenauts two poison DoTs don't stack, but a fire DoT and a poison DoT can happen simultaneously, so they do stack.

Stun: keeps you from doing anything for a time and interrupts whatever you were doing.

Microstun: this is a stun that is so short that it breaks your current attack, but doesn't keep you stunned. So you keep control, but if you are for example already charging an attack, then this charge is cancelled. In case of Awesomenauts, a good example of this can be found in Leon, who has microstun on his slash and can use that to turn off a Yuri's jetpack, causing the Yuri to fall to the ground, making him vulnerable for further melee attacks by Leon.

Silence: keeps you from using weapons and/or skills. Often this only influences special attacks and lets you keep doing your basic slash.

Snare: keeps you from moving, but allows you to still use weapons and skills.

Blind: for a while you can't see anything, or not far enough, or only right in front of you. In shooters this is often done with a flare grenade.

Knockback: an enemy is pushed away, often also a bit upwards into the air.

Throw: similar to knockback, but here you pick up an enemy and throw her away.

Crit: there is a certain chance that an attack is extra strong. Settings are the chance of the crit, and the amount of extra damage/effect. You can also implement rules that keep consecutive crits from happening, or that make sure that there is always at least one crit every N attacks. Have a look at these two blogposts for an analysis of designing crit chances.

Lifesteal: gain life yourself whenever you damage an opponent.

Note that many of these effects limit the player's control. Especially stun is really bad at this, but slow, silence, snare, blind and knockback all have such effects. When designing a single player game, these can be great fun: stunning an enemy is great and allows for mechanics in which you combat large groups of enemies without being overwhelmed. However, in an online multiplayer game like Awesomenauts, anything you do influences another human player. Being stunned for a long time is incredibly frustrating, and the same goes for snares and strong slows. All limitations are frustrating, and limitations on the player's movement even more so. Keep this in mind, and in general avoid designing situations where combos of these keep the player out of control for too long.

Projectile movement and count

For ranged attacks, the projectile you fire (or bullet, or magic fireball, or whatever) also has a number of its own properties:

Projectile speed: how fast the projectile moves makes a big difference for how dodgeable it is. If it moves pretty slowly compared to an enemy, you might even have to aim in front of her to be able to hit. The most extreme speed here is the instant hit, where the simulation doesn't consider it a projectile at all. This is the case for guns in most shooters. In the case of Awesomenauts, aiming is quite easy, but so is dodging. Using a sniper in an FPS, aiming for a headshot is pretty difficult, but if you aim correctly then the other player can't dodge at all. This greatly influences game feel.

Projectile size and shape: how big is the projectile, and is it square, rectangular, round or some other shape.

Homing: whether the projectile automatically changes its path to follow an opponent. You can tweak how quickly the projectile can rotate towards the enemy, but also how long it takes before the projectile starts homing after it was shot. Another setting is whether the projectile chooses its target only when shot or can alter its target during flight.

Arcs: some projectiles might not fly in a straight line. Grenades might be thrown in an arc, making aiming more complex but also allowing a projectile to be thrown over a wall.

Movement patterns: some weapons might move in strange patterns. For example, a waving sine movement could be applied.

Projectile count: you might shoot several projectiles at once. Important choices here are in what direction the different projectiles go (all somewhat forward, 360 degrees all around?) and whether all projectiles fire at the same time, or they come after each other in a series of shots.

Mines and bounces: what happens when a projectile hits the ground? Does it disappear, or explode with area of effect, or bounce away in another direction? Or does it stay on the ground like a mine, waiting to explode when an enemy is near? Also, if it does become a mine, is it a spider mine that jumps towards enemies that come near, or does it only attack who steps on it?

Detonation: when does the projectile explode? When it hits something, or when the player presses a button? The latter is mostly relevant in combination with area of effect and can be used with both mines and slow moving projectiles. Detonating a projectile by hand is a fun thing to do, but also often tends to somewhat slow down the game as the player watches every projectile fly.

Return to player: after a while or after a button press the projectile flies back towards the player. Might be used for a weapon that the player needs to retrieve before being able to use it again, or for a penetrating bullet that can hit enemies again on the way back.

Morphs: does the projectile change into something else at some point? Maybe if it hits a wall it bounces but becomes weaker, or if it flies long enough it grows, or if it flies through fire it gets an added damage over time fire effect?

Splits: whether the projectile can split into several separate projectiles. This can happen at a specific timing, or when hitting an opponent or wall. The newly spawned projectiles can also be more of the same projectile, or a different one altogether.

Who and where you hit

Friendly fire: whether you can hit your own teammates or not makes a big difference for the playstyle of the game.

Self-damage: weapons like rockets might damage yourself when they explode too near to you. An extreme example of this is Clunk's explode in Awesomenauts: he does massive damage to anyone near him, but also damages himself and can even kill himself this way. Another example is the famous rocket jump, where the knockback of the explosion of your own rocket allows you to jump higher.

Area of effect (AoE): normally an attack hits only one enemy, with AoE it can hit everyone in an area. AoE has several settings to play with: the size and shape of the area, and in some cases also the maximum number of opponents hit. A weapon can also be a combination: maybe the enemy that was hit gets 10 damage directly, and there's also an AoE effect that deals 5 damage to everyone near her. Be warned: area of effect can be very difficult to balance if the player sometimes faces many opponents, and sometimes only a few.

Penetrating: if you hit someone, does the bullet fly on and hit another target, or does it just end. Often seen variations to this are that a bullet has a maximum number of targets or deals decreased damage to consecutive targets. It might even change direction on hit, like with chain lightning.

Wall piercing: whether a weapon can shoot through walls or not. This makes a big difference to where an opponent will feel safe. In Awesomenauts only a couple of weapons can go through walls, and knowing whether there's an opponent in the field who has such a weapon is extremely important for successfully fleeing when low on health.

Buffing and debuffing

Buff: when you hit a teammate, she becomes stronger in some way. This can influence any parameter, like giving her a shield, increasing her health, reducing her cooldowns or increasing her damage output.

Debuff: this is an effect that makes an opponent weaker. This is the opposite of a buff and can again affect any aspect of the gameplay.

Cleanse: instantly removing the debuffs from a teammate.

CC immunity: this is a very specific type of buff that makes a player immune to things like stun, slow and snare, but doesn't make her immune to damage. "CC" stands for "Crowd Control", since such effects don't kill someone, but allow you to have some control over what others do.

Shield: another specific type of buff. A shield reduces damage you receive. This can be done in several ways: a percentage of damage can be removed, a fixed amount of damage per attack can be absorbed, or you can temporarily become invincible altogether. This makes a big difference: absorbing a fixed amount is weak against very strong attacks, but it also makes you invulnerable against weak attacks that are below the threshold, making this super effective against rapid fire of small bullets. A shield might also turn off once it has absorbed too much damage, allowing opponents to destroy the shield. Shields can then regenerate, turn on again after a cooldown, or be lost until picked up again.

Time bubble: slows down all time in an area, usually only for opponents. Everything takes longer, so movement is slower, bullets slow down and even cooldowns are longer. That makes this quite different from a normal slow, which only affects movement. This can also be inverted to make time go faster for teammates.

Invisibility: you can make yourself or a teammate invisible, or really hard to see. This is often frustrating for the enemy so it might be needed to limit how long you can be invisible or to give certain enemies a skill that allows them to see you even when invisible.

Phasing out: a weapon might make you disappear from the battlefield temporarily, making it impossible to receive or deal damage. It might also phase you out partially: maybe you can move through enemies temporarily, but you can still attack and receive damage.

Self (de)buff: all of these effects can also apply to yourself. They can be the main effect of a weapon, or a side effect. For example, maybe using some super powerful weapon drains your batteries and makes all other attacks have a longer cooldown for a while.

Armor and damage types

Damage and armor can have specific types. Often this means that you deal less damage against opponents who have a procentual resistenance against certain attacks. Some variations on this are:

Elemental: a weapon might deal a certain type of damage (fire, ice, poison, etc) and an armor might give extra protection against certain types of damage. An extreme example of this can be found in Titan Quest, where there are many types of damage and all items can give all kinds of resistances. For example, a bow might give a subtle 2% extra fire resistance.

Opponent type: some weapons might be extra strong or extra weak against certain types of opponents. Maybe poison has no effect against mechanical enemies, or ice has no effect against the undead. An example in Awesomenauts is that most special skills are very weak against turrets to avoid lame outranging tactics.

Piercing: if a weapon is piercing, it ignores the armor of the opponent and does full damage.

These things not only serve to add variation to the weapons and upgrades in a game: they can also help in balancing. Especially armor types are great for this. If it turns out that one specific class is way too strong against one specific other class, but everything else in the game is quite fine, then it can be really difficult to find a way to fix the balance between those two classes without destroying all the rest of the balance. Armor types are a great way to fix this: make one of the two classes undead and make the weapons of the other weak against undead. To the player this might feel like a general rule that makes sense, while in fact it's an extremely specific nerf.

Own movement

A weapon or skill might also include movement of the player herself. This greatly changes the feel of a weapon and has a large tactical impact on when to use a weapon.

Dash: attacks that combine with fast movement in a certain direction. A dash can go through enemies or stop as soon as you touch someone. Also important is when the dash does damage: whenever it touches someone (like Froggy G's dash in Awesomenauts) or only at the beginning end/or end of the movement (like Vinnie & Spike's dive).

Jump: whenever you perform this attack, you also jump up. Like doing an upward punch or overcharging your jetpack.

Groundpound: attacks that can only be done downward to the ground from a point in the air.

Pull: pull yourself towards and enemy or pull an enemy towards you. Which it is makes a big difference: the first might put you in the middle of a group of enemies, while the latter might for example pull an enemy in range of your own turret.

Teleport: an attack can let you or an enemy teleport towards a position. Maybe you teleport towards the position of the bullet, or towards an object you left behind before, or towards your own position 5 seconds ago.

Swap position: swap position with an enemy or teammate.

Movement limitations: an attack can also limit movement. Maybe you become really slow while carrying a heavy gun, or maybe you turn into a turret and can't move at all for a while.

Other

Chain: keeps an opponent chained to a certain point, but she has complete freedom within the range of the chain. This is a great variation to a snare, because it's much less frustrating to be limited to an area than to not be able to move at all. An opponent can also be chained to an object or another character, allowing you to drag someone along.

Weapons that add weaknesses: a great way to add more variation is to add special weaknesses to certain weapons. Maybe the player loses a bit of health whenever she shoots, or moves slower when holding a specific weapon. By adding weaknesses to a weapon you can make its strengths much more extreme without making a weapon instantly overpowered.

Summons: a weapon can also spawn a new AI character that can fight for you. If you do this, the summoned character can also have tons of upgrades, like Leon in Awesomenauts who can upgrade the AI of his clones to make them move around or attack. The character can be spawned near the player, or in the spot where a project hit something.

Transform: changes an enemy or teammate into something else. A famous example is the Warcraft spell that temporarily turns enemies into sheep that can't attack. The transformation might also be permanent: in Awesomenauts there's an upgrade that lets Genji morph normal droids into flying droids.

Vision: some weapons might let you see different things, like a scope on a sniper that let's you look far away, or heatvision, or a weapon that temporarily shows you invisible enemies.

Attack visibility: whether the enemy can see your attack coming, and how easily. A nearly invisible bullet is much harder to dodge than a big purple one. Also, if your attack has a clear charge animation then enemies can see it coming and have more time to dodge the attack.

That's it! I hope this list can help other game designers add variation to their weapons. At the very least I had fun making a list and have a nice reference now for my own game designs. ^_^

Sunday, 25 September 2016

An odd effect that can be very noticeable in 2D games is the distortion that happens when deforming a texture. Especially when tapering a polygon the art can become really weird. This has been a problem in our engine for years, but since strong taper isn't used often I didn't think it needed fixing. However, when I added trails last year the distortion became much more problematic and I had to look for a solution.

If you're not very familiar with how videocards work this looks really weird. To a human it is very clear how the texture should be squashed when tapering a polygon, why can't my computer figure this out?

The reason is that videocards only handle triangles. They don't really know what a square is so if you want a square, you need to create two triangles that happen to form a square together. Many 3D tools hide this, but that's what happening under the hood.

When applying this taper to a square that consists of two triangles we can see why the weird distortion is happening. Those two triangles aren't the same size anymore: one gets squashed much more than the other. Since half of the texture is on one triangle and the other half is on the other triangle, we get this ugly malformed texture.

Another way of looking at the problem is to look at the centre of the image. The middle of the diagonal corresponds to the middle of the texture. But if we taper, then the middle isn't quite the middle of our object anymore!

Does this mean all deformed polygons always look broken? Luckily not: this problem only occurs if the texture has a different shape. If you apply a tapered texture to a tapered polygon it will look fine. The problem occurs when applying a square texture to a tapered polygon.

Now that it's clear what the problem is, how do we solve it? My first thought was to do something with shaders and modifying the texture coordinates to compensate. Maybe I could do away with explicit texture coordinates altogether and define the texture placement through a formula instead? I expect this should be possible somehow, although handling more complex geometry like trails might be pretty hard. Also, this seems overly complex and might cost additional performance: calculating the texture coordinates in the pixel shader would cost additional fillrate. Fillrate is the main performance bottleneck in Awesomenauts so anything costly there is probably problematic. (Note this older blogpost about fillrate in Awesomenauts.)

There is a much simpler solution: more polygons! If you subdivide the quad into a bunch of smaller quads the distortion becomes much much less noticeable. After a couple of subdivisions the distortion is usually hardly visible anymore. It isn't actually gone, but if the player doesn't notice it then it's good enough!

Note that a much more efficient subdivision for this particular situation is also possible by not doing a grid-based subdivision. For example by adding a second diagonal, creating a cross with 4 triangles. (Thanks to Reddit users eggfruit and not_a_profi for pointing this out.) A grid-based subdivision is more flexible though as it can also be used for other things than just solving this particular problem.

Solving problems by throwing more polygons at them is generally not a good idea, since those polygons of course cost more performance. However, our games generally use very few polygons so that's not where the performance bottleneck is for us. Also, we don't have all that many objects that need this solution, so the performance impact is negligible. Here we see a common pattern in modern game development: computers are so fast now that many things that may seem like a performance waste really don't matter, unless you're making triple-A games and are trying to beat the competition in squeezing the most effects out of the hardware.

So there you have it: I've solved this problem by simple throwing more polygons at it! Adding an option for subdivisions to the Ronitech (our own engine) had been on my wishlist for quite a while, so this felt like a good excuse to finally do so. This not only fixes the texture distortion problem: now that we have this option we could also add other fun features, like an animatable bend. I have even been playing around in my spare time with using those subdivisions for 3D heightmaps to add depth to objects in Awesomenauts. My tests show this could look really awesome, but this is not in a state where there's anything to show yet. Maybe that will make for a nice future blogpost somewhere next year. ^_^

Saturday, 17 September 2016

Last week I discussed our tools for making trails. One problem I encountered with trails is that they often don't look very good when attached to a character. Player movement in a platformer like Awesomenauts is not very smooth: all of a sudden you jump, or land, or turn around, and this creates a trail with lots of angles in it. For some effects this is fine, but especially for wider trails a smoother, curvier look is much better. Today I would like to discuss the smoothing algorithm I implemented to solve this.

Before I continue, here's last week's overview video of our trails again, since it also contains some footage of the smoothing:

Key when smoothing a trail is that the trail should always remain attached to the emitter (in this case a character). If the smoothing makes the trail detach from the emitter just a little bit, it starts to look floaty and incorrect. An artist can work around this by fading out the trail near its start, but this is not always desired. This makes smoothing a challenge: the emitter follows the player's angular movement but the resulting trail should not.

The trail must also look stable: it can't suddenly pop from unsmooth to smooth. The conclusion of these two requirements is that we must apply the smoothing over time instead of immediately or in one step.

The smoothing algorithm I came up with turns out to be really simple. It was inspired by how subdivision surfaces work on 3D mesh smoothing, but without the subdividing.

The basic idea is this: for each segment of the trail I calculate the position in between its neighbouring segments, and then move the segment a little bit towards that centre. On a straight trail without corners this means nothing happens: the segment is already in between its neighbours. But on a corner this achieves exactly what we want: the corner moves inwards a bit, rounding the corner.

By repeating this process every frame with very small steps we get a curve that over time becomes ever smoother. At first it only processes the corners, but every frame it grows to smoothen further along the straight parts as well.

To control the smoothing I've provided our artists with two settings: one sets how strong the smoothing should be, and the other how long it should keep smoothing. If you let the smoothing take too long the trail keeps deforming and starts looking floaty and unstable. What usually works best is to use quite a strong smoothing for a very short amount of time (around 0.5 seconds). This way it stops quickly enough that the player doesn't notice significant instability.

An implementation note is that we ignore the first and last segment, since those have only one neighbour, which happens to also solve the problem that the curve needs to stay attached to the emitter. A small but important detail is that not only the first and last segment should be left out of the smoothing, but also the second and penultimate segment. This is because in my version of trails the start of the trail moves with the emitter, causing it to start really close to the second segment, which gives odd results when smoothing. Something similar happens with the final segment.

Now that I've explained how it works, here's a short video that shows the impact of the various settings that control the smoothing:

A downside of this algorithm is that the strength of the smoothing depends on the number of segments. The fewer segments there are, the stronger the effect is. This means that if an artist tweaks the number of segments after having set the smoothing, she'll need to tweak the smoothing again.

The smoothing algorithm I've described here today gets the job done, is easy to implement and costs little performance. It's nice how sometimes seemingly complex problems can have such easy solutions. :) Next week I'll discuss the texture distortion caused by deformed polygons, which is a problem for both trails and normal 2D sprites.

Sunday, 11 September 2016

Until recently most special effects in Awesomenauts were either animated by hand or made using particles. This seemed to give enough possibilities, until we played the gorgeous Ori and the Blind Forest, which has very crisp special effects. We analysed how they achieved this and one of the conclusions was that for many things that we would make with particles, they used trails. Trails are quite an obvious concept, but for some reason it had never seemed like a big problem that they weren't an option in our tools. So about a year ago I added trails to our engine.

How to implement trails seems quite obvious: it's just a series of triangle segments, what's interesting about that? However, when I started implementing trails it turned out there are quite a lot of different approaches imaginable. I went with one that's extremely flexible, but also sometimes slightly difficult to control for certain effects. Today I'd like to share how I built our trails tool and what the pros and cons are. But before I get to that, let's start with a video that shows the tools for our trails and what our artists have made with those.

The core idea of my implementation is that a trail is like a series of connected particles, called segments. Each segment has it's own position and velocity, and the trail is formed by creating polygons in between the segments. This is a fun approach: it makes things like gravity, waviness and wind really easy, since you can just apply those effects to each segment separately and then connect them. It also means that the trail automatically stretches when you move quickly, just like a trail should.

An interesting aspect is what to do when standing still. In the case of normal particles you would just keep creating them regardless of your own speed, but with a trail that isn't a good idea: connecting segments that are nearly on the same position results in jumpy segment orientations. My solution to this is to have a minimum distance between segments and only emit another segment if you've moved enough. Doing this naively does have a weird side-effect: you get small holes in between the starting point of the trail and the newest created segment. To avoid this I move the newest segment along with the position of the emitter until a new segment is made.

If no care is taken something similar would also happen at the end, where the tip of the trail just suddenly disappears when its segment times out. Here the solution is to smoothly move the timed out final segment towards the penultimate segment before removing it.

To avoid creating too many segments, the artist sets how many segments he needs. Since I already have this minimum distance between segments I figured this would also be a good way to limit the number of segments. However, I now bumped into another oddity: if you move really quickly you will create a new segment every frame. That means that the number of segments depends on the framerate. Our artists have fast computers and usually have vsync disabled so they often run the game at 200fps or more. They need to go out of their way to see what Awesomenauts looks like on 30fps or even 20fps. To avoid situations where artists unknowingly create effects that only look smooth and good on very high framerates, I've limited the maximum number of segments per second to 60. If on older netbooks the framerate drops below that you will still get less smooth trails, but the difference between 20fps and 60fps is much more acceptable than between 20fps and 200fps.

A fun option you get from treating segments as particles is that you can do more than just leave them. A normal trail only exists when you move, it's literally a trail. But why not shoot segments away, like you often do with particles? With some creativity this can be abused to create effects like flames (shooting segments upwards) or flags (shooting segments horizontally) and get a very dynamic look for them. I also added an option for gravity/wind to change the speed of a segment over time.

One of the most fun things when making tools is when artists (ab)use them to make things you didn't expect. Especially Ronimo artist Ralph is really good at this. He considers it his personal crusade to use every feature of our editors for something. The weirder the feature, the more Ralph considers it a challenge to use it for something pretty. So when making trails my goal was to feed the Ralph by making trails super flexible. For this reason I added four different ways of mapping textures to the trail, and two textures can be combined on a single trail:

When I showed the trails to our artists they immediately wondered whether they could make things like a hair or a cape with it. Doing so with a normal trail would result in a very elastic look: trails become longer and shorter depending on how fast you move, which is usually not desired when making something like a braid or cloth. To fix this problem I added a feature that gives the trail a maximum and minimum length, shortening or stretching it when needed. This turned out not to work too well. The minimum length gave really odd results, especially when the character is standing still, so I removed that feature altogether. The maximum length does work, but in practice it doesn't produce convincing hair or cloth movement, so our artists usually go back to hand-animating those. I think to make good hair or cloth you need a very different approach: those require a real physics simulation, instead of simply emitting segments like I do now.

On top of all of the things discussed so far we got a lot of additional flexibility for free: years ago I made the tools of the Ronitech (our internal engine) in such a way that almost anything is animatable, so our artists can change most of the settings of trails over time. This allows for a lot of fun additional possibilities, like varying the thickness of a trail over time, or making the colour pulsate.

My implementation of trails does have one main downside: textures aren't very stable. The constantly varying length of the first and last segments makes textures flicker a bit, even though I compensate to get the correct texture coordinates. Framedrops can also leave longer holes in between segments, which is not immediately a problem but again it makes textures slightly less stable. I think if I were to ever make another version of trails I would decouple the polygons from the segments, and just create the polygons at equal distances over the path laid out by the segments.

All in all I'm really happy with how our trails system turned out. The approach of treating a trail as connected particles makes some things slightly unpredictable, but it does give a lot of flexibility. I expect our artists will come up with some more unexpected uses of trails in the future.

In the next two weeks I will discuss two more specific aspects of trails: the smoothing algorithm I created to make angular movement produce very smooth and curvy trails, and how to solve the texture distortion that occurs when skewing a quad with a texture on it. Till then!