The prompt for this game is to create a platformer. We didn't have a very clear vision at first, but had the idea of creating a game that was heavily physics based.
I was designing the player, and this would be my first time designing player movement that reaches this level of complexity. It was definitely quite the challenge using the Rigidbody and pure physics rather than something more stable like the character controller. The player in the end still required some fine tuning in certain areas, the main one being the floaty feel of the long jump. However, even with the flaws I think I was able to create a unique and comfortable movement system for the player. Prior to development I did some research on platformers and came across a great Youtube video breaking down the gameplay and player controls of Celeste. This video inspired my process significantly. The video goes in depth on different platformers and how each achieves different effects. From it I also learned that movement systems that appear simple on the surface actually had a lot of thought put into them.
I took all these ideas in mind when designing the player movement. I decided on having a "heavier" feel to the movement that was similar to Super Meat Boy combined with more complex abilities such as dashing and wall jumps.
I started off with a capsule to stand in for the player and coded the basic movement system with sideways movement mapped to hortizontal input values and a jump system. Working off from there I needed to add a wall jump mechanic, which I was stuck on for a while. I used overlapbox to identify if the player was in contact with the wall which did work. The wall jump would simply work by pressing jump again when touching a side of a wall, which would send the player flying to the other direction. However, I encountered a bug and inconsistency in which the player would sometimes fly the full distance or just drop before reaching maximum height. I was pretty bewildered about it until I found out that the force would stop if I was pressing any of the movement keys (being A or D key). I was bewildered by this for a while until it hit me that it was the nature of the movement code itself that caused this problem. I basically mapped the velocity of the player to the keys. This meant that just by simply pressing the keys it will cause the physics enacted on the player would be reset. The function of velocity did not hit me until later on, as I thought that the code simply added onto the velocity instead of being directly mapped to it. I had to change the movement system to rigidbody.AddForce(). This itself meant that I would need to add numerous additional codes to make it playable.
Simply by using [velocity = (whatever keys its been mapped to) * speed value] meant the player will stop acceleration if the player stops pressing the keys. By adding force, however, caused the player to continue accelerating even without interacting with the keys. In response I made it so that if no keys are pressed, the player would brake by itself achieved through multiplying the velocity by a value. This enabled a more natural gradual braking system. Another problem that was solved was changing directions. If the player accelerates too fast, it will take a few seconds to completely change its direction to the other side. Thus I simply multiplied the velocity by -1 when the key for going to the other direction is pressed. There were some more micro adjustments made to make the movement feel more intuitive.
After having the movement system down I added three abilities, teleport, dash, and smash down. I later on took out the teleport ability as it did not fit into the premise and what we were trying to achieve with the game.
The smash down ability was simple, I just needed to check the player was in the air and had to stop player velocity in all directions with Vector3.zero and add a force downwards.
As for the dash ability, the player would dash toward the direction the player mouse clicks. At first I wanted to use addForce, but there would always be a slight drop due to gravity. For a dash to look smooth, the player needed to thrust in a straight line. So for this I simply mapped the player's velocity to the direction multiplied by the speed. Then I would Invoke a function after 0.13 seconds, making the bool for the dash false, stopping the dash. However, at first I did not add anything to stop the player, so the player would continue flying a far distance. To offset this effect I simply mapped the player's velocity to a slower speed in the Invoked function.
Player shooting system needed quite a lot of balancing. in the beginning the gun had always been a shotgun, and would increase bullet numbers and their speed as the player charged the shot by holding down the mouse button. However, it was obvious later on that simply by spamming the key was more powerful than charging the shot. Due to this it was changed to a pistol shot if the player tap fires while charging it would turn it into a shotgun, causing much greater damage. Also, charging the shot would cause a recoil effect that kicks back the player. It can be used to add an extra kick to the player's speed if used correctly.
While the player movement was fine tuned, the enemy AI's in the end still had certain bugs that needed addressing. Due to the time and deadlines I was only able to create three enemy types. One major issue was utilizing Rigidbody with NavMesh, and the two do not bode well with each other. One ability of the player as mentioned before was smash. When the enemies are in range of the smash perimetre, the enemies will fly up and get stunned for a few seconds. In order to achieve this, the NavMesh would have to be turned off first, and then activated again after the stun is over. However, a new issue arose, which is that two of the enemy types are using Trigger colliders which meant they will fall through the floor. A simple fix would be to just set isTrigger to false. But for some reason occasionally when the NavMesh is reactivated and the collider is set back to a trigger, the enemy falls though the platform as the NavMesh agent could not identify the Navmesh. This one recurring issue, although it did not impact the gameplay too much.
After sending the player to Cecil, he provided more feedback on what can be tuned down, or what can be adjusted. Before building I made final adjustments on certain values to make sure everything is balanced.
Moving on to the enemies:
The first enemy is a simple one. It has the ability to shoot at the player.
The second enemy was much trickier to make. It was my first time experimenting with a laser weapon. I used a line renderer to render the laser, and the damage is simply from a raycast. I made it so that when its two enemies present, they will form a link between each other and the damage will almost quadruple as well as penetrate platforms and surfaces. If there is only one or more than 2 of this enemy type they will simply target the player. However, their lasers will not penetrate anything.
The last enemy is a heavy type. It has the slowest attack rate, but its attacks pack quite the punch and deals a large amount of damage. Its ability is to physically smash into the player whenever the player is in sight and in range. The most important aspect of this enemy is that it slows down the player movement as they smash quite hard and can counter any velocity the player has.
Ultimately, the maps are sort of like a trial course. I designed the first level which is basically a tutorial. In the later levels Cecil would design the beginning trial courses, and then I would design the arena like room in which the player has to fight off all the enemies. Each arena is designed differently with more traversal options as the player gets further down the levels. The NavMesh is baked with very slim rectangles so that the enemy AI's cannot move too much across the x axis as this game is 2.5D platformer. The enemies' NavMeshAgent radius is also reduced to a small number so they can all get pass each other. To allow the enemies to travel across disconnected platforms, I added numerous Offmesh links. This process was the most redundant and draining one, as I had to consider a lot of the possible routes. This might have also caused slightly slower performance.
For a more simplified method of going from level to level, I made a portal that activates once all the enemies in the level has been cleared.
Since physics are a major part of the game's mechanism and core foundation, I began by experimenting with the shatter effectiveness in Unity. I built several different block primitives in Maya and applied shatter effects to them as a separate mesh. I then imported all my assets into Unity and applied the code where the original mesh will instantiate and be replaced by a different shattered mesh as an alternative GameObject upon collision. Once the effectiveness of this most fundamental shatter physics was achieved, I began to design and develop further on more diverse interactable physics assets.
When Developing the physics assets for the game, I have stick to the ideal and notion of several principals of mine in the form of these following questions:
- In what ways can the assets interact with the player?
- In what ways can the assets interact with the gameplay?
- In what ways can the assets interact with the environments?
- Can the assets interact with assets?
- Can the assets interact BE the environment?
As you may have noticed from these questions, they translate the assets in the game correspondingly in complement to the very progressive nature of gameplay itself in aspects of the gameplay difficulties, complexity, and interactivity.
This is the first physics assets in the game. The cube shatters upon collision with any physical objects with the circumstances of collisions at certain velocity. The cube has the least complexity to its mechanism, but it serves a robust purpose in the initiation to passively influence gameplay difficulties by amplifying challenges to the player's most fundamental but essential mechanism - movement.
During the development of the cubes, the most challenging difficulties encountered by myself was the velocity detection due to Unity's physics engine and calculation constrains. I solved the problem by coding with an independent distance logic that activates a Boolean for the measurement of Player's velocity.
The pillar further amplifies to the influence of the cubes. Instead of shattering the object upon collisions at certain velocity, the pillars only shatter when the player charges at it for 3 times. The pillars also progress to influence and interact with the environment, as its structural foundation nature will affect structural integrity under its absence, such as the tumbling of floors when the pillars are shattered below the levels.
To further enhance the impact feedbacks, I created a custom shader graph for the pillars:
The surfaces are the third types of physics assets in the game. The surfaces behave similar to glasses, where upon collisions the surface will shatter. I have implemented a slight delay to the shatter to tolerate reaction time from the players. The surfaces also progresses more advanced into the game's interactivity as it can also be purposed as the terrain of the map.
The bomb influences almost all objects in the game from destructible assets, to enemy AIs, and to the player. The detonation of the bomb will create significant impacts to enemy AIs. However, the trade-offs of the luxury to this advantage for the players are that the bombs will destroy almost everything in its proximity, including the players.
To achieve an d simulate the explosion effects, I wrote a custom script for the bomb involving OverlapSphere().
THE MENU SCENE
I created this menu scene in intends to embed all the motifs of out game. I composited the scene with slow motion to achieve a grand, epic, and cinematic finish.
Get Blue Wheels
Leave a comment
Log in with itch.io to leave a comment.