Scripting Good Game Feel
So in my re imagining of Super Mario Bros using El Lissitzky’s art style that I have decided to call Super Mario Bros. World El-Lissitzky, super creative right? Most of my ideas have been centered around the actual feel of the game, so I have been doing a lot of code that achieves just that. Although the duration of this project only lasted a single week, I think I’ve spent a good chunk of that coding rather than planning, designing or making the assets which just so happen to be the core of the project, or so I think.
So lets get this out of the way, I am a game designer and I love programming (wow!). This project doesn’t have the most complicated code I’ve ever made, but it definitely has the most coherent. Something else I should point out before diving in is that most of my code is based off of the usage of layers. By using layers I can choose what objects collide with one another and have easier access to deciding how different layered object behave with one another.
Something that is used in many video games is a grounded feature, what this does is checks if the player is on the ground. This can help with things such as a jump feature, a jump will typically add force or move the player upwards, if the player holds the jump button should they continue to gain force or be moved upwards? If that’s your intention sure but in this case its a no. To make sure the player can only jump when on the ground then you may need a grounded check. In addition to that I will explain how I implemented my score multiplier from Super Mario Bros. For those of you who don’t know, when you jump on a goombas head and then jump on another goombas head in succession without hitting the ground your score multiplier is doubled for each consecutive kill. For example if I jump on a goomba I will get 100 points, if I jump on another goomba without touching the ground I will get 200 points, if I then jump on another goomba without touching the ground I will get 400 points.
So to start off I needed an empty object on the player object, this object is placed slightly below the player object so that it is overlapping the ground collider when the player is on the ground. The first line of code in this snippet sets the is grounded bool to true if it returns an overlap point. In this scenario the overlap point takes 2 parameters which is the game object that was just placed on the player object and the layers that is reacts to. These will both need to be variables that are set either through code or the Unity inspector. I used the inspector and simply dragged the game object into the script component and selected the ground layer from the drop box also in the script component. So now whenever that empty object on the player is overlapping with the ground layer, is grounded will be set to true.
For the score multiplier I took advantage of the grounded functionality. The above code is from the player controller script and this snippet covers the on trigger enter 2D, specifically for layer 17 which is the enemies head. Jumping on the enemies head is the basic way to kill an enemy. When the player collides with layer 17 it will call the enemy death function from the enemy controller script which takes a parameter that is the stomp multiplier. After that it multiplies the stomp multiplier by 2, because the multiplier starts at 1, the next enemy that the player jumps on will have a score multiplier of 2 and then after that is 4 and so on.
It is vital that the multiplying of the multiplier is after calling the enemy death script because the stomp multiplier is applied within that function and we want the score to be increased for the next kill not the current one. That’s the general pattern of it, but what stops the player from increasing the score indefinitely? Well if we refer back to the previous image with the grounded check, there is a nested if statement that sets the stomp multiplier to 1 if it is greater than 1. What this means is that whenever the player is on the ground the multiplier is 1, if the enemy jumps on an enemy the multiplier is multiplied by 2.
Something else to note here is that at the end of this if statement the players velocity is zeroed and then some upwards force is applied them. This is so that the player can bounce off of an enemy and onto another to make use of the score multiplier feature.
Similar to the previous feature of applying an upwards force to the player object when jumping on an enemy, when the player hits the bottom of a platform block some down force will be applied to them. This is not something that I had initially noticed in Super Mario Bros but after some time testing my early build of this project I noticed that hitting the platform blocks felt really bad because you just float down. Another issue of not having this feature is that, because the platform animates upwards when hitting them, it’s possible to collide with the sides of adjacent platform blocks which completely negates horizontal movement. To fix all these issues simply zeroing the players velocity and then applying some down force which is equal to negative stomp force (kind of cheaty but still gives good results).
Something that I was unsure about at the start of the project was how to imitate the ‘hold to jump higher’ feature that Super Mario Bros has. Above you can see a snippet of the code I did for this function, this function is called whenever the player presses the space key. I’ll start with the second if statement, if the player is on the ground when the function is called, the jump sound will play, the was grounded bool will be set to true, the players velocity will be zeroed and then an upwards force will be applied. The reason behind zeroing the players velocity is because there are multiple objects in the level that apply force to the player. If they hit multiple of these objects in succession the player object will gain a whole lot of velocity which they shouldn’t. This workaround is a bit cheaty because it will stop any horizontal momentum too which shouldn’t happen, but it’s a quick way to get a nicer feeling jump.
The third if statement checks if the player was grounded, remember this was set to true when the player initially jumped. There’s some important variables to this code that is not explained in this screen shot, but basically the jump timer is set to 0.5 by default and the was grounded bool is set to false as soon as the player lets go of the space bar. While the player is holding space the jump timer variable will decrease by the value of delta time which is relative to real time and an upwards force will be applied to the player which is equal to the air force variable multiplies by the jump timer. This means that the longer they are holding jump the less the upwards force will be, this makes for a nice jump curve.
Finally the first if statement simply sets the was grounded to false if it was true and the jump timer is less than or equal to 0. What this means is that after the player has initiated a jump and is still holding jump in the air to increase their jump height, once the jump timer is over (hits 0) the was grounded variable is set to false and the third if statement will no longer be in effect and thus no force will be applied to the player object. This is to avoid the player from being able to hold jump down indefinitely and constantly apply force. If that were the case they would be applying down force anyways since the force of the jump is relative to the jump timer which would be in the negatives after the 0.5 second mark.
That’s quite a bit of information for some simple scripts but it makes for a much more polished feeling game. Having the player be able to hold the space bar to increase their jump height is a fundamental thing for replicating Super Mario Bros that I simply had to have in this project. The method of implementing it really makes for a good feeling jump curve and is something I’m proud of. The feature of applying down force to the player when hitting the under side of a block is a nice touch of player feedback and makes the game more fast paced. You can speed through the level and bounce off the under side of a platform to quickly get a goomba stomp in without the need for predictions or difficult timing. While the upwards force after a goomba stomp is quite necessary to use the stomp multiplier to any effect it definitely makes is easier and adds that extra player movement that makes the game feel very bouncy.