Bite Sized Game Development : Examples of NPCs in 2D platformers

0

Attached Files

The following files have been attached to this tutorial:

.capx

platformerai.capx

Download now 254.19 KB

Stats

4,403 visits, 6,610 views

Tools

Translations

This tutorial hasn't been translated.

License

This tutorial is licensed under CC BY 4.0. Please refer to the license text if you wish to reuse, share or remix the content contained within this tutorial.

Published on 6 Nov, 2014. Last updated 19 Feb, 2019

In this tutorial, I'm going to show you some different ways to make various forms of computer controlled characters and NPCs for 2D platformers. This will differ slightly from my previous tutorials in that the game will be more of a playable demo, a series of rooms that you enter when you play, and each room will demontrate a different kind of AI. This writeup is designed as a guide to be read while playing through the demo. This tutorial was made as part of Ludum Dare's October challenge, so while I will be releasing the .capx file available to all, please consider going to my store page and purchasing the template files to support me.

You can find the game here : https://dl.dropboxusercontent.com/u/245987915/PlatformerAI/index.html

Lets get straight on with explaining the first room.

Room 1 : Basic 'walkers'

For this room I will refer to the characters as 'walkers', and these first basic walkers will only have one thing they can do and have no interaction with the player. The most basic kind of walker in most platforming games move in one direction until they hit a wall, at which point they turn around. Even this basic kind of behaviour needs a few lines of events and an instance variable to implement. I have given the walker sprite the platformer behaviour, and for every tick, make the platformer behaviour move to the right. I have given the walker a boolean variable that is true when the walker should be walking right. Using the platformer 'has wall to' condition we can test if there is a wall immediately to one side of the sprite. When there is a wall immediately to the walkers right, we set the boolean false and the walker starts moving to the left, and when there is a wall immediately to the left, we set the boolean true and the walker starts moving to the right. This behaviour will form the basis for most of our further walkers.

The next example simply adds a drop that the walker can fall down to the previous example. Due to the way platformer behaviour works, no changes are needed here. As soon as the walker walks over the edge, gravity takes effect. This does not affect the walkers ability to turn when it hits walls. What I have added however, is a little teleporter that moves the walker back to the top of the area when it reaches the bottom. I have created two sets of particles and added two invisible squares that mark the exit and entrance of the teleporter. Once the walker reaches the 'In' square, I start a fade out, move it to the 'Out' square, and immediately start a fade in. This moves the walker but the fade also adds a nice little teleporting effect.

As this game is meant to illustrate examples, at this point I will add a switch that turns invisible blocks, like the one used in the teleporter above, visible. The switch simply turns all objects in the family visible or invisible.

The last walker in this room is a little cleverer and doesnt drop off the edge of platforms. Instead when reaching the end of a ledge he turns around and walks back in the opposite direction, just as if he'd hit a wall. The test for this is relatively simple. Just check wether the walker collides with a platform at an offset slightly in front of and below him. If he doesnt, theres a gap coming and we should toggle the boolean variable that turns him around.

The great thing about this behaviour in comparison to others like having an invisible sprite that checks for ledges is that by using a 'for all' loop we can check for all possible walkers in a single tick, and have them all check independantly wether they should turn or not. I have illustrated this by adding some more platforms in with several walkers on them.

There are several other basic walkers that you can implement that only have one possible action they can perform, such as jumping and climbing, all of these can be implemented through a bit of experimentation with the platformer behaviour.

Room 2 : Basic 'flyers'

Lets move on to the next room. In this room we're going to implement a set of 'flyers' that ignore gravity, and move in different ways. I'm going to create a family for the flyers, and give them all the bullet behaviour, which will be what we use to get most of the movement going. We could easily use the '8 directional movement' behaviour, but bullet gives a few more options for control and direction and so for these examples I will use that behaviour.

The first flyer will just move up or down, or left and right, and will simply turn around when it hits a wall. This first flyer is good for just providing a projectile that the player needs to dodge, its also easy to implement, in the bullet behaviour, just set 'bounces off solids' to yes. Thats it! To differentiate between the vertical and horizontal flyers I have simply added a boolean check on layout start that sets the inital angle of motion.

The 'bounces off solids' behaviour is actually very useful and flexible, as we can also use it for fliers moving diagonally. In the second example the fliers bounce around off walls, the angle changing whenever they hit a solid to send them at roughly 90 degree angles every time they hit a wall. This method does mean that sometimes the fliers go bouncing off at strange and unpredictable angles, especially if they get into a corner, so what if we want them to be predictable with every bounce?

We could turn off the behaviour, but a few more short events should fix the issue while still letting us use the 'bounces off solids' behaviour to do most of the work. We know that the only four angles this flier should have are 45, 135, 225 and 315, so we simply tweak the angle of motion every time it is not one of these values. The events to do this are in the screenshot below :

We can also change their initial bounce direction by adding a local variable and changing the angle of motion based on the value of this variable at layout start.

So we have a lot of flyers that do well within square or rectangular rooms with no other obstacles, but what if the level we have for them is a little more complex then that? The next flyer I'm going to demonstrate is a flyer that follows straight walls. This flyer will move in its given direction until it hits a wall, at which point it will change its direction to move parallel to that wall. I will produce two different flyers for this, one that will turn clockwise when it hits a wall, and another that will turn anticlockwise. To implement this, simply make sure 'set angle' in the bullets property is set to no, and on collision with a wall, send the flyer clockwise (angle of motion + 90) or anticlockwise (angle of motion -90). How do we tell which is going clockwise and which is going anticlockwise? Just put in a boolean instance variable that we can change. You could even make two separate sprites and give them individual events if you wanted to colour them differently.

The last flyer is a strange kind of flyer that you see now and then in various kinds of games : the 'wave' flyer. This is actually very easy to implement as well, because there is a sine wave behaviour built in to Construct 2! So, we're going to add another sprite, keeping it in the same family as the others, and give it the 'sine' behaviour. Why do we keep it in the same family as the others? Because the sine behaviour moves our sprite along only one plane of movement, which can be vertical or horizontal, and we might still want to have a sprite move in a certain direction in addition to moving in a wave motion. From here, the sine behaviour gives us countless options to change how our sprites move. However dont forget for each sprite that uses a sine behaviour, modify the bullet behaviour where necessary so that it does what you need it to do.

There are a huge number of combinations you can implement, so I have made a few as an example, and I will briefly explain each one as follows : Flyer 5 and 6 both are both normal sine wave flyers, flyer 5 moves horizontally (bullet angle of motion of 0) and has its wave move in a verticle direction (set by the sine behaviour) and flyer 6 moves vertically but has its wave move horizontally. Both flyers have their bullet angle of motion increased by 180 degrees whenever they hit a wall. Flyer 7 has no bullet speed. This means that it will always return to a central point. Every time it completes a wave cycle, it changes direction. Flyer 8 is a modified version of flyer 5 that uses the 'triangle' motion. Flyer 9 is a modified version of flyer 6 that uses the 'square' motion.

Thats it for basic flyers! We've covered quite a lot of different methods of moving flyers here.

Room 3 : Advanced Walkers

Ok, its time to add some walkers that react a little more dynamically to the environment and the player. The first example is an edit of the original 'basic walker' that walks towards the player character if he is visible to them. Luckily for us, there is a behaviour for that! The line of sight behaviour should handle this nicely, its a great little behaviour that allows you to adjust range, cone of vision and wether or not its blocked by solids and the like. In my example, the range is quite short and is blocked by solids. I get the walker to walk towards the player by modifying the walkers walking boolean dependant on wether the player is in sight and then secondly wether the player's X value is higher or lower than the walkers. When the player goes out of sight, the walker reverts to its normal behaviour.

Next, lets add a walker that will jump small gaps but turns around if it detects a gap in a platform it cant jump across. So, lets reuse the events from the beginner walker that turns around when it reaches the end of a platform, but with a few changes. When the walker encounters an edge it first tests to see if there is another platform within jumping range. The jumping range, or distance a walker can jump, is determined by a few things, including but not limited to the jump strength, walker max speed, and the gravity strength upon the walker. By modifying these you can change the jump range, but remember to change where the walker checks for a platform to land on. This set of events isnt exactly complex but it requires a lot of individual checks to be made before you can either tell the walker to go right, left, or jump.

Lets combine this gap jumping behaviour with the following behaviour. Im going to add a button so I can toggle wether the minion follows the player or not. If the walker is following, the walker should use a completely different set of events. The events for this should be a simple copy of the events of the follower walker with some changes. For instance, if the minion is following the player, and it is at a jump it cannot make, I want it to stay near the platform edge. This can be achieved by telling the minion to simulate control on both left and right in the same tick, which will cause the minion to stop moving as they cancel each other out. I have also placed areas in the gaps that cause the minion to respawn if it falls through. Like before, this method isnt particulary complex but it does require a lot of checks to be made, as shwon in the screenshot below :

Lets see what else we can make sprites do when they see a player. Going back to using the Line of Sight behaviour, heres a set of sprites that enter an 'enraged' state whenever the player is within the conditions set by their individual line of sight behaviours. The first simply moves much faster if it can see the player. This is achieved simply by increasing the max speed of the sprites platformer behaviour when the player is within sight. The next walker has a short charge up time when the player gets within sight range, and then dashes towards the player. The walker then has a short cooldown period where this dash is no longer possible. The third walker waits for the player to enter its line of sight, then jumps at the player until the player is directly under (or above) it, and then pauses before hurling itself down and landing at the spot the player was standing. The dash and jump walkers both use a 'DashTimer' instance variable that allows for control of various pauses and time periods of actions, as well as the cooldown period for each walker.

The last example in this room is a pair of very basic combat AIs. This AI uses the jump attack enrage behaviour from before to pit two opponents against each other. If one walker hits the other with the jump attack while the other isnt attacking, the loser is destroyed and respawns out of their teleporter. On line of sight, we random a number out of 100. On a more than 75, the walker jumps to its target. Otherwise, the walker does nothing for a short while before the next random. These two walkers will constantly fight and respawn while this layer is active.

Room 4 : Homing Flyers

In this room, I'm going to show some different ways of how you can have a flyer move towards a target. In a game this could be the player, a NPC or an item. Most of these examples are going to be modified versions of flyers from Room 2. Each example will have one or more flyers and a target, each time a flyer reaches a target, it will be moved to somewhere else in the room immediately.

The first example is the flyers that move in 90 degree straight lines only, (so will only have an angle of motion of either 0, 90, 180, or 270) and turn in 90 degree angles when they hit walls. I have put in two flyers here to illustrate turning anticlockwise and clockwise when they hit walls, but either one will turn towards the target if they get on roughly the same horizontal or vertical plane. This can be accomplished relatively easily by checking if the flyer's X or Y values are close to the targets X or Y values. We dont tell the flyer to change direction when the X or Y values are equal, because at higher speeds the flyers will pass right past this point in a single tick. The faster the flyer is going, the bigger the boundary that needs to be checked, but you do want to keep the boundary as small as possible in most cases.

Once the flyer is within that boundary, we need to tell it what angle to move to by comparing its position to the target again. As there are only four possible angles of motion these flyers should accept using this behaviour, this can be accomplished relatively easily using the events below :

The next examples are all flyers that move directly to the point of the target, and we will make them more advanced as we go through the room. The first flyer of this type will simply use the pathfinding behaviour to move towards the target. This is very simple thanks to the fluidity of the pathfinding behaviour in Construct 2. I've built a small maze for the next flyer and its target. To begin with, the flyer will simply move over the maze obstacles straight towards the target, but I have also placed a button that resets the position of the flyer and modifies the pathfinding behaviour on the fly so that it navigates around the maze to get to the target. This behaviour can be toggled on or off at will using the button.

The next flyer slightly modifies this behaviour and adds an enrage behaviour similar to the walkers in the previous room by adding the Line of Sight behaviour. The line of sight distance is very small by design, but when the target gets close enough to trigger the line of sight behaviour, the speed of the flyer is greatly increased. You could also easily add a delay into this event section to show the flyer 'charging' its dash.

The last homing flyer will behave in the exact same way, using the pathfinding behaviour, but will instead follow the player when he enters the maze. To make this work we will need to have a boolean variable (that is only true when the player is inside the maze) trigger the pathfinding behaviour to create a new path to follow several times a second, not every tick, but often enough that it looks fluid when following the player. I still want to turn clipping on and off at will for this example, so I've created a new family with two flyers in it, and given both of them a second animation frame to better show wether they are following the player or not. When the player enters the maze by colliding with the lasers, the animation frame changes and the flyer begins to follow the player, when the player leaves the maze, he collides with the lasers again, toggling the boolean off, and the flyer's path reverts back to where it started in the top corner. Pressing the button simply destroys the flyer and spawns the other one, which will immediately start following the player according to its behaviours, until the button is pressed again or the player leaves the maze.

Room 5 : Shooters

This last room is going to use a mixture of the previous rooms sprites and add various ways for them to shoot at targets. The sprites they shoot will be simple bullet sprites that I'll make a separate family for to manage most of their behaviours. Always remember when making the family and adding instance variables and behaviours for them to keep in mind what you want to control. A few examples : Do you want the bullets to pass through, bounce off, or simply stop at walls? How far do you want the bullets to travel? Are there any other restrictions you want to place on the bullets? All these questions you should be prepared to answer when you initally make the family's variables.

The simplest kind of shooter is one that just shoots in the direction it is facing. The first example is a basic walker and flyer, both of which just move left and right horizontally and fire a bullet once every while. The button toggles wether or not bullets are destroyed when they collide with a wall. All of the bullets have shared behaviours and variables, but changes to these variables are made when each bullet is created, depending on wether the state of the switch is on or off.

The next shooters will fire at a target. There is a useful behaviour in Construct 2 that would be useful for this, called Turret, but I am not going to use it. Why? Mostly due to the way the Turret behaviour works, its 'On Shoot' trigger activates when a sprites angle is pointing at its target, and for this example I do not want my sprites to rotate at my targets, among other things. For this example I simply make each sprite spawn a bullet after a given time period, and then immediately set the bullets angle towards to target. I have also set a button to show how I can have bullets bounce of walls, and extend the length the bullets travel before destroying them.

Now we have 2 sets of sprites that can shoot, we might as well put them up against each other! This area has fast moving and shooting flyers up against slow moving and shooting walkers, as an example of basic AI fighting against each other that the player could watch or get involved with. The interesting part is that they all shoot at slightly random intervals, and there are two buttons that can either destroy all the sprites or spawn additional flyers and walkers into the area. The targeting works by giving each sprite their own timer. When the timer expires, they pick a random enemy sprite and spawn a bullet in their direction. The timer for shot cooldown then resets, and the random modifier is added here.

The last example is a minigame that is started by the usual button press. The objective of the game is simply to survive as long as possible without getting hit by a bullet from the pair of hunters working together. Pressing the button spawns the player into the maze, and the sprites in the maze will immediately begin to hunt the player using their behaviours. The flyer constantly shoots and uses the pathfinding example from the previous room to keep track of the player, while the walker has quite a long cooldown on its shots and moves constantly towards the X value of the player to attempt to keep them in the upper half of the maze. Both sprites have their shots controlled by a timer that restarts when it expires and fires a shot at the players current position. Thats pretty much it! Theres plenty of ways this little game could be made harder : Speeding up the enemies, adding more enemies, making the maze harder are all good ideas.

Wrapping up

So there you have it, five rooms of examples of how to build various computer controlled NPCs in Construct 2. Hopefully this has given you some ideas for your own games! Once again, this tutorial was made for the Ludum Dare October challenge, so please consider going to my store page and purchasing the files here : Store page

.CAPX

platformerai.capx

Download now 254.19 KB
  • 0 Comments

Want to leave a comment? Login or Register an account!