+46 70 468 33 45 tobnyl@gmail.com

PieRats

Game Design / Scripting

About the Game

Ahoy! Welcome to the Caribbean, where pirates are confronting each other on the sea. But wait a second I hear you say, these are no ordinary pirates? You are correct, these are PieRats!

You play as Captain Rat, and it is your task to navigate your pie-rat ship and to sink your opponent by firing pies from the ship’s cannon!

Specifications

Platform: PC

Engine: Unity 5

Development Time: 1 weekend

Team: 1 Designer, 1 3D Artist, 1 2D Artist

Year: 2017

My Role

The game was made at Global Game Jam 2017 at Tekniska museet in Stockholm. The theme for the jam was “Waves”, which comes in many variations. My team consisted of me as a designer and scripter, a 3D artist, and a 2D artist. We started by discussing the theme and what we should do for a game. First, we were into a more of a futuristic game, but then we started discussing pirate ships, and then we came up with the brilliant idea pie-rats…

I implemented all of the scripting in the game. I made scripts for steering the ship and the cannon, the behavior of the cannonballs (read pies), and a camera script for following the ship. I made the logic for the start, instructions, and loading screen. I also implemented all of the sounds in the game. We used a plugin called “Fracturing & Destruction” which takes a mesh and divides it into chunks which then can be triggered to explode via script.

During the weekend actual visitors of the museum came and tested our games. It was nice seeing children enjoy our games and that their parents were impressed with what we had accomplished in such a short time. At the end of the weekend, we uploaded the game to Global Game Jam’s site and held a short presentation for the other jammers and visitors.

  • Game Design
    • Core Mechanics
  • Scripting
    • Ship Movement, Cannonball, Camera
  • Sound Design
Player 1 trying to hit player 2.

Post-mortem

This was the first time I used physics for movement in a game. I learned the different aspects of a rigid body, how to apply forces, the difference between force and impulse, and the meaning of kinematic. I realized the benefit of building hierarchical objects and how child objects move relative to each other. I also learned how to use linear interpolation for making a camera feel more dynamic.

The Ship

Steering the ship is done by using the left thumbstick. Rotating the cannon is done by moving the right thumbstick left or right. By moving the right thumbstick up or down you aim the cannon. To fire from the cannon you press the right bumper button. The aiming of the cannon is relative to the base of the cannon, and the rotation of the base is relative to the ship.

The camera rotates along with the rotation of the cannon, e.g. if you rotate the cannon to the right you won’t be able to see where you are sailing. This was a design decision we made for it to feel more challenging. Also, when aiming straightforward it’s hard to see where you are aiming because of the sail. This was a thing we first realized after we had finished the ship mesh. At first, we thought this was bad and we were discussing how to solve it. In the end, we decided to keep the sail because it made it harder to just go straight forward and shoot in the same direction as the ship moved, and that would have made the rotation of the cannon pointless.

The ship has a Rigidbody Component and all of the ship’s movements are made by using physics and is done in the FixedUpdate method. The hierarchy of the ship’s Prefab is shown in the image to the right. The base of the cannon is a child to the ship, the cannon is a child to the base. The cannon itself also has a child “CannonBallPosition”, which is an empty GameObject used as a spawn point for the cannonballs.

Ship prefab hierarchy.

Movement

All the movement is done in Unity’s FixedUpdate event. It is only meant for handling movement of rigid bodies, e.g. applying forces to make it move. To move the ship the method AddForce is used. For rotating the ship AddTorque is used. Torque is the same as rotational force.

If the player has pressed the fire button the variable “_instantiateCannonBall” will be set to true. The logic for this, however, is made in the Update method. The reason for this is that FixedUpdate doesn’t run as often as Update, if you check buttons in FixedUpdate latency will be noticed. Back in FixedUpdate, a check is made if “_instantiateCannonBall” is true, and if so, a cannonball will be instantiated. AddForce is used for this too but with the option ForceMode.Impulse which means that it will only apply the force once. Later, I’ve learned that this can be simplified, if you use ForceMode.Impulse it doesn’t need to reside in FixedUpdate since the idea of it is only to fire once.

The Cannon

The rotation of the cannon is handled in Update. A new Y angle, i.e. rotation around the up-axis, is calculated based on the horizontal movement of the right thumbstick. This value is then clamped to a min and max value to prevent from rotating the cannon 360 degrees. The new angle is then added with the ship’s current angle, this is needed for making the cannon rotate with the ship’s rotation. I use Quaternion.AngleAxis for converting this angle into a quaternion. By using quaternions you avoid gimbal locks and they are good for interpolation.

Finally, I use Quaternion.Slerp for interpolating from the cannon’s current rotation to the new target rotation. Slerp stands for spherical interpolation and it takes three parameters. The first parameter a, is the start rotation, the second parameter b, is the end rotation. The third parameter is t, or the time. The proper way of using slerp is to assign t a value between 0 and 1. If t is 0 it will return a, if t is 1 it will return b, if t is 0.5 it will return the value in between a and b. But you can use it in another way which I do here. By assigning t a tiny amount each frame and updating the cannon’s rotation it will start to rotate fast and the nearer the rotation gets to the target the slower the rotation will occur. This will make the rotation of the cannon feel more natural since it won’t just instantly stop when you release the thumbstick. A simplified example of slerp is shown below.

  • Slerp(0, 1, 0.5) = 0.5
  • Slerp(0.5, 1, 0.5) = 0.75
  • Slerp(0.75, 1, 0.5) = 0.875
  • Slerp(0.875, 1, 0.5) = 0.9375

Cannonball

If a cannonball hits a ship the following happens. The health of the ship is reduced and particle effects and sounds are instantiated at the location of the hit. If the health of the ship gets below 0 the ship explodes. This is done by using the plugin Fracturing & Destruction which we bought from Unity’s Asset Store. The plugin has a script FracturedObject. The hull of the ship has this script assigned to it. This script has many settings that can be tweaked for how the mesh should be divided into chunks. To make the mesh into chunks one simply hits the compute chunks button. The script has a method called Explode, which takes two parameters. The first parameter is the position, i.e. the point where the explosion shall start. The second parameter is the force.

The other objects of the ship have rigid bodies that are set to kinematic. When the ship explodes these are set to be non-kinematic and a force and a torque is applied to the objects. Since the logic is similar for all the objects I made a method “DestroyGameObject” to make it easier to reuse and tweak the values.

Ship exploding.

Camera

The camera is a separate GameObject it has a script “ThirdPersonCamera”. The script has an inspector variable called “Target”, which is the object that the camera shall follow. The target in the game is set to the base of the cannon on the ship. When the player rotates the cannon the camera will follow that rotation. Another variable “DistanceFromTarget” tells how far from the target the camera shall be.

When the game starts the position of the camera is calculated by taking the target’s position and then subtracting it with the target’s forward vector times “DistanceFromTarget” as shown in the code below. The Y rotation is set to the target’s Y rotation. A reference to the ship is stored for being used later.

For updating the camera I use LateUpdate, which is an event that is called after all other object’s Update calls have been made. This is to ensure that the cannon’s position and rotation have been calculated before the camera’s. First I check if the ship hasn’t exploded if it hasn’t I calculate the new position and rotation. Again by using Quaternion.AngleAxis and slerp.

There are two cameras in the game since it’s a 2 player split-screen game. On Unity’s Camera component there is a setting called Viewport Rect, which lets you configure how much of the screen the camera should occupy. By setting the first camera to start at position 0, 0 and have a width of 0.5 and a height of 1, and the second camera to start at 0.5, 0 and the same width and height you get a vertical split-screen.

GameObject Extensions

One nice feature with C# is the ability to create Extension Methods for already existing types. For this project, I made extension methods for the GameObject class. Extension methods must be marked as static and they must be declared in a static class. The first parameter of an extension method must start with the keyword this and be of the type that the extension is meant for.

Below is an example of a declaration and a call. In this case, I’ve made Instantiate as a part of the GameObject class, reducing the number of parameters needed to be specified. Only position and rotation needs to be passed here.

I also made some overloads, e.g. one where only position is the parameter and where the rotation is set to Quaternion.identity internally.

Another extension method I made is “GetComponentsInChildrenOnly<T>”. By default when using Unity’s GetComponentsInChildren it will also return components from the object it was called from. So I made this little handy method which will only return the actual children for the object. I also made it generic so it works with any class deriving from MonoBehaviour.