1. The Basics
  2. Let's make a Mario game
  3. Let's refine our Mario game

Let's refine our Mario game.

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:

Teleporting

Let's start off by implementing teleporting. The concept is pretty straight forward:

There 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.

a teleporter
Something like this

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

Canyons mean nothing to us

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!

a pipe head a pipe
You know the ones I'm talking about

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:

In debug view
In game view

How cool is that?

Moving between layers

Level swapping

Player powers

Using decals

Animation trajectories and paths