Please check the new and updated blog at http://www.dgkanatsios.com
Current blog post can be found at http://dgkanatsios.com/2014/07/28/angry-birds-clone-in-unity-3d-source-code-included-3/
Last time I posted something around Angry Birds was 3 years ago, when I blogged about a 5’ creation of Angry Birds with 0 lines of code using XAML, Behaviors and Expression Blend. Well, it wasn’t exactly a clone, but it simulated on how to implement some basic dragging/collision/particles functionality with the Farseer Physics Engine. This blog post serves as an attempt to create a single level (with reusable components, long live the prefab!) of Angry Birds using Unity 3D platform. The game features a single level, 3 birds for the user to throw, 3 pigs that the user has to destroy, parallax backgrounds, a maximum strength for the bird (we cannot drag the bird more than a predefined distance), animations with the help of the excellent GoKit library, a projection of the trajectory of the bird (so we can more or less predict where it’ll land), the user’s ability to drag the screen to see the entire scene, pinch to zoom ability (for multitouch devices) and a small trail that is left by the bird when it’s thrown.
Game scene in the Unity editor
The projected trajectory
The trail left by the bird
Before we begin, let’s mention the necessary stuff! Angry Birds, the red birds and the pigs are all registered assets trademarks of Rovio and this blog post serves no other than education purposes.
Game code was written in Visual Studio (check the free Community edition here). For debugging purposes, don't forget to check the Visual Studio tools for Unity here.
Onto the game! It’s a pretty big game, we’ll attempt to cover most of the stuff. We’ll begin with examining our scene in the editor. As always, it would be better if you opened the game in Unity and examined all relevant parts as the post walks you through them.
On our scene, we have
We should mention that the ground graphics for the game were found here http://opengameart.org/content/forest-themed-sprites (opengameart is an awesome place!), the birds and the pigs were found here http://jared33.deviantart.com/art/Angry-Birds-OCs-Sprites-Update-340030555 andhttp://chinzapep.deviantart.com/art/Small-Helmet-Pig-Sprites-315726760, slingshot was self-drawn, the wooden pieces were found herehttp://angrybirds.wikia.com/wiki/File:INGAME_BLOCKS_WOOD_1.png, the forest backgrounds were found herehttp://opengameart.org/content/hd-multi-layer-parallex-background-samples-of-glitch-game-assets and the sounds come fromwww.freesound.org. Both assets from wikia.com were spritesheets, so we used Unity’s sprite editor to automatically slice them.
Let’s analyze the scripts. We’ll start with the easier ones and continue with the most complicated!
The brick script checks for collisions. It has a “health” variable which is decreased proportionally to the collision object’s velocity. If health drops below zero, then the brick is destroyed.
The Bird script starts by disabling the bird’s trail renderer since we do not want it to show before the bird gets thrown, then it makes the bird kinematic (no gravity before departure!), it increases the radius of the collider and sets the initial state for the bird.
On the FixedUpdate (since we don’t want this every frame) we check if bird’s speed is very small and the bird has been thrown from the slingshot. This means that is has completed its fall and stays idle. We proceed in destroying the bird after 2 seconds.
We also have an OnThrow method (that is called by the slingshot script) that makes the bird non-kinematic, decreases the collider’s radius and enables the trail renderer.
The camera follow script moves the camera along the thrown bird’s movement. It clamps the position so that camera’s X position is never out of scene bounds.
The camera move script allows the user to drag the sceen via touch or mouse when the bird is idle and waiting to be thrown, so that she can see the entire scene and estimate where to hit. The script compares mouse coordinates frame by frame and moves the camera accordingly. We also use a timer so that a single tap will not modify camera’s transform and we’re adding some dragSpeed every frame to simulate an accelerated drag by the user. X and Y camera values are clamped so that the user doesn’t drag the camera out of scene bounds.
This script was found here http://unity3d.com/pt/learn/tutorials/modules/beginner/platform-specific/pinch-zoom (Unity site has some awesome resources). It basically does a frame by frame touch coordinates comparison and modifies the size of the camera to simulate zoom in/zoom out. Again, values are clamped.
Destroyer script checks for trigger collisions (remember that destroyer has a trigger collider!) and will destroy any bird, pig or bricks that it collides with. You may wonder “Why do you use Pig or Brick, only bird will hit it!”. Answer is “yes”, but, nevertheless, you don’t know what will happen when the game is played by millions of users so why not code it and be on the safe side? :)
The Pig script checks for collisions. If the pig is hit by a bird, the pig is immediately destroyed. If it is hit by something else (such as another pig or brick) then we calculate the damage. If it is below a threshold, then we change the pig’s sprite (we show the one with the black eyes). Again, if health drops below zero, the pig is destroyed.
The parallax scrolling script is applied to each of the 3 background images we have in our scene. It gets a reference to the camera and moves the respective background proportionally to the camera’s movement (delta), with the proportion being determined by a ParallaxFactor variable (we obviously need to set this in a different value for each of our backgrounds to achieve the parallax effect).
The game manager script does the following things to get the game started
The update method checks for the gamestate enum value
The AllPigsDestroyed method checks if all the pigs are null, i.e. they have been destroyed.
The AnimateCameraToStartPosition method animates the camera to the starting position. This occurs after we’ve thrown the bird and everything has stopped moving. When the camera’s movement has been completed, we run a check to determine the state of the game. If all pigs have been destroyed, then the user has won. If she hasn’t won, the game uses the next bird for throwing, if available. If no more birds are available, then the user has lost.
The AnimateBirdToSlingshot method takes the next bird for throwing and places it in the correct position. When completed, slingshot is enabled and the bird is ready to be thrown.
The BirdThrown event handler lets the camera follow script that the current bird is the one to follow. Camera will move depending on the bird’s position.
The AutoResize method scales the GUI whereas the OnGUI method shows some simple labels depending on the game state.
The slingshot script handles all the interactions made when the bird is tied to the slingshot. It begins by initiating some variables.
On the Start method, it sets the sorting layer for our line renderers (since you currently cannot do it in the editor) and calculates the middle point of the slingshot, that is the point that has an equal distance with the two parts of the slingshot.
The Update method is somewhat big, so we’ll analyze it case by case. When the slingshot is on the idle state it transfers the bird to the correct position, displays the slingshot slings line renderers. If the user taps and the tap is inside the bird’s collider (remember that the bird’s collider is bigger before it’s thrown!) we change the state.
On the user pulling state, we have the user keep dragging the bird. We calculate the distance between the bird and the slingshot’s middle point. If it’s large enough, we do not let the user pull it more. If it’s in the “1.5” range, it’s OK so we move the bird to the desired position. Finally, we display the projected bird trajectory when thrown.
How does this “max distance” thing work? Take a look at the following sketch. B is the middle point of the slingshot, C is the position where the user is dragging the bird (more than 1.5f distance) and A is the (unknown) position where we want to place the bird.
If we were to replace the above sketch with a cartesian level, this would be like this
So, we’re looking for the OA vector. We know OC and OB and we know that BC is BA normalized multiplied by 1.5f (that’s the max distance we set). Some vector maths are on the way!
BA = BC.normalized * 1.5f <=> (since BA + OB = OA)
OA – OB = BC.normalized * 1.5f <=> (since BC = OC – OB)
OA = (OC – OB).normalized * 1.5f + OB
Since we know OC as the location that the user is tapping and OB is the slingshot middle vector, we can replace them and find the max drag location
var maxPosition = (location - SlingshotMiddleVector).normalized * 1.5f + SlingshotMiddleVector;
When the user leaves the bird, we check if she has pulled long enough. If she has, we throw the bird. If not, we animate it back to the starting/idle position.
The ThrowBird method calculates the velocity of the thrown bird (which is proportional to the amount of drag that bird has endured) and throws the bird by modifying its ridigdbody velocity property. An alternative way would be to add a force to it. In the end, we raise the BirdThrown event so that the game manager takes notice of bird’s throw.
The DisplaySlingshotLineRenderers method sets the correct position for the slingshot “strings” that the bird is attached to whereas the SetSlingshotLineRenderersActive and SetTrajectoryLineRenderers methods simply enable/disable the respective renderers.
The DisplayTrajectoryLineRenderer is used to display the trajectory of the bird when thrown. As we remember from our physics class for throws
- the horizontal space is calculated like spaceFinalX = spaceInitialX + velocityX * time (no acceleration in the X axis)
- the vertical space is calculated like spaceFinalY = spaceInitialY + velocityY * time + 0.5 * accelerationY * timeSquared
However, accelerationY is equal to gravity! Consequently, in vector math this would be space = spaceInitial + velocity * time + 0.5 * gravity * timeSquared. Which leads us to the below formula. We use 15 segments which are more than enough for the projected trajectory.
If you’ve made it this far, you certainly deserve a break! Feel free to take one, then
- download or browse the source code here on GitHub https://github.com/dgkanatsios/AngryBirdsClone
- play the game via Unity’s web player here http://unitysamples.azurewebsites.net/angrybirdsclone.html
If you are new to Unity, check out a cool intro video series here. For instructions on how to deploy your existing game onto Windows Store/Phone, check out the Microsoft Virtual Academy video here: http://www.microsoftvirtualacademy.com/training-courses/porting-unity-games-to-windows-store-and-windows-phone