Game Design : A Look at Luck in Game Design

he luck vs. skill aspect of games is one which is fairly central to good design—indeed, it's something we've covered before. But before we worry about trying to balance luck and skill, we really need to ask: what is chance, and to what extent is it necessary in a game? Furthermore, how can we implement chance in a way that feels rewarding rather than punishing, and use it to improve rather than detract from the overall playing experience?
It's nearly impossible to create a game without luck. A game without luck isn't really a game—something like "who's the tallest" or "who has the most fingers" doesn't really involve any sort of challenge. These are simply measurements that the players are unable to change, and so are unlikely to provide much entertainment. A game must have an element of uncertainty—something like "who can balance on one leg the longest", while not terribly in depth, is at least not predetermined. Even when one player is better, their success is not always guaranteed.
For many games, we use cards, dice, or a random number generator to create this unpredictability. But not all games use randomisation tools, and a serious strategy game like chess still requires an element of randomness: this element comes from the players themselves. Players are unpredictable, and will often adjust strategies and tactics on the fly, based on what they consider the best probable outcome. This is why, despite being a fairly static game, chess games can vary wildly: no two players approach the game the same way.
The reason humans can provide chance to chess is because chess is incredibly complex – in fact, we can describe chess as a complex game. Unfortuntely, unlike concepts such as "flow" or "zero sum game", the term "complex game" isn't a recognised term. Since we'll be talking about complexity a lot, we should probably define what we mean by it.
So what is a complex game?
If we look at Tic-tac-toe, we can see a game with fairly simple rules. There are nine spaces, players place an X or an O, trying to make a straight line, and the game is always over in nine moves or fewer. It's fairly easy to predict the results of a Tic-tac-toe game, even before the first move is made—assuming the two players play "correctly", then the game will always end in a draw.
We can say then, that tic-tac-toe is hard to justify as a complex game. In fact, Tic-tac-toe has been solved, which is to say that we've calculated every set of possible moves, and essentially proven the best moveset. To make matters worse, humans are capable of "solving" a tic-tac-toe game without much mental agility.
Compare this to chess, which has 64 pieces and six different types of piece, each with their own moveset, special moves such as castling and en passant, and a ruleset that means a game could (technically) last forever. Given these conditions, it's perhaps unsurprising that chess has never been solved, even by the most powerful computers.
So, essentially, a complex game is one which has not been solved, or that cannot be solved by the players.
This addendum "cannot be solved by the players" is important. It means that games can continue to be fun, assuming the players are incapable of solving them. This is why Four-in-a-row (also known as Connect 4) remains a fairly popular game; although computers have solved it, when players sit down they are unlikely to be capable of calculating the perfect game in their heads, so they play non-optimally. Tic-tac-toe, while trivial for most players, is still a good game for young children who are unable to plot out every move in their head. Complexity is subjective.
So why is this important? Because a non-complex game (a simple game) is a boring game. If the game is not complex, then it is solvable. If it is solvable, then the outcome is predetermined; all the player is required to do is work out the best moveset, and they've won. And at that point, they may as well go back to playing "who has the most fingers".
A small sidenote here, a solvable game can be better described as a puzzle. And while puzzles are popular (many newspaper print daily crossword puzzles), a puzzle is only fun up until it's been solved – which is why crossword enthusiasts generally don't sit and solve the same crossword over and over. Theres certainly nothing wrong in deciding to make a puzzle game, but be aware of what it is you're aiming for, and how that will impact replayability.
So, when we look at games like chess or Tic-tac-toe, we can see they are all strategy games with no inbuilt randomisation: basic strategy games. There are, however, many games which do use dice, cards or other tools as an inbuilt mechanic, like snakes and ladders or poker. As most of these games can't be considered complex, the inclusion of these dice or cards is necessary to prevent the game from being solvable. If, in the game of Snakes and Ladders, rather than rolling, players chose a number between one and six spaces to move every turn, then even children would quickly work out that "always choose six unless you land on a snake" is an optimal strategy.
Of course, adding randomness does not automatically make a solvable game good. In fact, you simply change the aim from "find the solution" to "find the best probable outcome". You still essentially have a puzzle game, except that the win condition is not guaranteed. Too much randomness is just as bad as none; here again Snakes and Ladders is an obvious example. Almost no-one other than children plays the game, as it lacks any sort of interactive challenge and, therefore, people see it as ultimately pointless.
So why does a game like poker continue to work? Poker is, essentially, a series of mini puzzles. You are given a hand, and you have to "solve" how probable it is for you to win. You can then bet on your hand, based on how likely you are to win the game.
This is an oversimplified view of poker, and if this was all there was to the game, it would be fairly boring. It would be trivial to write a program to calculate the odds (although people do that anyway), and simply run it to maximise your wins.
The fun of poker comes from player interaction: from bluffing and confidence. You are not required to bet on a good hand, and you are able to bet on junk. In fact, this is arguably what the game of poker is truly about; the cards are simply there to facilitate this, and to provide a fresh round of lying every few minutes. By adding the random element, we've eliminated player knowledge, which means that we can use uncertainty as a game mechanic. Players are required to perform based on what they know, and it is the combination of calculating winning odds and outfoxing other players that lets poker maintain its fanbase.
So, although we've been talking about about traditional gaming, computer games use exactly the same principles of design. Games like Tetris or Bejewelled can be considered simple (with added randomness), and games like Starcraft or Team Fortress can be considered complex.
In almost all games, there is a certain puzzle-like quality. Even in an RTS or FPS, players are constantly making decisions based on optimal play: should I build tanks or planes? Should I choose the machine gun or grenade launcher? Should I turn left or right? Like in chess, the player attempts to make decisions based on what they think will result in the best outcome. The randomness isn't (generally) provided by computer dice, but by the choices of the players in the game. Players are trying to outwit each other, as well as outskill them.
In fact, its possible to argue that the only randomness in a PvP game (like an FPS or RTS) should come from the players themselves. As we've talked about before, crits in TF2 are a subject of much contention in the playerbase—to summarise, any shot in TF2 has the possibility of being a crit shot. Crit shots require nothing more than the roll of a dice, and any damage dealt by a critical bullet will be twice or three times as much normal, which causes crits to be frequently lethal. While new players may enjoy the thrill of randomly getting a kill, "pro" players will see the crit mechanic as unnecessarily spoiling their skills.
The range of numbers we use for randomness also play a large effect on how things pan out. If a rifle deals 90-110 damage a shot, then if we have 150 health the random element is really a flavour effect: no matter what happens, we need to be shot twice to die. However, if we have 100 health, than a rifle will randomly kill us in one shot half of the time. Despite there only being a small range in randomness, the numbers used matter a great deal.
So why is it that "pro" players bemoan crit systems (and improperly implemented damage ranges) whereas "casual" players don't? The answer, simply, is player expectation.
A pro player will have played their game of choice a lot. They will know it inside and out. They will know what damage they can take, what they can deal out, and what the outcome of any situation should be. And while they may sometimes judge things poorly, it is generally due to underestimating the opponent's skill level, or making bad split-second decisions.
So when a pro player enters a battle, and they are instantly gibbed by a bullet for no reason other than luck, they might feel cheated. They knew what they wanted to happen, but because of an electronic dice roll, they were instantly killed instead.
New players will generally not feel this sting as sharply; they don't know the game as well, they have fewer expectations of what should happen, and so they can enter a battle not really expecting to win. To them, battles are as much a learning experience as a test of skill.
This randomisation destroying expectation is something that can happen in almost any game with randomness. When you're waiting for a line block in Tetris, and the computer instead gives you six S blocks in a row, the player might feel a little cheated. The popular game Puzzle Quest (essentially a bejewelled clone with RPG elements) received many player complaints about "cheating AI"; there are enough forum threads about it that the developers had to specifically come out and say that the AI doesn't cheat.
So why does it feel this way? Why are so many players upset over randomly falling jewel colours? Because the randomness is subverting player expectations. When a player goes into a game, they are (generally) expecting to be challenged, but they're also expecting that if they play well, they can win. When the game randomly throws some bad numbers at you, and you immediately lose, then you can feel cheated. You had an idea of how the game was going to play, and despite your best efforts, you were defeated—not by your own lack of skill, or superior opponent strategy, but by electronic dice. This, for most players, is incredibly infuriating.
This "luck subverting player expectation" extends into all sorts of game. In fact, the more luck involved in a game, the more likely it is to be frustrating. RPGs are a notable example, especially because of crit systems. Crits systems often seem like a fun little addition, but by the numbers they will almost always punish the players. This is because:
  1. Players are, generally, expected to defeat most enemies.
  2. Crits add randomness to battles.
  3. Randomness in battle means unpredictable results.
  4. Therefore, players will (occasionally) win battles they should have lost, but more often:
  5. Players will lose battles they should have won.
This is, of course, assuming that the encounters are designed or tailored towards the player. Some RPGs simply throw the player at giant monster and be done with it; however, as professional designers, we should be looking to ensure that the game is tailored towards our players, rather than just throwing some dragons in and calling it a day.
The other problem then, assuming we have designed our combats carefully, is that a crit system over-favours the player. Imagine if, after a harrowing journey through time and space, the hero of our game walks up to the ancient demon terrorising the planet and kills him in one (critical) blow. Its not quite the epic battle of legend, and is likely to leave the player feeling underwhelmed and unsatisfied. A player wants a challenge, and denying them that challenge because of randomness is unlikely to provide satisfaction.
In the case of Puzzle Quest, whether or not the AI actually was cheating isn't actually important: what is important is that to some players, it felt like the AI was cheating. The lucky streaks gotten by the player are likely to be ignored (due to their expectation of winning anyway), but having your victory snatched away by a series of unfortunate dice rolls may seem unfair and punishing.
So how do we fix things? It might seem like so far all we've really said is "randomness is bad". And essentially, that's true. We're professional games designers; we shouldn't be doing things randomly. Every decision the player makes should be the result of a carefully crafted experience, and putting in randomness can endanger that.
When we look more closely at it, we realise that randomness can be added for two primary reasons:
  • To make the outcome unpredictable, or
  • to generate content.
Let's examine these:
As we talked about in our previous article, players enjoy winning.
As we talked about here, randomness somewhat replaces the need for skill.
Therefore, adding randomness to a game allows (in some sense) bad players to win against good players. In a game with no randomness, a good player will always win against a bad player.
Because of this, having this unpredictability can be an important part of a game: it allows bad players to influence the game, and (hopefully) become better. If a player is constantly matched up against superior opponents and is losing, chances are they will quickly lose interest.
However, good players will often dislike this randomness, and will often be put off by a game which "punishes" their skill.
So how can we fix this?
Well, one option is to have an Elo rating system. This essentially gives players a number based on their skill level: beat a grandmaster, and your Elo rating goes up; lose games to newbies, and your score will probably go down. It originated as a way for chess players to measure their skill, but many MOBAs (Multiplayer Online Battle Arenas, such as League of Legends and Defense of the Ancients) do this, so that when you enter a battle you are (theoretically) placed with people around the same skill level. Some first-person shooters have also attempted this, allowing players to rebalance the teams if one side is continually getting crushed.
Another option would be to have a handicap system. Not too dissimilar to an Elo system, a handicap system allows players to give themselves an artificial advantage based on their skill level. Fighting games will often do this, giving the weaker player a variable health and damage output bonus. Although a handicap system might not solve all skill imbalance issues (it's easy to imagine online players abusing a system like this), it's a good way to allow casual players to compete more equally with their hardcore friends.
In both these cases, you can reduce the randomness of innate game elements, leaving the players as the only randomness generators.
The problem with generating content randomly (or gameplay elements, like in the game of Tetris's falling blocks) isn't so much that we're generating unpredictable content; it's that, often, certain sequences of random elements are extremely punishing to the player.
If, in Tetris, the player is waiting for a line block, but we only generate S blocks for the rest of the game, then the player will have every right to be annoyed. And, while it's improbable, it can happen.
In other games, such as a dungeon crawl RPG, we might have a 1% chance of generating a boss every time a monster spawns. If, by chance, we generate three bosses in a row, then the player might find themselves in an unwinnable battle.
By the same token, we might go through the game and never generate a boss. This could make the game incredibly easy, or (if the bosses drop equipment upgrades) incredibly hard. In either case, randomness has essentially destroyed our players' enjoyment of the game.
In certain cases, randomness can completely remove a player from the game. In the popular collectible card game Magic: The Gathering, players build decks that require a combination of lands (power sources) and spells to defeat their opponents. The use of lands is an important balancing mechanism: a simple goblin might require one land in play, while a mighty dragon might require ten. However, if the player happens to draw no land cards, then they are unable to play anything; they are essentially forced to sit there with zero options until their opponent defeats them. While it's possible to mitigate this to some extent, it's a serious design flaw that a non-insignificant number of game losses are the result of bad luck, rather than being outplayed.
People feel loss more strongly than they feel gain. It's an interesting psychological phenomenon; consider these two scenarios:
  1. You are given $1,000. I ask if you want to gamble on a coin toss: heads you win an extra $1,000, tails you don't. Alternatively, you can just have an extra $500 (no coin toss required).
  2. You are given $2,000. I ask if you want to gamble on a coin toss: tails you lose $1,000, heads you don't. Alternatively, you can just give back $500 (no coin toss required).
In general, people tend to take the guaranteed extra $500 in the first case, but gamble on the coin toss in the second... even though the outcomes for gambling are the same in each scenario! (Do the maths: whether you choose to flip the coin or not, and whether the coin comes up heads or not, the amount of money you end up with in the end is the same regardless of whether we're talking about Option 1 or Option 2.)
This means that, if you have a mechanic in game which randomly rewards or punishes they players, the losses will, psychologically, outweigh the gains. If the gamble is optional then it opens up extra avenues of gameplay, but a forced gamble will mostly feel like punishment.

A final problem with generating content randomly is that it can be very difficult to generate content which is interesting. A great example of this is MMOs; World of Warcraft has dozens upon dozens of dungeons, each of which can take hours to complete, and weeks to successfully master. However, once the dungeons are mastered, they (arguably) offer few variations and little replayability, save for the obvious grind for equipment. In Anarchy Online, the number of designed dungeons was tiny: however, players could enter randomised dungeons. In theory, no two dungeons the players encountered would ever be the same: however, in practise, every dungeon felt the same. Because dungeons were randomised, they had no narrative structure or overall design concept. Instead of feeling unique, every dungeon felt the same.
A lot of this is down to how many rules are put in place and how the generation is implemented: Nethack and Spelunky both use randomly generated levels, and have massive fan bases. The generation of rules for interesting map design is, however, a slightly different issue from randomly generating gameplay elements, and is probably best left for another discussion; it suffices to say that a good designer should be aware of the limitations of generating maps. We can still apply much of this randomness discussion to the generation of these maps, however.
So where does this leave us? Well, sometimes we can actually just remove randomness from a game entirely. In the case of an RPG, instead of spawning a boss 1% of a time, we can spawn them after every 100 kills. In the case of collectible card games, one of Magic's competitors (Versus system) solved the issue of players needing land by making every card playable, face-down, as a land instead of a spell; this meant that you would never find yourself "stuck", while maintaining the momentum that lands crucially provided.
However, a total removal of randomness can often be an overzealous case of throwing the baby out with the bath water. In the example of the RPG, making a boss spawn every 100 kills exactly is likely to make them too predictable, and when a game gets too predictable, it becomes a puzzle. A better option would be making a boss spawn somewhere between every 50 and 150 kills. This means that bosses are still within a random range (making them hard to predict), but aren't so random you can get attacked by three at once.
This use of carefully controlled numbers is pseudorandom generation. There are many ways to do it: in Tetris, if we spawn an L block, then for the next three blocks we "re-roll the dice" once if an L block is spawned again. Normally, the L block has a one-in-seven chance of spawning, but giving it a re-roll makes it a 1/49 chance for those threee turns. It can still happen, but is much less likely.
This isn't the best way, of course: there are many ways to generate numbers randomly, ranging from simple re-rolls to weighted random numbers; plus, sometimes, in a case like Tetris, just leaving it as a one-in-seven chance to generate any block might be the best option.
If we do use randomness, we also have the opportunity to introduce seeds. This simply means that the random numbers we use in our game aren't actually random; the "random" sequence is entirely defined by a number called a seed. In Tetris, it appears that we can't predict what blocks are going to fall; however, if we seed a tetris game with the number 42, and we start off with square, L block, T block, square, then every game that uses the 42 seed will begin with those blocks. Seeds aren't used often in gaming (Minecraft and FreeCell being two notable examples), but can be a nice addition.
The ability to seed randomness comes from the fact that computers aren't actually capable of generating random numbers: often, they simply take a base number (such as the time in milliseconds), and then perform a calculation to get a "random" number. By ensuring the base number is the same every time, the calculations will give us the same "random" numbers.
On Windows, FreeCell game #11982 is the only impossible game.
On Windows, FreeCell game #11982 is the only impossible game.
The alternative to removing randomness is to use more of it. This might seem crazy initially, but can actually be extremely effective: roll two dice, add them together, and you have a one in six chance of getting a seven. Roll 2,000 dice, add them together, divide by 1,000 (and round), and you will almost always get a seven. In this case, using so much randomness has almost entirely removed randomness.
At the end of the day, randomness isn't inherently evil; it all comes down to perception. Players want to be be challenged, or to have an interesting experience, and there's nothing challenging or interesting about throwing a bunch of random monsters at a player, with little regard to whether they live or die. By tempering the randomness, we can craft the results we want, and hopefully make a game which interacts with the player, rather than ignoring them.
Randomness is generally a bad way to solve conflict, because (by its very nature) it creates unpredictable results. Randomness can also be a poor way to generate content, but is often the only sensible way to approach it; imagine designing a Tetris game which had a list of every block that should drop, in order.
When randomness does occur, it should generally favour the player (which is hard to achieve in a player vs player environment). If game elements are generated randomly, they should allow the player to react to a worst case scenario in a way that still allows a reasonable chance of success.
However, we have to accept that sometimes randomness is necessary. RPGs would be a lot less exciting without dice to determine combat. Board games, in particular, are unlikely to resolve the issue anytime soon: throwing 1,000 dice and averaging out just isn't practical for a game of Snakes and Ladders.

And of course, the final big caveat: if we remove randomness entirely, are we making a game, or a puzzle?

0 comments:

Game Design : 3 Questions to Help You Finish Your First Game

Many gamedevs find that their first game is the hardest one to finish. I've worked as a consultant on almost a dozen "first" games with budding independent development companies, and gathered all of the lessons learned from those experiences here. After going over each question, I'll also give you a quick anecdote about how I (or someone I worked with) learned that lesson the hard way.
Preview image icon: Question by Henry Ryder, from The Noun Project.
Picking too big scope is the most common pitfall that I've seen developers fall into. You think up this great game idea, eagerly get started, and soon find yourself with too many features left to add, not enough of the main game done, and very little time left.
Having a large game scope isn't necessarily a bad thing; if you are developing for fun, or have a highly organized design system, it can be great. But for your typical small team that hopes to make a profit from their pursuit, trying to build a large game can be a early death sentence.
The easiest way for new developers to get past this phase is to take the time to break down their game design into layers. When working with developers, I like to have them create a to-do list of the core game. This consists of everything necessary for the game to be finished in an acceptable state - no additional features, unneeded side quests, or extras. This list serves as the absolute minimal amount that needs to be done by the release date and still allow you to feel proud of your game.
Then, I have them create a "polish" list. This includes everything needed to make the game more complete, but not required to make it playable and fun. Typically these are things such as additional graphics, transition animations, more animation frames, additional or ambient sound effects, more audio tracks, improved UI, particle effects, advanced lighting, and more advanced AI.
The final list, "extras", consists of anything that they would like to add that can wait to be added in at the end, or skipped if there is not enough time to implement it. These items typically consist of unlockables, social or multiplayer features (unless they are an integral part of the game), additional side quests, and so on.
The first major game that I worked on was called The Fallen. I was 16, and two of my friends and myself decided we were going to make the best MMO ever.
Our idea was to have multiple persistent servers with shared character and enemy instances (so that each server would be a different, massive area, and herds of the enemies, called "fallen", could migrate between them), in a giant FPS-meets-RPG built on the Torque engine.
We put a little more than a year's worth of work into the project before giving up, managing to get two servers up and running, and having as many as 27 players per day during our fever pitch.
The project was way too large for the three of us to handle on our own, and the game's quality suffered. There was no audio and barely any dialogue; the enemies were terribly modeled 3d atrocities; and all of the players were the standard Torque 3D model.
Questions to help you finish your first game
Every player was the standard Torque model, with different colors for different classes. (Image from Torque's FPS tutorial.)
Had we planned things out prior to getting started, and kept the scope of our game smaller at the beginning, we might have stood a chance of finishing it. Instead, we quickly became overwhelmed by a behemoth of files and badly programmed (and terribly commented) code that only a group of teenagers can create.
The second most common pitfall for projects is being over-ambitious.
When preparing to start a project, ask yourself: is this something that I can do? Am I able to do tons of 3D programming and collision detection? Do I know enough about 3D modeling and lighting? Can I make this within a reasonable time frame?
If you find that you can't, don't be disheartened! Do your research and see if there are any libraries, APIs, tools, or anything at all that would make it possible for you to actually finish your game. If you can't find anything to make your game's creation easier, then consider simplifying or modifying the design so that it is.
Personally, I like to take more complicated game designs and use them as end goals, designing a few smaller games leading up to it, each allowing me to learn a new skill needed to complete the final game. I urge developers that I work with to do the same. If you want to make a MMORPG, first try making a standalone RPG, then move onto a game with basic multiplayer, and then take a stab it. Having those needed skills under your belt will make developing your game much easier, and means you're much more likely to finish it!
Questions to help you finish your first game
EvolvingPoet's first game, Tiny Hero.
When I finally decided to try to make game development into a full-time pursuit by starting an LLC, I planned on making a game that was not possible within our current constraints. The problem wasn't that it was technically challenging; it was more that there was a huge amount of content that needed to be created and very little time in which to do so. This ultimately lead to a solid week of crunching and the production of a sub-par game.
Questions to help you finish your first game
It took weeks for me to try and bury this game, and I still couldn't completely wipe it from the internet.
To make matters worse, having too little time to complete the game led to us having to spend even more time doing damage control so that we wouldn't be hated forever in the indie dev community. (That doesn't actually happen - most everyone involved is super nice.)
Questions to help you finish your first game
When making your first game as an independent developer, finances often get overlooked. For hobbyist developers or those who have another job to support themselves, this isn't a huge deal, but developers that quickly jump into being full-time game makers often find themselves in over their heads.
Assuming that your first game is going to make enough for you to live on is not a safe bet, and the same goes for your second game as well (and maybe even a few after that). Being an indie dev means that you are running a business, and, on average, small businesses take around two years to become profitable. If you have two years' worth of living saved up, then this isn't too big of a problem, but for most people, this is a huge hurdle. It is a good idea to have a part-time or full-time job to supplement your gamedev income for a while, if only for the safety net it provides.
Having realistic sales targets is another important caveat. Unless you are extremely skilled and extremely lucky, you will not be selling thousands of copies of your game. Depending on the market and scope of the project your sales targets will vary, but I find that most commonly developers I work with will be aiming to get around 700 to 1,000 sales. While these may sound like low numbers, once you actually release your game you will find out just how difficult those targets are to reach.
If your game has a price point of $10 and you sell it through the typical online stores, you can expect to bring in around $5,600 from those 700 sales. Considering that most developers that I work with are two-person teams who create a game every two to three months, you can expect maybe $1,250/month per person after expenses. In that time, the teams are typically working full-time hours (most of the time, many more). That works out to around $7.80/hour, or just above minimum wage - and again, that's if you're lucky.
It isn't all doom and gloom, though; you can make a living off of making independent games, just don't expect to do so with your very first one. On average, I see that developers start to make a decent wage from their work at some point around the release of their third game.
This particular lesson ended up ruining a client of ours. They aimed to create a huge Zombie-based MMO (think DayZ), and the prototype that they had was great - fun to play, technically sound, immersive, and all that jazz - but they hadn't done all of their financial homework.
Screenshot from Day-Z, not Zombie MMO.
Screenshot from Day-Z, not Zombie MMO.
Through a lack of planning they managed to find themselves ten months into a project with very little to show, very little money left, ten angry employees, and one irate investor. They brought me in to try to clean up the situation, and switching to a more need-based development approach, the game started to make headway very fast, and even earned a bit of money through its alpha.
(They then decided that an unrelated factor was the reason for the improvement, not the change in development systems, and upon changing back to their previous method of development, quickly found themselves out of business.
These questions are here to help you be realistic in the pursuit of your first game, not to dissuade you from making it. A lot of would-be game makers whom I've met hear me talk about these things and decide that the whole pursuit is just too hard - but it's not! You just need to prepare yourself and plan ahead so that you can actually finish your game.
Some relate game development to running a marathon, and that's an apt metaphor.Running is not something that I have any experience in (most people would say that I'm phobic to most exercise in general), and if I tried to run a 5K, there is an extremely small chance that I would actually finish. Game development works the same way: if you don't build up your endurance and skill over time, it is going to be an arduous journey, and you will find yourself either giving up part way through, or reaching the end exasperated and out of breath.
Making your first game is a monumental step in your development career, and hopefully getting it done will be a little easier now. If you keep the game's size reasonable, its complexity within your ability, and your financial expectations realistic, then there is no reason that you shouldn't be able to finish.
Now go make some games!

0 comments:

Game Design : Creating Dynamic 2D Water Effects in Unity

In this tutorial, we're going to simulate a dynamic 2D body of water using simple physics. We will use a mixture of a line renderer, mesh renderers, triggers and particles to create our effect. The final result comes complete with waves and splashes, ready to add to your next game. A Unity (Unity3D) demo source is included, but you should be able to implement something similar using the same principles in any game engine.
Here's what we're going to end up with. You'll need the Unity browser plugin to try it out.

Click to create a new object to drop into the water.
In his tutorial, Michael Hoffman demonstrated how we can model the surface of water with a row of springs.
We're going to render the top of our water using one of Unity's line renderers, and use so many nodes that it appears as a continuous wave.
Create 2D Dynamic Water Effects in Unity (Unity3D)
We'll have to keep track of the positions, velocities and accelerations of every node, though. To do that, we're going to use arrays. So at the top of our class we'll add these variables:
1
2
3
4
5
float[] xpositions;
float[] ypositions;
float[] velocities;
float[] accelerations;
LineRenderer Body;
The LineRenderer will store all our nodes and outline our body of water. We still need the water itself, though; we'll create this with Meshes. We're going to need objects to hold these meshes too.
1
2
GameObject[] meshobjects;
Mesh[] meshes;
We're also going to need colliders so that things can interact with our water:
1
GameObject[] colliders;
And we'll store all our constants as well:
1
2
3
4
const float springconstant = 0.02f;
const float damping = 0.04f;
const float spread = 0.05f;
const float z = -1f;
These constants are the same kind as Michael discussed, with the exception of z—this is our z-offset for our water. We're going to use -1 for this so that it gets displayed in front of our objects. (You might want to change this depending on what you want to appear in front and behind of it; you're going to have to use the z-coordinate to determine where sprites sit relative to it.)
Next, we're going to hold onto some values:
1
2
3
float baseheight;
float left;
float bottom;
These are just the dimensions of the water.
We're going to need some public variables we can set in the editor, too. First, the particle system we're going to use for our splashes:
1
public GameObject splash:
Next, the material we'll use for our line renderer (in case you want to reuse the script for acid, lava, chemicals, or anything else):
1
public Material mat:
Plus, the kind of mesh we're going to use for the main body of water:
1
public GameObject watermesh:
These are all going to be based on prefabs, which are all included in the source files.
We want a game object that can hold all of this data, act as a manager, and spawn our body of water ingame to specification. To do that, we'll write a function called SpawnWater().
This function will take inputs of the left side, the width, the top, and the bottom of the body of water.
1
2
public void SpawnWater(float Left, float Width, float Top, float Bottom)
{
(Though this seems inconsistent, it acts in the interest of quick level design when building from left to right).
Now we're going to find out how many nodes we need:
1
2
int edgecount = Mathf.RoundToInt(Width) * 5;
int nodecount = edgecount + 1;
We're going to use five per unit width, to give us smooth motion that isn't too demanding. (You can vary this to balance efficiency against smoothness.) This gives us all our lines, then we need the + 1 for the extra node on the end.
The first thing we're going to do is render our body of water with the LineRenderer component:
1
2
3
4
5
Body = gameObject.AddComponent<LineRenderer>();
Body.material = mat;
Body.material.renderQueue = 1000;
Body.SetVertexCount(nodecount);
Body.SetWidth(0.1f, 0.1f);
What we've also done here is select our material, and set it to render above the water by choosing its position in the render queue. We've set the correct number of nodes, and set the width of the line to 0.1.
You can vary this depending on how thick you want your line. You may have noticed that SetWidth() takes two parameters; these are the width at the start and the end of the line. We want that width to be constant.
Now that we've made our nodes, we'll initialise all our top variables:
01
02
03
04
05
06
07
08
09
10
11
12
xpositions = new float[nodecount];
ypositions = new float[nodecount];
velocities = new float[nodecount];
accelerations = new float[nodecount];
 
meshobjects = new GameObject[edgecount];
meshes = new Mesh[edgecount];
colliders = new GameObject[edgecount];
 
baseheight = Top;
bottom = Bottom;
left = Left;
So now we have all our arrays, and we're holding on to our data.
Now to actually set the values of our arrays. We'll start with the nodes:
1
2
3
4
5
6
7
8
for (int i = 0; i < nodecount; i++)
{
    ypositions[i] = Top;
    xpositions[i] = Left + Width * i / edgecount;
    accelerations[i] = 0;
    velocities[i] = 0;
    Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));
}
Here, we set all the y-positions to be at the top of the water, and then incrementally add all the nodes side by side. Our velocities and accelerations are zero initially, as the water is still.
We finish the loop by setting each node in our LineRenderer (Body) to their correct position.
Here's where it gets tricky.
We have our line, but we don't have the water itself. And the way we can make this is using Meshes. We'll start off by creating these:
1
2
3
for (int i = 0; i < edgecount; i++)
{
    meshes[i] = new Mesh();
Now, Meshes store a bunch of variables. The first variable is pretty simple: it contains all the vertices (or corners).
Create 2D Dynamic Water Effects in Unity (Unity3D)
The diagram shows what we want our mesh segments to look like. For the first segment, the vertices are highlighted. We want four in total.
1
2
3
4
5
Vector3[] Vertices = new Vector3[4];
Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);
Vertices[1] = new Vector3(xpositions[i + 1], ypositions[i + 1], z);
Vertices[2] = new Vector3(xpositions[i], bottom, z);
Vertices[3] = new Vector3(xpositions[i+1], bottom, z);
Now, as you can see here, vertex 0 is the top-left, 1 is the top-right, 2 is the bottom-left, and 3 is the top-right. We'll need to remember that for later.
The second property that meshes need is UVs. Meshes have textures, and the UVs choose which part of the textures we want to grab. In this case, we just want the top-left, top-right, bottom-left, and bottom-right corners of our texture.
1
2
3
4
5
Vector2[] UVs = new Vector2[4];
UVs[0] = new Vector2(0, 1);
UVs[1] = new Vector2(1, 1);
UVs[2] = new Vector2(0, 0);
UVs[3] = new Vector2(1, 0);
Now we need those numbers from before again. Meshes are made up of triangles, and we know that any quadrilateral can be made of two triangles, so now we need to tell the mesh how it should draw those triangles.
Create 2D Dynamic Water Effects in Unity (Unity3D)
Look at the corners with the node order labelled. Triangle A connects nodes 0, 1 and 3; Triangle B connects nodes 3, 2 and 0. Therefore, we want to make an array that contains six integers, reflecting exactly that:
1
int[] tris = new int[6] { 0, 1, 3, 3, 2, 0 };
This creates our quadrilateral. Now we set the mesh values.
1
2
3
meshes[i].vertices = Vertices;
meshes[i].uv = UVs;
meshes[i].triangles = tris;
Now, we have our meshes, but we don't have Game Objects to render them in the scene. So we're going to create them from our watermesh prefab which contains a Mesh Renderer and Mesh Filter.
1
2
3
meshobjects[i] = Instantiate(watermesh,Vector3.zero,Quaternion.identity) as GameObject;
meshobjects[i].GetComponent<MeshFilter>().mesh = meshes[i];
meshobjects[i].transform.parent = transform;
We set the mesh, and we set it to be the child of the water manager, to tidy things up.
Now we want our collider too:
1
2
3
4
5
6
7
8
colliders[i] = new GameObject();
colliders[i].name = "Trigger";
colliders[i].AddComponent<BoxCollider2D>();
colliders[i].transform.parent = transform;
colliders[i].transform.position = new Vector3(Left + Width * (i + 0.5f) / edgecount, Top - 0.5f, 0);
colliders[i].transform.localScale = new Vector3(Width / edgecount, 1, 1);
colliders[i].GetComponent<BoxCollider2D>().isTrigger = true;
colliders[i].AddComponent<WaterDetector>();
Here, we're making box colliders, giving them a name so they're a bit tidier in the scene, and making them each children of the water manager again. We set their position to be halfway between the nodes, set their size, and add a WaterDetector class to them.
Now that we have our mesh, we need a function to update it as the water moves:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
void UpdateMeshes()
    {
        for (int i = 0; i < meshes.Length; i++)
        {
 
            Vector3[] Vertices = new Vector3[4];
            Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);
            Vertices[1] = new Vector3(xpositions[i+1], ypositions[i+1], z);
            Vertices[2] = new Vector3(xpositions[i], bottom, z);
            Vertices[3] = new Vector3(xpositions[i+1], bottom, z);
 
            meshes[i].vertices = Vertices;
        }
    }
You might notice that this function just uses the code we wrote before. The only difference is that this time we don't have to set the tris and UVs, because these stay the same.
Our next task is to make the water itself work. We'll use FixedUpdate() to modify them all incrementally.
1
2
void FixedUpdate()
{
First, we're going to combine Hooke's Law with the Euler method to find the new positions, accelerations and velocities.
So, Hooke's Law is [Math Processing Error], where [Math Processing Error] is the force produced by a spring (remember, we're modelling the surface of the water as a row of springs), [Math Processing Error] is the spring constant, and [Math Processing Error] is the displacement. Our displacement is simply going to be the y-position of each node minus the base height of the nodes.
Next, we add a damping factor proportional to the velocity of the force to dampen the force.
1
2
3
4
5
6
7
8
for (int i = 0; i < xpositions.Length ; i++)
        {
            float force = springconstant * (ypositions[i] - baseheight) + velocities[i]*damping ;
            accelerations[i] = -force;
            ypositions[i] += velocities[i];
            velocities[i] += accelerations[i];
            Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));
        }
The Euler method is simple; we just add the acceleration to the velocity and the velocity to the position, every frame.
Note: I just assumed the mass of each node was 1 here, but you'll want to use:
1
accelerations[i] = -force/mass;
if you want a different mass for your nodes.
Tip: For precise physics, we would use Verlet integration, but because we're adding damping, we can only use the Euler method, which is a lot quicker to calculate. Generally, though, the Euler method will exponentially introduce kinetic energy from nowhere into your physics system, so don't use it for anything precise.
Now we're going to create wave propagation. The following code is adapted from Michael Hoffman's tutorial.
1
2
float[] leftDeltas = new float[xpositions.Length];
float[] rightDeltas = new float[xpositions.Length];
Here, we create two arrays. For each node, we're going to check the height of the previous node against the height of the current node and put the difference into leftDeltas.
Then, we'll check the height of the subsequent node against the height of the node we're checking, and put that difference into rightDeltas. (We'll also multiply all values by a spread constant).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
for (int j = 0; j < 8; j++)
{
    for (int i = 0; i < xpositions.Length; i++)
    {
        if (i > 0)
        {
            leftDeltas[i] = spread * (ypositions[i] - ypositions[i-1]);
            velocities[i - 1] += leftDeltas[i];
        }
        if (i < xpositions.Length - 1)
        {
            rightDeltas[i] = spread * (ypositions[i] - ypositions[i + 1]);
            velocities[i + 1] += rightDeltas[i];
        }
    }
}
We can change the velocities based on the height difference immediately, but we should only store the differences in positions at this point. If we changed the position of the first node straight off the bat, by the time we looked at the second node, the first node will have already moved, so that'll ruin all our calculations.
01
02
03
04
05
06
07
08
09
10
11
for (int i = 0; i < xpositions.Length; i++)
{
    if (i > 0)
    {
        ypositions[i-1] += leftDeltas[i];
    }
    if (i < xpositions.Length - 1)
    {
        ypositions[i + 1] += rightDeltas[i];
    }
}
So once we've collected all our height data, we can apply it at the end. We can't look to the right of the node at the far right, or to the left of the node at the far left, hence the conditions i > 0 and i < xpositions.Length - 1.
Also, note that we contained this whole code in a loop, and ran it eight times. This is because we want to run this process in small doses multiple times, rather than one large calculation, which would be a lot less fluid.
Now we have water that flows, and it shows. Next, we need to be able to disturb the water!
For this, let's add a function called Splash(), which will check the x-position of the splash, and the velocity of whatever is hitting it. It should be public so that we can call it from our colliders later.
1
2
public void Splash(float xpos, float velocity)
{
First, we need to make sure that the specified position is actually within the bounds of our water:
1
2
if (xpos >= xpositions[0] && xpos <= xpositions[xpositions.Length-1])
{
And then we'll change xpos so it gives us the position relative to the start of the body of water:
1
xpos -= xpositions[0];
Next, we're going to find out which node it's touching. We can calculate that like this:
1
int index = Mathf.RoundToInt((xpositions.Length-1)*(xpos / (xpositions[xpositions.Length-1] - xpositions[0])));
So, here's what going on here:
  1. We take the position of the splash relative to the position of the left edge of the water (xpos).
  2. We divide this by the position of the right edge relative to the position of the left edge of the water.
  3. This gives us a fraction that tells us where the splash is. For instance, a splash three-quarters of the way along the body of water would give a value of 0.75.
  4. We multiply this by the number of edges and round this number, which gives us the node our splash was closest to.
1
velocities[index] = velocity;
Now we set the velocity of the object that hit our water to that node's velocity, so that it gets dragged down by the object.
Note: You could change this line to whatever suits you. For instance, you could add the velocity to its current velocity, or you could use momentum instead of velocity and divide by your node's mass.
Create 2D Dynamic Water Effects in Unity (Unity3D)
Now we want to make a particle system that'll produce the splash. We defined that earlier; it's called "splash" (creatively enough). Be sure not to confuse it with Splash(). The one I'll be using is included in the source files.
First, we want to set the parameters of the splash to change with the velocity of the object.
1
2
3
4
float lifetime = 0.93f + Mathf.Abs(velocity)*0.07f;
splash.GetComponent<ParticleSystem>().startSpeed = 8+2*Mathf.Pow(Mathf.Abs(velocity),0.5f);
splash.GetComponent<ParticleSystem>().startSpeed = 9 + 2 * Mathf.Pow(Mathf.Abs(velocity), 0.5f);
splash.GetComponent<ParticleSystem>().startLifetime = lifetime;
Here, we've taken our particles, set their lifetime so they won't die shortly after they hit the surface of the water, and set their speed to be based on the square of their velocity (plus a constant, for small splashes).
You may be looking at that code and thinking, "Why has he set the startSpeed twice?", and you'd be right to wonder that. The problem is, we're using a particle system (Shuriken, provided with the project) that has its start speed set to "random between two constants". Unfortunately, we don't have much access over Shuriken by scripts, so to get that behaviour to work we have to set the value twice.
Now I'm going to add a line that you may or may not want to omit from your script:
1
2
Vector3 position = new Vector3(xpositions[index],ypositions[index]-0.35f,5);
Quaternion rotation = Quaternion.LookRotation(new Vector3(xpositions[Mathf.FloorToInt(xpositions.Length / 2)], baseheight + 8, 5) - position);
Shuriken particles won't be destroyed when they hit your objects, so if you want to make sure they aren't going to land in front of your objects, you can take two measures:
  1. Stick them in the background. (You can tell this by the z-position being 5).
  2. Tilt the particle system to always point towards the center of your body of water—this way, the particles won't splash onto the land.
The second line of code takes the midpoint of the positions, moves upwards a bit, and points the particle emitter towards it. I've included this behaviour in the demo. If you're using a really wide body of water, you probably don't want this behaviour. If your water is in a small pool inside a room, you may well want to use it. So, feel free to scrap that line about rotation.
1
2
3
4
        GameObject splish = Instantiate(splash,position,rotation) as GameObject;
        Destroy(splish, lifetime+0.3f);
    }
}
Now, we make our splash, and tell it to die a little after the particles are due to die. Why a little afterwards? Because our particle system sends out a few sequential bursts of particles, so even though the first batch only last till Time.time + lifetime, our final bursts will still be around a little after that.
Yes! We're finally done, right?
Wrong! We need to detect our objects, or this was all for nothing!
Remember we added that script to all our colliders before? The one called WaterDetector?
Well we're going to make it now! We only want one function in it:
1
2
void OnTriggerEnter2D(Collider2D Hit)
{
Using OnTriggerEnter2D(), we can specify what happens whenever a 2D Rigid Body enters our body of water. If we pass a parameter of Collider2D we can find more information about that object.
1
2
if (Hit.rigidbody2D != null)
{
We only want objects that contain a rigidbody2D.
1
2
3
      transform.parent.GetComponent<Water>().Splash(transform.position.x, Hit.rigidbody2D.velocity.y*Hit.rigidbody2D.mass / 40f);
    }
}
Now, all of our colliders are children of the water manager. So we just grab the Water component from their parent and call Splash(), from the position of the collider.
Remember again, I said you could either pass velocity or momentum, if you wanted it to be more physically accurate? Well here's where you have to pass the right one. If you multiply the object's y-velocity by its mass, you'll have its momentum. If you just want to use its velocity, get rid of the mass from that line.
Finally, you'll want to call SpawnWater() from somewhere. Let's do it at launch:
1
2
3
4
void Start()
{
    SpawnWater(-10,20,0,-10);
}
And now we're done! Now any rigidbody2D with a collider that hits the water will create a splash, and the waves will move correctly.
Create 2D Dynamic Water Effects in Unity (Unity3D)
Advertisement
As an extra bonus, I've added a few lines of code to the top of SpawnWater().
1
2
3
4
gameObject.AddComponent<BoxCollider2D>();
gameObject.GetComponent<BoxCollider2D>().center = new Vector2(Left + Width / 2, (Top + Bottom) / 2);
gameObject.GetComponent<BoxCollider2D>().size = new Vector2(Width, Top - Bottom);
gameObject.GetComponent<BoxCollider2D>().isTrigger = true;
These lines of code will add a box collider to the water itself. You can use this to make things float in your water, using what you've learnt.
You'll want to make a function called OnTriggerStay2D() which takes a parameter of Collider2D Hit. Then, you can use a modified version of the spring formula we used before that checks the mass of the object, and add a force or velocity to your rigidbody2D to make it float in the water.
In this tutorial, we implemented a simple water simulation for use in 2D games with simple physics code and a line renderer, mesh renderers, triggers and particles. Perhaps you will add wavy bodies of fluid water as an obstacle to your next platformer, ready for your characters to dive into or carefully cross with floating stepping stones, or maybe you could use this in a sailing or windsurfing game, or even a game where you simply skip rocks across the water from a sunny beach. Good luck!

0 comments: