The last stage of the character is its code class. The code class will receive input from the PlayerInput class we wrote earlier, so the character can be controlled through the input and filter out the type of GameObjects that collided with from the parent of that collider type.
Perform the following steps:
To start with, navigate to the Assets/Scripts
folder and right-click on it. Select Create and then C# Script. Name it Character
.
Next, we need to give our PlayerInput
class a reference to our character. To perform this, navigate to the Assets/Scripts
folder and double-click on the PlayerInput.cs
file to open it.
At the top of the class, under the SimpleTouch
struct, add the following code:
public Character GameCharacter;
We also need to tie the function that will call GameCharacter
when PlayerInput
gets a new one. At the bottom of the CalculateTouchInput
function, add the following code:
if (GameCharacter != null) { if (!GameCharacter.isDead) { GameCharacter.RecieveInput(touchDistance, touchDirection); } }
At the top of the Character
class, we need to add the bool value that will be used to know whether a character is jumping or not and if a character is dead or alive; we need an int value to know how many coins a character has and if a character should be fading out. The isJumping
bool will be used in the code that we will write to know when the character jumps from the SimpleTouch
type of PlayerInput
. Finally, we will store references to GameCharacterSprite
and CharacterRigidBody
so that we can use them through the use of this class. Add the following code before the Start
function:
[System.NonSerialized] public bool isJumping; [System.NonSerialized] public bool isDead; [System.NonSerialized] public int CoinCount; [System.NonSerialized] public Vector3 RestartLocation; private bool isFadeOut; SpriteRenderer GameCharacterSprite; Rigidbody2D CharacterRigidBody; Animator CharacterAnimator;
[Sytem.NonSerialized]
is a way to declare a public variable and have Unity ignore it when it populates the exposed component variables. It means that isJumping
won't show up in the component list of variables in the editor. It will also prevent this variable from being serialized. Serialization is the process of converting an object to a stream of bytes so that it can be transmitted into memory.
To explain more, let's understand the following terms:
isJumping
: This is the bool value to let both the class and the animator know if the character is jumping. True is jumping, whereas false is not jumping.isDead
: This is a bool value that does exactly what the isJumping
bool does only to check whether the player is dead or not. Again, true is dead, whereas false is not dead.CoinCount
: This specifies how many coins the player has collected. Later on, we will store this value so it doesn't reset every time the player restarts the game.RestartLocation
: This denotes the Vector3
location that we can use to reset the character to after they have died.IsFadeOut
: This is a bool that transitions the character from an opaque state to a translucent state. True is fading to translucent, whereas false is fading to opaque.GameCharacterSprite
, CharacterRigidBody
, and CharacterAnimator
: These are references that we can keep, so we don't have to keep getting them. GameCharacterSprite
is the sprite sheet, CharacterRigidBody
is Rigidbody 2D for the character, and CharacterAnimator
is the animation tree for the character.To set some of these values, we can use the Start
function. Add the following code in the Start
function:
// Use this for initialization void Start () { RestartLocation = gameObject.transform.position; GameCharacterSprite = gameObject.GetComponent<SpriteRenderer>( ); CharacterRigidBody = gameObject.GetComponent<Rigidbody2D>( ); CharacterAnimator = gameObject.GetComponent<Animator>( ); }
This allows you to use all of these references in the other code for this class without having to use the GetComponent
function again and again.
Now, we know that we have to have a couple things for character to be functional. Character needs to take the SimpleInput
struct data from PlayerInput
so that it knows how to move, and the character needs a function to know when it has collided with another GameObject that has a collider.
Under the Update
function in the Character
class, add the following functions:
// Get input data from PlayerInput public void RecieveInput(float Distance, Vector2 Direction) { if (CharacterRigidBody != null && CharacterAnimator != null ) { Direction.x = 0.0f; if (Direction.y > 0.0f && !isJumping) { CharacterRigidBody.AddForce(Direction * Mathf.Clamp(Distance, 0, 200)); CharacterAnimator.SetBool("Jump", true); isJumping = true; } else if ( Direction.y < 0.0f ) { CharacterRigidBody.AddForce(Direction * Mathf.Clamp(Distance, 0, 250)); } } } // When the character collides with another GameObject void OnCollisionEnter2D(Collision2D Col) { if (CharacterAnimator != null) { CharacterAnimator.SetBool("Jump", false); isJumping = false; } }
The RecieveInput
function takes the parameters, the distance of the player's swipe, and the direction of the player's swipe. We need the distance in order to know how "hard" the player swiped on the screen, and we need the direction to know whether the player swiped up or down.
When we receive this data from PlayerInput
, we then need to get ready to use it. To begin, we need a reference to the Character GameObject's Rigidbody 2D component. To have this, we will use gameObject.GetComponent<Rigidbody2D>( )
and store it in a local variable named characterRigidBody
.
We will also need a reference to the animator on character that holds the controller. The controller holds the Jump
variable, which we will set when the character jumps.
We then check to make sure that characterRigidBody
is something by checking that it is not null; null meaning empty. We also perform the same for characterAnimator
.
If we have a reference in
characterRigidBody
to GameObject's Rigidbody 2D, we start by making sure that the X axis in Swipe is set to 0
. This means that the direction will only be up and down, so the player can't jump forward or backward.
We then check whether the Y axis on the Direction parameter is more than 0.0
, meaning the player swiped up if the character is not already jumping. If this is true
, we will add a force to the Rigidbody2D
character.
AddForce
takes two arguments: the direction the force needs to be and how much force to apply to Rigidbody 2D. For us, the direction has already been given and altered. The force amount is calculated using Mathf.Clamp( )
.
Mathf.Clamp( )
is a way to make sure that the force value is within bounds. If we let the distance decide how much force is added to the character always, the character would go shooting off into space if the player swiped the entire screen of their device. So, we will use the clamp function to check whether the swipe distance is less than 200 and use this value. If the swipe distance is more than 200, clamp it down to 200.
After Rigidbody2D
character has a force, we want to update the controller so that it knows to switch to the jumping animation. We do this by setting the Jump
bool value to true with the stored reference set earlier in the function and check to make sure that it exists.
As the character needs to know whether it is jumping, we will set isJumping
to true
here as well. This way if the player tries to swipe while character is already jumping, the condition we have will fail. Also, there will be no force added in the positive Y axis. If we didn't have this check, the player would be able to swipe continuously. Also, the character would continue to jump higher and higher.
If the first condition that checks whether the direction of the swipe is in positive Y and if character is not jumping fails, we check whether the direction of the swipe is in negative Y. The reason we don't check isJumping
here is because it doesn't matter if we swipe down if the player is or isn't currently jumping. If Character is already on the ground, it can't go farther down, and if the character is jumping, a force downwards will make it fall to the ground faster, which is intended in design.
The OnCollisionEnter2D
function is a Unity function designed to manage what happens when colliders collide.
OnCollisionEnter2D
takes a single parameter: the collider that was collided with. For the moment, the only thing we know is that the character can collide with the temporary floor we have on the scene. As a result, we can assume that the only thing to do here is to stop the player from jumping. We start by having the reference back to the character's Animator component and check whether it exists. If it does, we will set the Jump
bool value to false
; to set that the player is no longer jumping, we will also set the isJumping
class variable to false
, so if the player was to swipe upwards again, the AddForce
function gets a call. Also, the character will jump.
We will compare the type of GameObject with the one that is being collided with so that we can decide what to do with the colliding GameObject as well as what to do with the Character GameObject.
If you play the game now, you should see the character transition from jumping and not jumping; this is based on the player input of swiping upwards to jump and the character stopping the jump by hitting the temporary floor
In addition, we will check to make sure that GameCharacter
isn't null (it exists); we also make sure that it is not dead and then we will call GameCharacter.RecieveInput( )
, which will send touchDistance
and touchDirection
to the Character
class, which allows you to control the character based on them.
Perform the following steps:
PlayerInput
file and navigate back to Unity, which should compile the code now.Player Input
.This will give the PlayerInput component of the PlayerInput GameObject a reference to the Character GameObject. This also allows you to use PlayerInput to "communicate" with Character or to be more direct, send the SimpleTouch
struct data of the current touch input from the user to a function of Character so that Character knows what it should do.
Next double-click on the Character.cs
class to open it. Like the PlayerInput
class, you will see the generated code from Unity; this includes the Start
and Update
functions and the two Using
declarations as well.
Now, we need to write a function to kill the character. This will be used when the character collides with an obstacle.
Under the OnCollisionEnter2D
function, add the following code:
// Kills the character public void KillCharacter() { if (!isDead) { if (CharacterRigidBody != null) { CharacterRigidBody.AddForce(new Vector2(Random.Range(-1, 1), 1) * 512); isDead = true; isFadeOut = true; } } }
This function is designed to make sure that the character's Rigidbody 2D component exists—by making sure it is not null—add a force to it in a random direction between the negative one and the positive one on the X axis and the positive one on the Y axis. This means that a force will be added, which will throw the character upwards with the force of the random direct multiplied by 512. We can also set the character as dead, so it won't be able to pick up anything.
To avoid having the character go floating off into space, let's use the isFadeOut
variable to fade the character if it is true. This way the player will see the character fade to transparent before it escapes the screen.
In the Update
function of the character C# class, add the following code:
// Update is called once per frame void Update () { if (isFadeOut) { if (GameCharacterSprite != null) { float currentAlpha = GameCharacterSprite.color.a; currentAlpha -= 0.0085f; if (currentAlpha < 0.01f) { currentAlpha = 0.0f; } Color newColorAlpha = new Color(1.0f, 1.0f, 1.0f, currentAlpha); GameCharacterSprite.color = newColorAlpha; } } }
Let's start by checking whether the isFadeOut
bool value is true
. If so, we can continue to set the alpha of SpriteRenderer
. To set the alpha or fade out the Character sprite, we will need a reference to its SpriteRenderer
component. We can have this by using the gameObject.GetComponent
function. When we have this, we can make sure it exists by verifying that it is not null. We will then set a float variable to the current value of the SpriteRenderer
reference and then subtract a small amount from this.
We will then check
currentAlpha
of the sprite to check whether it is less than 0.01
. If it is, we set currentAlpha
to 0
. Also, we will create a new Color
variable and set the alpha channel to the currentAlpha
value.
Finally, we will set the reference to the SpriteRenderer.color
value to the updated alpha value.
Then, we will need a function that will reset the character back to running. This will be used later on when we create a reset button in the UI. Write the following function under KillCharacter
:
// Revives the character public void ReviveCharacter() { Color resetColorAlpha = new Color( 1.0f, 1.0f, 1.0f, 1.0f ); gameObject.transform.position = RestartLocation; isDead = false; isFadeOut = false; if (GameCharacterSprite != null) { GameCharacterSprite.color = resetColorAlpha; } }
To revive the character is quite easy. We can create a new world location at the 0X
, 0Y
, and 0Z
location (the center of the world) and set the Character GameObject to that location.
We also need to reset the SpriteRenderer.color.a
(alpha) value back to default. To do this, we need to get a reference of SpriteRenderer
and make sure that it exists by making sure that it is not null.
If it exists, we set the color value of SpriteRenderer
to the resetColorAlpha
value, which is 1.0f
red, 1.0f
green, 1.0f
blue, and 1.0f
alpha by default.
We will then set
isDead
to false
, meaning that the character is alive, and set isFadeOut
to false
so that the character does not fade out anymore.