image

5
Robot Heist: Creating Consequences for Losing

Now that our game has lasers, danger is a possibility, which means there’s the potential for the player to lose. What does losing mean in a game?

In Herding Cats, the player could get stuck and have to restart the level, which was a form of losing. In that case, when players make a mistake, they need to try again. So one way to think of losing is as a process by which the player learns. Every time the player makes a mistake, they learn what not to do next time. We used the same thought process when we were figuring out how to make our lasers work. A good game always makes it clear to the player what they did wrong.

Do all games need to let the player lose? Not at all! Some games are just about exploring, telling a story, or petting a dog. Can you lose the experience of petting a dog? Losing doesn’t make sense in some contexts.

image

Robot Heist, on the other hand, is about trying to break into a bank without being detected. The possibility of getting caught makes the game more exciting and tense. Because the players can lose if they’re not careful, they can also feel like they’ve outsmarted the game when they succeed. Feeling challenged gives the player the opportunity to overcome obstacles, which can motivate them to beat the game. Let’s look at how to make losing feel like losing.

Getting Caught

What does getting caught look like in Robot Heist? Remember that losing is the process of making mistakes and learning from them. We should make sure that when the player loses, they understand that they made a mistake and they should understand what the mistake is. That’s the only way they’ll learn.

Click Rebuild and try moving a robot through a laser. What happens? Nothing!

We need to decide what losing looks and sounds like when a laser hits a robot. Perhaps an alarm should go off when the robots trip a security laser? We can play an alarm noise to let the player know they did something wrong.

The player also needs to know what they did wrong so they can change their strategy. We’ll let the players know by making them stop in their tracks when they’re caught. That way, if they were caught stepping into a laser, the player can see that was the mistake. We’ll turn both robots red (because they’ve been caught red-handed) to show they can no longer move. Even if only one robot is spotted, when the alarm goes off, the jig is up for both of them.

We’ll need to add two new objects to the game to serve as the caught, red versions of each robot, as shown here.

VertibotCaught
red darkgray
.000.
.101.
.000.
.111.
.000.

HoribotCaught
red darkgray
.....
01010
00000
01110
.....

VertibotCaught and HoribotCaught are just Vertibot and Horibot colored red. We don’t need to worry about adding these new objects to the legend, because the robots will never start a level already caught: that would be a very hard game to win!

Let’s think about whether we can add any groups to the legend at this point to make writing our rules easier. If the awake robot walks into a laser, the sleeping robot should get caught too. Therefore, it shouldn’t matter whether the robot is awake or asleep. So let’s make groups that let us refer to each robot regardless of what state they’re in.

Add these two groups to the LEGEND section:

Vbot = Vertibot or VertibotSleeping
Hbot = Horibot or HoribotSleeping

We call the groups Vbot and Hbot because Vertibot and Horibot are already being used; they refer to the awake robots. Here, we tell PuzzleScript that Vertibot and VertibotSleeping count as Vbot, and Horibot and HoribotSleeping count as Hbot. Now we can use Vbot and Hbot to check whether the robots are caught and whether they’re awake.

Next, we need an alarm sound.

image

Adding Sound Effects

Our “You got caught!” sound doesn’t need to sound exactly like an alarm, but it should sound menacing! When a player hears it, they should immediately know there’s trouble. Click the sound effects buttons until you find a sound you like (try the *, :(, or ? button). Then copy and paste the sound into the SOUNDS section of your script. Alternatively, you can copy the numbers as shown here:

=======
SOUNDS
=======
sfx0 72277508

In PuzzleScript, each sound is a set of numbers. We have to give our sound a name before we can use it. We’ll call this sound sfx0, and when we add this name to a rule, the sound should play whenever the rule runs.

Adding Caught Robots to the Collision Layers

Now we’re ready to put the caught robot objects on collision layers. Try doing that on your own! Make sure you add them to the same collision layer as the other robot objects.

Click Rebuild, and just to be safe, make sure there are no errors! If you forgot to add the new objects to the COLLISIONLAYERS section, you might see an error message like this below the game window:

image

An error message

Usually, an error message tells you what you forgot to do. In this case, it’s clear that the error is caused by the objects not being assigned to a collision layer. If this is what you see, go to the COLLISIONLAYERS section and make sure to add the objects!

Writing the Rules for Getting Caught

Let’s write some rules to make the robots turn into their caught forms if they touch a laser. Keep in mind that you can show that two objects are in the same space by putting them both in square brackets with no vertical bar dividing them. Because it doesn’t matter whether the robots are asleep or awake when they get caught, we’ll simply use the Vbot and Hbot groups that we created earlier. Add the following lines under your existing code in the RULES section:

(getting caught)
late [ Vbot Laser ] -> [ VertibotCaught ] sfx0
late [ Hbot Laser ] -> [ HoribotCaught ] sfx0

The rules are marked as late because we want them to happen after the robot has walked into a laser, not before. Be sure to put sfx0 at the end of each rule to make your cool sound effect play when the rules run.

Click Rebuild and try the program. When you move a robot into a laser, it should turn red like this.

image

Testing the rules for getting caught

Try using one robot to push the other sleeping robot into the laser. The sleeping robot should also turn red when it touches the laser beam. Because the caught forms of the characters don’t count as players according to the legend, you shouldn’t be able to move them after they turn red. Caught red-handed!

Right now, only the robot that touches the laser gets caught. But we want them both to get caught, even when only one messes up. Let’s add a rule that makes sure that both robots get caught.

(getting caught)
late [ Vbot Laser ] -> [ VertibotCaught ] sfx0
late [ Hbot Laser ] -> [ HoribotCaught ] sfx0
late [ VertibotCaught ] [ Hbot ] -> [ VertibotCaught ] [ HoribotCaught ]
late [ HoribotCaught ] [ Vbot ] -> [ HoribotCaught ] [ VertibotCaught ]

With this rule, when PuzzleScript sees that Vertibot has been caught, Horibot becomes caught too, and vice versa. Because you have two different pairs of square brackets on the same side of the arrow, both of those objects can be anywhere in the room. Recall that when we wrote the rules for switching earlier, having two objects in their own sets of square brackets means they don’t need to be next to each other. This rule makes sure that if the alarm goes off, it doesn’t matter where the robots are: they’re both busted.

Click Rebuild to test this new rule! Now both characters should get caught at the same time.

image

Both robots get caught

You can see that when Horibot triggers the alarm by crossing a laser, both Horibot and Vertibot turn red to show that they’re caught.

But notice that something weird happened. The characters get caught, but you also get a Win Condition Satisfied message and the level ends! This is the opposite of what we want to happen. Winning is the opposite of losing! Why do we win instead of losing?

Updating the Win Conditions

Let’s put our detective hats on again to figure out why the win condition is met when Vertibot and Horibot get caught. Because PuzzleScript gave us the Win Condition Satisfied message, maybe we should check our WINCONDITIONS section, which currently looks like this:

==============
WINCONDITIONS
==============
all Buddy on Exit

Our Buddy group currently contains Horibot, Vertibot, HoribotSleeping, and VertibotSleeping. Recall that all Buddy on Exit means all the buddies on the screen, regardless of how many there are, should be on the Exit objects for the player to win the level. The Buddy grouping should still hold true even if there are no buddies on the screen. The problem is that when Vertibot and Horibot become VertibotCaught and HoribotCaught, they no longer count as buddies because VertibotCaught and HoribotCaught don’t belong to the Buddy group.

This means that when the robots are caught, there are no objects that could possibly meet the win condition. And because there are no buddies not on exits, PuzzleScript decides that the win condition is met. Computers are more logical than we are, but they’re way less smart. When PuzzleScript checks the win condition all Buddy on Exit, it’s making sure that there are no buddies anywhere other than on exits. If there are no buddies at all, then as far as PuzzleScript is concerned, all the buddies on the screen (that is, zero!) are on exits.

Fortunately, we can have multiple win conditions in our game. Let’s add new win conditions to the WINCONDITIONS section that specify the player can’t win when both robots are caught, like this:

all Buddy on Exit
no VertibotCaught
no HoribotCaught

The code no VertibotCaught checks that there are no VertibotCaught objects in the level. To win a level, all the win conditions need to be satisfied at the same time, so the level can’t be won if PuzzleScript finds any caught robots, even if they are all on the exit.

This new win condition should work fine (try it and see!). But we could simplify this even further by making a group that contains both caught robots, which we’ll call Trouble. Add this line to the LEGEND section:

Trouble = VertibotCaught or HoribotCaught

So we’ll finish the level with no Trouble. The player wins if they’ve gotten both buddies onto the exit without running into any Trouble. Now we can use the Trouble group to streamline our WINCONDITIONS section code, like this:

==============
WINCONDITIONS
==============
all Buddy on Exit
no Trouble

Now that we’ve written rules for how the robots should interact with lasers and how that should affect the outcome of the game, let’s add a few more threats to make our game more exciting.

Adding Robot Guards

The second threat we’ll add to the game are robot security guards that patrol the Data Bank. If the robot guards find Vertibot or Horibot, the player will lose!

The challenge will be to make the guards move on their own, because this can be a very complicated process to program. Have you ever played Hide and Seek? A lot of decisions go into finding your friends when they’re hidden. You probably know the best hiding spots in the area in which you’re playing. You know your friends. You know which friend always surprises you by trying to pick the cleverest hiding spot. You remember which places you’ve already looked in. You look over your shoulder sometimes in case someone is sneaking around behind you. Somehow a human being can synthesize all of this information to make decisions quickly. How can we possibly program a video game guard that’s as smart as a person?

Fortunately, we don’t have to, because this is only a robot guard. It’s okay if it follows a very simple pattern. In fact, it’s probably better if the guard’s actions are predictable. For example, if it covers the same ground over and over, the player can anticipate its path and sneak around it. If the guards were as smart as real humans, our game would be impossible to win.

The simplest way we can code this kind of patrolling movement is to have the guard move in a straight line and then turn when it reaches a wall. We can have it continue moving forward and turning in the same direction until it eventually returns to the point where it started and then starts over again.

Creating Guard Objects

Let’s start by creating the objects. We’ll need four guard objects: one for each direction the guard can move in (up, down, left, and right). We start with one of the objects and then change it into the next type of object when it runs into a wall. A benefit of using four distinct objects is that we can change each object to look different depending on the direction in which it’s moving. That way, the player can look at the screen and see where the guards are going.

Let’s add four new objects to the game.

GuardUp
gray red
..0..
.010.
00000
00000
.000.

GuardRight
gray red
.00..
0000.
00010
0000.
.00..

GuardDown
gray red
.000.
00000
00000
.010.
..0..

GuardLeft
gray red
..00.
.0000
01000
.0000
..00.

We’ll name them GuardUp, GuardRight, GuardDown, and GuardLeft. As you can see, the red pixel should signal to the player which direction each object can move in.

Adding Guards to the Legend and the Collision Layers

Now we need to put these guards into the legend. We also need to assign a symbol for each direction, so we can use it to choose which direction the guards start in. Add the following lines to your existing code in the LEGEND section:

8 = GuardUp
6 = GuardRight
2 = GuardDown
4 = GuardLeft

I chose those numbers as symbols because of their placement on the keyboard’s keypad (the part that looks like a calculator). An up arrow is on the 8, a right arrow is on the 6, and so on. When I see these numbers in a level and forget which direction it is, I can just glance at the keypad on my keyboard to remind myself.

Let’s also add a Guard group to the legend that will let us refer to all guard objects regardless of which direction a guard is facing, like this:

Buddy = Player or HoribotSleeping or VertibotSleeping
Guard = GuardUp or GuardRight or GuardDown or GuardLeft
Depresser = Player or Pushable or Guard
Blocker = Wall or GateClosed or Crate or Gun or Guard

Note that the Guard group should come before the Depresser group in your script. The reason is that we want guards to be able to step on panels like the other depresser objects and be able to block lasers as a blocker. Before we can add Guard to the Depresser and Blocker groups, we first need to tell PuzzleScript what a guard is. Only then can we tell PuzzleScript that a guard can be a depresser and a blocker.

Last but not least, be sure to add all four guard objects to a collision layer. Put them on the same layer as the robots and all the other solid objects. Then click Rebuild to make sure there aren’t any errors.

Now let’s write the rules that make our guards patrol.

Writing Rules to Move Guards Forward

A guard’s motion has two parts: it moves forward, and then, when it bumps into a wall, it turns to the right.

image

Let’s add the forward movement first by adding the following to the RULES section:

(guardbots)
left [ GuardLeft ] -> [ > GuardLeft ]
down [ GuardDown ] -> [ > GuardDown ]
right [ GuardRight ] -> [ > GuardRight ]
up [ GuardUp ] -> [ > GuardUp ]

At every turn, we just change our guards into moving guards. Because the four rules are directional, the > simply matches the direction of the rule. For example, GuardLeft always moves left, GuardDown always moves down, and so on.

Put together a simple test level to check that this script works now. Be sure to include all four objects on your test level, like this:

########
#..6...#
#......#
#..I..2#
#8..H..#
#......#
#...4..#
########

Try playing your level! You should see something like this.

image

Guard test level

If your guards start one space forward from where you put them, it’s because of run_rules_on_level_start! Don’t worry—this won’t be an issue when we turn on realtime mode shortly. The guards should move only when the player moves. Wiggle the player back and forth and watch how the guards behave. They’ll move forward until they hit the wall. Then they’ll get stuck because we haven’t told them what to do when they run into a wall. Let’s tell them to turn to the right after bumping into something.

NOTE: If you get an error message that says, “Trying to access a level that doesn’t exist,” try pressing Rebuild and start a new game.

Writing Rules for Turning Right

Before we can make the guards turn, let’s define a new group in the legend called Obstacle for every object that could get in a guard’s way. Add the following to the LEGEND section:

Guard = GuardUp or GuardRight or GuardDown or GuardLeft
Obstacle = Wall or Crate or GateClosed or Gun or Guard

The new Obstacle group includes all the solid objects a guard can run into.

Now we can add rules to make the guards turn when they bump into any obstacle. Guards turn clockwise (to the right) whenever they bump into something. We’ll make guards turn by replacing them entirely with another object that points in the new direction. Add the following to the RULES section to do this:

(guardbots)
left [ GuardLeft ] -> [ > GuardLeft ]
down [ GuardDown ] -> [ > GuardDown ]
right[ GuardRight ] -> [ > GuardRight ]
up[ GuardUp ] -> [ > GuardUp ]

[ > GuardLeft | Obstacle ] -> [ GuardUp | Obstacle ]
[ > GuardDown | Obstacle ] -> [ GuardLeft | Obstacle ]
[ > GuardRight | Obstacle ] -> [ GuardDown | Obstacle ]
[ > Guardup | Obstacle ] -> [ GuardRight | Obstacle ]

When a guard tries to move into an obstacle, it instead is turned into the guard object facing the next direction clockwise. GuardLeft turns into GuardUp, GuardUp turns into GuardRight, GuardRight turns into GuardDown, and GuardDown turns into GuardLeft.

Note that I’ve grouped the rules so all the guards move forward at the same time and then all the guards turn at the same time, because rules are run in order from top to bottom. Click Save now. To see what can happen when you change the order of the rules, try moving the [ > GuardLeft | Obstacle ] -> [ GuardUp | Obstacle ] rule before the up [ GuardUp ] -> [ > GuardUp ] rule. Then try grouping the rules so each guard object’s move and turn rules are together.

(guardbots)
left [ GuardLeft ] -> [ > GuardLeft ]
[ > GuardLeft | Obstacle ] -> [ GuardUp | Obstacle ]

down [ GuardDown ] -> [ > GuardDown ]
[ > GuardDown | Obstacle ] -> [ GuardLeft | Obstacle ]

right [ GuardRight ] -> [ > GuardRight ]
[ > GuardRight | Obstacle ] -> [ GuardDown | Obstacle ]

up [ GuardUp ] -> [ > GuardUp ]
[ > GuardUp | Obstacle ] -> [ GuardRight | Obstacle ]

Click Rebuild and try playing the game with this updated code. You should see that one of the guards moves differently than the others. In this example, GuardLeft turns into GuardUp after it runs into a wall. But because of the misplaced code, GuardUp moves one step forward immediately after turning instead of turning in place and waiting a turn to move like the others. This is why it’s important to always think about the order in which your rules execute. When GuardLeft hits a wall, it turns into GuardUp and immediately moves up; the other guards wait a turn after turning to move. Can you read through the rules one by one and see why?

Early in the rules, GuardLeft becomes GuardUp. But a later rule moves GuardUp up. Because the turning rule for GuardLeft happens before the moving rule for GuardUp, the guard turns and moves on the same turn!

Click the Load menu and return to the game you last saved.

Catching the Intruders

Let’s create rules that let the guards actually catch Horibot and Vertibot by adding these two lines to the guardbots section in the RULES section:

(guardbots)
left [ GuardLeft ] -> [ > GuardLeft ]
down [ GuardDown ] -> [ > GuardDown ]
right [ GuardRight ] -> [ > GuardRight ]
up [ GuardUp ] -> [ > GuardUp ]

[ > Guard | Hbot ] -> [ Guard | HoribotCaught ] sfx0
[ > Guard | Vbot ] -> [ Guard | VertibotCaught ] sfx0

Now if a guard tries to move into either robot, the robot turns into its caught equivalent, setting off the alarm sound at the same time. (I know the guards are robot guards, but whenever I mention “robots,” I’m referring to just Vertibot and Horibot.)

Notice how grouping helps us write fewer rules. The Guard group includes four different guard objects, and Hbot and Vbot include two objects each: the sleeping and awake versions of the robots. Without groups, we’d need to write 16 different rules to cover all the different combinations!

Click Rebuild to test your game. Try moving a robot in front of a guard’s patrol path. Then try putting an asleep robot in front of a guard’s patrol path. When the guard bumps into the robot, the robot should turn red just like it would when it walks into a laser.

image

Testing the Guards

Earlier, we put guards in Depresser and Blocker groups in the legend. Let’s make some test levels to test those features.

Here’s a level that tests how guards work with panels.

image

Testing guards and panels

Because a guard is a depresser, the gates should open when the guard moves onto the panel and close when the guard moves off the panel. If you want the gate to stay open for a longer time, try putting a bunch of panels in a row, so it will take the guard longer to get past them.

Next, try making a level to test whether guards will block lasers. Can you come up with a level where the robots need to time their movements with the guards to sneak past lasers?

As a last test, let’s see what happens when a guard runs into a dead end. Make a level shaped like the following.

image

Testing guards at a dead end

When the guard bumps into the dead end in the upper left, it turns to the right. Then it immediately bumps into another wall and turns right again. Now it should head back down. What do you think will happen when it gets back to the bottom and bumps into the wall there? You should see something like this.

image

Guard stuck in a dead-end path

It should turn right again, just like we told it to. It should turn around and start heading back up. This guard will keep going up and down this pathway forever and never return to its original patrol path. Keep this example in mind when you design your levels so you won’t make the same mistake!

Realtime Mode

All the games we’ve made so far are called turn-based games. In a turn-based game, one player has a turn and then the next player takes a turn, as in a game of Checkers. In Herding Cats, the cats move only when the player moves.

In Robot Heist, we want characters that move on their own. Right now, the security guards in our game move only when the player moves. But if we switch our game into realtime mode, they’ll move on their own, even when the player isn’t moving. Realtime refers to basing the movements of game objects on the amount of time that has passed in the real world, not the movements of the player character.

How does realtime feel different than a turn-based game? First, there’s an element of time pressure. Even when the player is not moving, the guards are patrolling. That means the player won’t be able to sit and think about their next move for as long as they want. As in a real heist, the player has to think and act quickly. It also helps create the sense that these guards have a life of their own, outside the player. After all, real security guards wouldn’t wait for an intruder’s permission to move, would they?

Making Objects Move in Realtime

Let’s make objects move by themselves by turning on realtime mode in PuzzleScript. We can tell PuzzleScript how often we want objects to move, such as once every second. Then, every time a second passes, PuzzleScript runs all the rules whether or not the player has moved.

To turn on realtime mode, simply add the following line to the beginning of your PuzzleScript code:

title Robot Heist
author anna anthropy
run_rules_on_level_start
realtime_interval 0.2

The number after realtime_interval tells PuzzleScript how often to run the game rules, in seconds. So realtime_interval 1 would be once every second. And 0.5 would be once every half a second, or two moves per second. Our example has realtime_interval 0.2, which is two-tenths of a second, or five moves every second.

Realtime works best if you add a couple more of PuzzleScript’s optional features, like throttle_movement and norepeat_action (the latter of which was added in Chapter 4). Add both below realtime mode, like so:

title Robot Heist
author anna anthropy
run_rules_on_level_start
realtime_interval 0.2
throttle_movement
norepeat_action

The throttle_movement feature limits the player’s movement speed. Without it, pressing an arrow key a bunch of times can make you move faster than just holding down the arrow key. That’s not a big deal if everything in the world moves at the same speed as you, but it’s important if everything else is moving at its own speed. And it isn’t a big deal if everyone else in the game moves whenever the player moves. But if guards are moving at their own speed, the player can move way faster than them by mashing buttons. And because this game is about planning and strategy, not about button mashing, we’ll use throttle_movement to keep the player from mashing buttons in order to win.

Without norepeat_action, holding down the action key will continue switching between both robots, meaning it’s easier to accidentally switch more times than you want to. Also, norepeat_action makes sure the player releases the action key before they can press it again. Adding both features makes the game more user-friendly.

Click Rebuild and watch your guards go! They should start moving on their own in a beautiful, synchronized ballet, even when your robots aren’t moving.

Now try moving the robots. Whoa!

image

When you move the robots, you should see that the guards speed up and move super fast whenever you move! Yikes! What’s going on?

Checking for a Stationary Player

Instead of calling a turn as normal (running the rules, then moving objects, and then running late moves) PuzzleScript’s realtime mode calls a turn every 0.2 seconds. So the rules for guards are run even when the player hasn’t pressed any keys. But we haven’t accounted for the fact that PuzzleScript still calls a turn whenever the player presses a key. This means that the guards move on their own every 0.2 seconds and move again when the player moves. We want the guards to move independently of the player, not to move faster when the player moves.

We need the guards to move only every 0.2 seconds, but not when the player moves. To do that, we just need to check that the player is stationary whenever we try to move the guards. Go back to the rules for moving guards in the RULES section, and add [ stationary Player ] to both sides of each arrow, like this:

left [ GuardLeft ] [ stationary Player ] -> [ > GuardLeft ]
[ stationary Player ]
down [ GuardDown ] [ stationary Player ] -> [ > GuardDown ]
[ stationary Player ]
right [ GuardRight ] [ stationary Player ] -> [ > GuardRight ]
[ stationary Player ]
up [ Guardup ] [ stationary Player ] -> [ > Guardup ]
[stationary Player ]

Adding [ stationary Player ] lets guards move forward only when there’s a stationary (non-moving) player somewhere in the level. Note that this doesn’t change the player at all. These rules just make sure that guards move every 0.2 seconds, but not when the player robots move.

Click Rebuild and try moving around. The guards’ speed should remain constant no matter what the player does.

Extra Challenges: Make It Look Good

Here are some extra touches I added to the finished version of Robot Heist to make the game look more appealing. The challenges are listed in order of difficulty. Can you figure out how to script them? If you need help, hack my finished Robot Heist game and look at how I did it.

Challenge 1: Make characters bigger than a single space If you look closely at the player robots in Robot Heist, you’ll see that they’re slightly larger than the 5 × 5 pixel size of most PuzzleScript objects. In fact, each player robot is made up of three separate objects that all move together.

Challenge 2: Randomize the walls One way to keep the walls from looking too similar is to create a bunch of slightly different wall objects. When the level starts, each wall changes its appearance at random. These walls all behave in the same way, but they add visual variety to the levels.

Challenge 3: Add animation In a realtime game, we can make objects change appearance over time and animate them. For example, the solid gold superconductor—the treasure the robots are trying to steal—shines and gleams by changing between three different objects, each representing a single frame of animation.

Challenge 4: Wire paths To make it easier for the player to make the connection between panels and the gates they open, I drew wires leading from the panels to the gates. These work a lot like the lasers do: at the beginning of the level, the player looks around for adjacent wires and then draws connections between them.

What You Learned

In this chapter, you added the ability to lose the game. You gave lasers and robot guards the ability to catch the player, adding an element of tension to the game. You learned what losing looks like and why you might want to give the player the possibility of losing. And you made the guards move in realtime, patrolling for the robots instead of waiting for them to move.

Now you have a complete cast of objects: the robot protagonists, crates to push, gates to open and close, security lasers, and patrolling guards. You’re ready to combine all these objects into action-packed levels. In the next chapter, you’ll do just that.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset