Escape From a Moon: Creating a Rotating 2D Platformer in Unity (1)

itch.io | GitHub

It’s been some time since Ludum Dare 38 and I wanted to make some improvements to my Compo entry, called Escape From A Moon (you can take a look at Ludum Dare rules if you don’t know what I am talking about), so I worked on them and not long ago I released Escape From A Moon v0.2. You can find the devlog here. Now that this new version is out, I have decided it is time for me to explain a little bit about how this small prototype game works.

You might think this is a fairly simple game and there is not much to explain. Well, that’s basically true 😀 but it’s also true that developing a platformer with a rotating environment can be challenging and is much more difficult than a “normal” one. I will explain this later, but first of all I want to clarify: I am not saying this is something new or revolutionary, and I didn’t look for examples of what I wanted to achieve either, so there may be better approaches than the techniques I’m going to share here, but I want to share them anyway. I will split the explanation into several posts, so this first post will only contain the basics.

Just one more thing: let me remind you all this is an open source project so you can download the source code, use it, and collaborate on it if you want. Now let’s start.

 

 A circular environment

This is the whole environment of the game

Inspired by the LD38 theme (Small World) and with not much originality, Escape From A Moon takes place in a very small satellite. The player controls an astronaut who needs to walk and jump around in order to reach his spaceship before he runs out of oxygen. A peak prevents the astronaut from taking the short way, forcing him to encompass the whole satellite.

The rotation in Escape From A Moon is real. This is: it’s not a visual trick by which I am rotating the background instead of moving the character. I didn’t do such thing because I don’t think physics would work properly, it would have limited extensibility (what if I wanted to add the ability to move to a different satellite or planet?) and the result would have been much less intuitive from the development perspective. But, making the character walk around the satellite has some important implications. Let’s go through all of them.

 

#1 Vertical orientation

For the character, the vertical orientation depends on the position on the planet. Which means, the vertical orientation can be constantly changing. Knowing the vertical orientation is important for things like jumping: a jump is basically the result of applying a force in the direction of the character’s vertical orientation.

Vertical orientation is defined by the vector that goes from the center of the planet to the character’s position

To get the vertical orientation vector, just subtract its origin (planet’s center) from its tip (character’s position). So, in the Character script:

Vector3 _direction = transform.position - planet.transform.position;
upDirection = new Vector2 (_direction.x, _direction.y).normalized;

In this case, we are also storing its normalized, 2D version, as we will need it later.

 

#2 Horizontal movement

Intuitively, we can think of horizontal movement as the one that is parallel to the ground (assuming the ground is horizontal), or perpendicular to our vertical orientation. But in a circular environment, both the vertical orientation of the character and the ground’s orientation depend on the position within that environment. So, in order to know in which direction to move the character, we need to calculate it.

As mentioned above, the horizontal direction is perpendicular to the vertical one. Given we have already calculated the vertical direction vector, we can get the horizontal direction from it.

Looking at the character with a 3D perspective, we can see the trick. The red arrow is the vertical direction. The blue arrow is the depth direction, which is fixed. The green arrow is the direction we want to calculate.

As you can see in the image, we already have two direction vectors: the vertical and the depth ones. The horizontal vector is perpendicular to them both, so we can get it by calculating their cross product. Leveraging the _direction vector we got earlier:

Vector3 _walkVector = Vector3.Cross (Vector3.back, _direction).normalized;
Vector2 _walkDirection = new Vector2 (_walkVector.x, _walkVector.y);

 

Conclusions

We have seen here how by applying simple vector arithmetics we can get the information we lack because of the nature of the game. Now we have both the direction in which to apply the jump force, and the direction in which to apply the walk force. More on this, in a future post.