Note: A completed project file for this tutorial can be found at the end of this article.
In this tutorial, we will create the artificial intelligence (AI) for a convincing enemy slime for a platformer-style game. We will be covering some more advanced features and behaviors in hyperPad, so this tutorial assumes some basic knowledge with the hyperPad editor. If you think you need to familiarize yourself more with making a game from scratch, you can check out the Complete Guide - Creating a Platform Game tutorial first.
In order make the slime look "intelligent", it should do more than simply move back and forth, and should be somewhat aware of its surroundings.
Sounds exciting! But before we get started, we should first detail how the enemy slime should behave:
- Instead of walking, the slime will jump left or right to move.
- The slime will jump in the direction of the player.
- If the player is too far away, the slime will not move (idle).
- The slime will take away the player's health on contact.
- The slime is defeated if the player jumps on top of it.
Now that we know what we want to do, let's begin!
Part 1: Project Setup
In this tutorial, we will start a new project from scratch to keep things simple. Make a new project and set it to "Side View", you can leave the other settings as their default values.
With your new project created, go to the Asset Library and grab the "Platformer Starter Pack" by hyperPad in the asset shop. (It's free!)
Now that we have some assets to work with, go ahead and place some ground blocks and create your world. If you're not already there, go to the Asset Library and tap on "Downloads", then "Platformer Starter Pack".
In the image below, some ground blocks ("Grass World" folder) have been placed and a background ("Backgrounds" folder) has been set.
Now that we have a nice scene to work with, let's add the Player and Slime objects. Go to the Asset Library and open your "Downloads" folder, then the "Platformer Starter Kit" folder if they are not already open.
We'll use the "Green Spaceman" as a player, so tap on the green spaceman asset to see all of its animation cycles. Be default, we want the player to simply stand, so tap on the "Stand" animation and place it on the scene.
In the same folder in the Asset Library, there is also a "Slime Pink" animated character. Perfect for us to use as a slime enemy! Tap on the slime asset and choose the "Move" animation and place a slime on to your scene with your player character.
So far, so good! We now have our player object and slime object, ready for us to use. But if we were to play our project now, we would not be able to interact with the game at all. So, we will have to create some logic to bring some life to our game.
Part 2: Player Controls
Before we can get to work giving our slime enemy some life, we should first have some player controls for the green spaceman.
Tap on the green spaceman and set its physics mode from Wall to Physical, which will let the player move freely. Then, tap the "Behaviors" button to open the Behavior editor for our player character so we can add some controls.
In the behavior editor for our player character, you should already see a lonely Play Animation behavior. We can leave that behavior alone, but we can give it some friends.
Under the Interaction section, place down anywhere a Joystick Controlled behavior as well as a Jump with Button behavior. With these behaviors, our player is now ready to control. But, it's lacking some animation.
Tap on the Joystick Controlled behavior and set the "Joystick Left Animation" to the "Walk" animation cycle in the Green Spaceman asset. Do the same with "Joystick Right Animation"
Now our player character walks, but still doesn't animate when they jump.
Tap on the Jump with Button behavior and change both the "Falling Animation" and "Jumping Animation" to the "Jump" animation cycle.
If we play our game now, we can move and jump around! Next up, we can get to work on our slime enemy.
Part 3: Slime Movement
As we detailed in the introduction section of this tutorial, we want our slime to jump towards the player. In order to do this, our slime needs to know where the player is in the world. Because of this, we should add a Tag to our player character, that way our behaviors can retrieve our player character in any scene.
Tap on the player character, and select the Tags tab on the bottom of the properties view. Type "Player" in the text field at the top of the tags menu and press the add (+) button next to it. Now our player character is tagged with the "Player" tag.
Similar to what we did with the player character, tap on the slime and set its physics mode from Wall to Physics. Then, tap on the Behaviors button to open the slime's behavior editor.
In the behavior editor, go to the Custom behavior section and drop a Timer behavior into the editor.
Tap on the Timer behavior and set its interval to 2 seconds.
This will be the start of our logic for our slime's artificial intelligence.
The idea here is that every 2 seconds, our slime will "think" of what to do next, such as: Should the slime Jump? If it chooses to jump, what direction should it jump?
Next, go to the Transform behaviors section and place down a Get Position behavior. Tap on it and set its target to the "Player" tag we created earlier by tapping on the Tags tab on the bottom of its properties view and selecting "Player". Now connect the Timer to the Get Position behavior.
Now we know where our player is in the world! To determine whether the player is left or right of the slime, we also need to know where the slime is in the world, too.
Drag another Get Position behavior into the editor. By default, the behavior is already targeting our slime so we don't need to do anything else besides connecting it to our logic branch. Go ahead and connect the Get Position for the player to the Get Position for the slime.
Optional: To make things easier to understand, I have renamed my Get Position behaviors to clearly indicate what they are tracking: "Get Position Player" and "Get Position Slime". This is optional, but if you too would like to change the behavior names, you can do so by tapping on the behavior and tapping its name at the very top of the properties view.
So far, what we have is that every 2 seconds, the slime retrieves the position of the player character and itself. Now that we have that information, we can determine if the slime should jump left or right.
Under the Logic tab, drop and connect an If behavior. In its properties, set the first value to the first Get Position's ("Player" tag) x-position by dragging the behavior's output value into the field in the If properties. For the second value, set it to the second Get Position's (slime) x-position. Lastly, set the If behavior's condition to "Is Less Than" (<).
Note: An object's x-position is the value representing where the object is in the world on its x-axis. (Left and right)
So, what does all this mean? The If behavior will check to see if the player's x-position is less than the slime's x-position. If the above is true, then it will execute the next connected behavior. What this essentially means, is that if the player is further left than the slime, then behavior execution will continue.
Alright, we now have the logic set up to have the slime jump left, so let's make the slime do exactly that!
Under the Physics behavior section, drop and connect an Apply Force behavior to the If behavior. In its properties, set the Force to: x = -0.5, y = 1. Then, set the multiplier to a sizable number, 200 should do it.
What we have now, is if the slime finds the player further to the left than itself, it will jump upwards (y = 1 × 200) and a bit to the left (x = -0.5 × 200).
Try it out! Play the game and test to make sure everything's working. What you should see is the slime jumping to the left every 2 seconds, as long as the player is to the left of it. If you move the player to the right of the slime, it should stop jumping until you move to the left of it again.
Note: If your slime is not moving, go through the previous steps to make sure you didn't miss anything. Make sure the behaviors are connected properly, and that your slime physics mode is set to Physics (movable) instead of Wall (non-movable). Also, soon we'll fix the issue where the slime will sometimes flip on its side or upside-down.
Great progress so far! If your slime is jumping around as expected, then we're ready to continue. Let's finish up its movement behaviors by allowing the slime to jump to the right, too.
Back in the slime's behavior editor, duplicate the If behavior. Drag and snap the new If behavior on the right of the original If behavior. This will turn it into an "Else If" behavior.
Duplicate the Apply Force behavior as well, and connect that to the new Else If behavior.
In the Else If's properties, change the condition from "Is Less Than" (<) to "Is Greater Than" (>). This will have the opposite effect of the first If. If the first If behavior is not true, it will then check to see if the Else If is true instead. Meaning, that if the player is not to the left of the slime, it will check if the player is right of the slime, and continue executing behaviors connected to the Else If instead.
You should now have a behavior branch that looks something like this:
In the second Apply Force (under the Else-If), change its properties so that its force is x = 0.5, instead of x = -0.5. Now, instead of jumping to the left, the slime will jump to the right.
Play the project and give it another try! Now you should see the slime jumping in which ever direction the player is. Excellent!
One thing you may have noticed while play testing, is that if you collide with the slime, it can sometimes fall on its side or go upside-down. We want the slime to stay upright, so let's fix that real quick.
Back in the slime's behavior editor, go to the Transform behavior section and drop a Lock Rotation behavior into the editor. No need to connect it to another behavior, we want it to lock its rotation as soon as the slime is created so you can leave it as is. Once you've done that, try the game again. This time you should have a tough a time trying to knock the slime on its side.
You may have also noticed that the slime's animation is playing too quickly. If this is happening, tap on the Play Animation behavior in the slime's behavior editor and change the Frames Per Second to a much smaller number. 6 frames per second looks pretty good.
Nice! Now our slime is not only moving around nicely, but it looks great, too. For the most part, our slime's movement logic is complete.
However, we also want the slime to only jump if the player is nearby. That way if we build a bigger world, slimes far away won't immediately try to jump towards and attack the player. In the next part, we will look at adding another condition to block the jumping logic if the player is too far away.
Part 4: Slime Idling
In this part, we will implement some slime behaviors that will make it idle (that is to say, not move) when the player is too far away.
To achieve this, we need to find a way to measure the distance from our slime to the player. Conveniently, there are behaviors available to do just that! Let's get to work.
Open the slime's behavior editor and drop down a Calculate Distance behavior from the Logic section. In its behavior properties, the first object is already set to our slime. For the second object, go to the Tabs menu and select the "Player" tag.
This behavior, when executed, will calculate the distance in meters from our slime to the player. Using that information, we can see if the player is too far away by checking if it surpasses a certain distance.
Place down another If behavior from the Logic section and connect it under the Calculate Distance behavior. In the If behavior properties, set the first value to the Calculate Distance's output value. Set the condition to "Is Less Than" and the second value to 15.
Now what we have, is a way of checking if the player is too far away by seeing if the distance between the slime and player is less than 15 (meters). If that condition is false, then the player is too far away and the If behavior will not execute the next behavior.
Now that we have our new condition ready to use, disconnect the Get Position (slime) and old If behavior from the previous part of the tutorial and make some room for our new condition. You can disconnect behaviors by tapping and holding the connections between behaviors.
With the new distance condition in place, connect the Get Position (slime) to the Calculate Distance behavior then connect the new If behavior to the old If behavior. You should now have something like this:
It's starting to look pretty complicated now, but the idea is relatively simple. Now what we have, is before we even check to see what direction the slime should jump, we first check to see if the distance between the slime and the player is less than 15 (meters).
If the player is closer than 15 meters, then it will continue on and check which direction we should jump, as usual. If the player is further than 15 meters away, we don't continue and the logic ends there until next time the slime "thinks" again (2 seconds later).
Give the game a try! If you get too close to the slime it should start jumping towards you, but if you move too far away it should stop moving and wait until you get close to it again.
Our slime is beginning to look pretty menacing now, but it's still missing a way of damaging the player. In the next part of this tutorial, we'll give the player a health bar and implement some logic to deal damage to the player.
Part 5: Attacking the Player
As detailed in the introduction section, we will implement slime attacks by making the slime hurt the player when it touches the player. To get started on implementing a way for the slime to attack the player, we'll first need to give the player some health.
Since it is placed on the Scene UI layer, if we choose to expand our world and have the screen follow the player around, it will stay in the same spot on the screen.
You may place it anywhere you see fit for it on the screen. I have placed mine in the center of the screen near the bottom.
When you're done finding a good spot for your health bar, we can get to work adding a way to remove health from the health bar. Return to the Main layer where your slime is and open its behavior editor again.
In a nice empty spot in your slime's behaviors, drop down a Collided behavior found under the Object section. In its properties, set the second object to the "Player" tag.
Under the Custom behavior section, drop down a Broadcast Message behavior and connect it under the Collided behavior. In the Broadcast Message behavior properties, set the Event Key to "player hit", and set the value to -10.
Remember: Event keys are case sensitive! For simplicity, this tutorial will use lower-case for event keys. You may choose to use capitals if you prefer, but be sure to remember which letters are capitals for later.
Now we have a way of detecting when the player should take away health. When the player collides with the slime, a message for the event "player hit" will be broadcast with a value of -10. We will use the -10 to tell the health bar to "add" -10 health, and since the value is negative it will remove 10 health.
We still need to add that logic to the health bar, so let's do that now. Exit the behavior editor for the slime then in the main editor tap on the Scene UI layer then tap on the health bar. Open the behavior editor for the health bar.
In the health bar's behavior editor, drop down a Receive Message behavior from the Custom section. In its properties, set the Event Key to "player hit". (Remember it is case sensitive!)
This behavior will be our listener for the "player hit" message sent when ever the player hits the slime. When this behavior detects a broadcast message for "player hit" anywhere in the game, it will execute.
In the Add to Health Bar properties, drag the output value from Receive Message to the "Value to add" field. That value will be the -10 value we set earlier in the Broadcast Message behavior on the slime. This way, if we choose to make other enemies deal different amounts of damage, all we have to do is change the value on the broadcast to what ever else we want and the health bar will use that value instead!
Ensure that "Event On Full" is off, and "Event On Empty" is on. This means that if the health bar reaches zero health, it will execute the next behavior.
In the Load Overlay settings, change the selected overlay to the "Game Over" overlay that is automatically made for you when you make a new project.
Play the game and try it out! Now, whenever you make contact with the slime, it should drain your health bar. When the health bar reaches zero, it's game over.
Cool! Now we have a way of damaging the player and lose the game.
Something feels a bit off, though. We aren't giving much feedback to the player that they've been hit besides removing health from the health bar.
We can fix this by propelling the player and the slime away from each other, making it very clear that they have made contact, as well as keeping the player from taking damage too quickly by colliding with the slime rapidly.
Go to the behavior editor for the slime (you may to select the Main layer first), and drop down a Propel Object behavior under the Physics section. Connect it under the Broadcast Message behavior from earlier and open its properties.
In the properties, change the second object to the "Player" tag by opening the Tags tab and selecting "Player". Change the Force value to 200, the Object A Multiplier to 0.25, and the Object B Multiplier to 1.
Now when the player collides with the slime, it will push the player back away from the slime with a force of 200 (1 × 200 force) and the slime will be pushed away from the player with a force of 50 (0.25 × 200 force).
Our game is starting to feel pretty good now! Only problem now is that we have an invincible slime and our player has no way of defeating it. In the next part, we'll implement a way for our player to fight back.
Part 6: Defeating the Slime
To allow our player to attack the slime by jumping on top of it, we'll have to change how we're handling collision by having the slime ignore the bottom of the player (the player's feet) when dealing damage.
Open the behavior editor for the slime and tap on the Collided behavior we created in the previous part of the tutorial. Disable the option "On Bottom", which will ignore collisions if the slime collides with the bottom of the player's collision box.
Now, our slime won't hurt the player if the player jumps on it. Instead we want to perform different logic when that happens.
With the Collided event selected, duplicate it and reverse all the options in the new Collided behavior so only the "On Bottom" event is enabled.
This will serve as our entry point when the player jumps on top of the slime.
Next, drop and connect a Destroy Object behavior from the Object behavior section. It should already be set to destroy our slime object by default.
Now when we play our game, jumping on top of the slime destroys it. But again, as it did with getting hit by the slime before, it doesn't feel very satisfying.
Like we did in the previous part of the tutorial, let's add another Propel Object. Duplicate the older Propel Object behavior from the player damage logic and connect it under the Destroy Object behavior. Its properties from the older Propel Object behavior can remain the same.
Now when we play the game, there's a nice satisfying bounce when jumping on top of the slime.
Conclusion & Exercises
And that's pretty much it! We've checked all the boxes made in the introduction section:
- Instead of walking, the slime will jump left or right to move. Check!
- The slime will jump in the direction of the player. Check!
- If the player is too far away, the slime will not move (idle). Check!
- The slime will take away the player's health on contact. Check!
- The slime is defeated if the player jumps on top of it. Check!
If you made it this far, you should now have a decent understanding of how you can give your enemies some intelligence, and have them think and make decisions on their own. Well done!
However, there's always more things that can be done. As an exercise, try doing the following on your own:
- Play a "hit/dead" slime animation when the slime is jumped on from the player.
- Play a "hit" player animation when the slime attacks the player.
- Add some randomness to how far the slime will jump each time.
A completed project file for this tutorial can be found here: Download Tutorial Project