In the previous tutorial we made a Mario game. And it was awesome. Now it's time to visit some more advanced topics when it comes to games, and we'll introduce all of them as Mario game mechanics. This tutorial will cover the following things:
Let's start off by implementing teleporting. The concept is pretty straight forward:
actor.setPosition(...,...)
to effect a teleportThere are many ways in which to listen for teleport events,
including using a teleport Pickup
, a teleport Trigger
region, or that old favourite: an actual teleporter. So let's implement one
of those, to get a feel for the whole thing. First we extend our previous
tutorial's Mario with a "crouching" state when you press 'S', so that we have
something to monitor for teleporting, and then we'll set up our actual teleport.
Alright, that's a fair bit of code, but you should be able to see what's going on at this point. Two new states have been introduced, the crouch and crouch-jump states, and they require input handling to be extended so that we can change to them, and don't change away from them when we shouldn't reasonably be able to.
So let's make a teleporter.
You might recognise that as a Megaman (or Rockman if you prefer) teleporter. Super convenient! we're going to turn this into an in-game teleporter by setting up the following:
With this, we'll have a simple teleport procedure: actor jumps onto teleporter,
actor crouches, teleporter teleports actor to its new location. So, let's make
a teleporter; we make it a BoundedInteractor
, just like we did with
the Muncher
in the previous tutorial, because we need to attach
boundaries to this things:
Alright, done. Of course we need to define this special "Teleporter Boundary" too:
The important bits happen on lines 22 through 38, and specifically on lines 30 through 38: we "intercept" the function that is normally used to determine how to update an actor position. When an actor is moving around freely, this is isolated code and we can't see what's happening, but when an actor is standing on a boundary, the boundary is asked whether the intended motion of the actor is "legal" or not, and if it's illegal, how it should be changed to make it legal.
For instance, an actor standing on an incline, while subjected to
gravity, would LIKE to fall straight through the incline, because
gravity points straight down, but this would be illegal movement.
Instead, the incline will change the illegal movement into legal
movement along the surface of the incline, so we slide left or
right, rather than going straight through. This happens in the
redirectForce
function, so we can hijack that in order
to make teleportation happen.
This function is called every frame as long as an actor is standing on a boundary, so what we do is check: "is the actor crouching? If so, it's teleport time! ... if not, just let the normal code for force redirecting do its thing", which you see on line 37. You may also see that lines 25, 26, and 27 don't seem to do anything - they just immediately call the superclass's function. This is mostly to appease Processing.js, which (thanks to JavaScript and Processing being slightly different in how they deal with super/sub classing) makes everything work. Without them, the call on line 37 will actually call the function on line 30, which would be very inconvenient.
Anyway, back to what our code does: when an actor crouches,
we detach it from the boundary, set its state to idle instead
of crouching, and then teleport them to their new location.
return STATIONARY
on line 35 is required because
the redirectForce
must always return a list of
numbers that represent the new x and y directions of an actor's
intended movement. Even if we don't want them to move, we have
to explicitly say this, so we return the list "no movement in the
x direction, and no movement in the y direction", which we've
stored in a variable called STATIONARY
on line 22.
Finally, we want to actually make use of these teleporters.
So let's take our original LevelLayer
and strip
out everything except the ground, and add some teleporters
(for testing purposes, we're going to make the level layer
only one screen size wide and height):
Alright, cool. We now have a level with teleporters. Let's try this out! We should now be able to jump onto a teleporter, press 'S', and teleport across the gaping canyon in the level. We should also able to just walk off the cliff, although that won't do us much good to get the other side...=D
Niiiice... but not very Mario. You've seen the idea behind teleporting, so let's take what we know, and apply it to the only possibly correct Mario teleporter: green, tubish, pipe-like "brrp-brrp-brrp" teleporters!
We're going to use the same idea, but work it out slightly different:
There is nothing in this plan that isn't going to work. Let's get to it:
Of course, we'll need to actually define this pipe class, too:
We do a few things here. First off, we reserve head
, body
,
and trigger
as properties that we can call from somewhere else, like the
Level Layer that we create the pipe in. Because the two sprites and the trigger need
to be added to a layer, we make them accessible.
Then, in the constructor, we set up the head and body as simple sprites. To make
positioning easier, we also align them so that if we position them on (x,y), that
will be their left/bottom corner, using align(LEFT,BOTTOM)
.
After that we add the boundaries. We box in the pipe, and add another boundary inside the pipe so that we can't "fall through" the pipe if we're inside it. This is important, because we're going to be inside it when we teleport!
The teleporting we effect by adding a trigger region that lives inside the pipe, so that when something falls through the lid (when that gets disabled), they fall onto the trigger region, and get teleported.
So far, so good! Now, the Lid
and TeleportTrigger
look
as follows:
This is similar to the previous teleport boundary, except that we don't immediately teleport any actors, we simply disable the boundary, so they'll fall through, and hit the teleport trigger - sneakily hidden behind the pipe's graphics:
So what does this look like? Well, to show this off best, let's look at this in two different ways. One with boundaries and triggers shown (in debug mode) without drawing the pipe graphics, and one in "game view", where we just see the graphics, with boundaries and triggers hidden:
How cool is that?