THE ROAD RUNNER TUTORIAL 7: ADDING GAMEPLAY LOGIC

Check out the gameplay of the finished game below:



The Gameplay, perhaps, is considered the heart of any game. And why not? After all, it is the one makes the game. You might play a game with a superb gameplay and a mediocre graphics, but not the other way around.

If you have followed the previous posts then you would have a game where in the character runs endlessly and there are some some random spawning of the snags and powerups. The character manages to collect these things, however, nothing happens on collecting. Most importantly there is no game over condition to our game 

In this post, we will be address the above mentioned issues, well not really issues(but yeah, who cares!). Arrrrgh..

Well, before we get into any coding, it is better to know the gist on which our gameplay logic is based.
To put it in simple words, On the start of the game, our player will have some pre earned time, which of course will not persist eternally. To make it eternal, (well, if the player(GAMER) is good enough, then, why not?) you need to collect the powerups. Of course there are these snags that we added in the last post(and perhaps we will add some more as we move on, hopefully!), which will hinder your progress.
For now, we will extend the time on collecting the powerup and reduce the time on collecting the snags. If the time becomes zero, then.. Yeah, I know you people will have guessed it.  

To get started, create a C# script and name it, say, GameControlScript. Attach it to the GameController gameobject. 

As suggested by one of our mate, instead of giving all the code and explaining it later, we will do it the reverse way.

The first thing that we will be needing is some variables:

float timeRemaining = 10;   //Pre-earned time
 float timeExtension = 3f;   //time to extend by on collecting powerup
 float timeDeduction = 2f;   //time to reduce, on collecting the snag
 float totalTimeElapsed = 0;   
 float score=0f;      //total score
 public bool isGameOver = false;  

I believe these does not need any further explanation(Comment below if you have any issues with these.)

Now, we will create two methods as below:

public void PowerupCollected()
 {
  timeRemaining += timeExtension;   //add time to the time remaining
 }
 
 public void AlcoholCollected()
 {
  timeRemaining -= timeDeduction;   // deduct time
 }

These methods basically add or deduct time on collecting of powerup or snag respectively.

The Update function will look something like:

void Update () { 
  if(isGameOver)     //check if isGameOver is true
   return;      //move out of the function

  totalTimeElapsed += Time.deltaTime; 
  score = totalTimeElapsed*100;  //calculate the score based on total time elapsed
  timeRemaining -= Time.deltaTime; //decrement the time remaining by 1 sec every update
  if(timeRemaining <= 0){
   isGameOver = true;    // set the isGameOver flag to true if timeRemaining is zero
  }
 } 
Here, we calculate the total time the user has managed to survive, and with that we calculate the score.
If the time remaining is less than one, then we end the game(i.e. we set the isGameOver flag to true.) and we move out of the function, if so.

Next, we use the OnGUI function to display the score, time remaining and the game over menu. 


void OnGUI()
 {
  //check if game is not over, if so, display the score and the time left
  if(!isGameOver)    
  {
   GUI.Label(new Rect(10, 10, Screen.width/5, Screen.height/6),"TIME LEFT: "+((int)timeRemaining).ToString());
   GUI.Label(new Rect(Screen.width-(Screen.width/6), 10, Screen.width/6, Screen.height/6), "SCORE: "+((int)score).ToString());
  }
  //if game over, display game over menu with score
  else
  {
   Time.timeScale = 0; //set the timescale to zero so as to stop the game world
   
      //display the final score
   GUI.Box(new Rect(Screen.width/4, Screen.height/4, Screen.width/2, Screen.height/2), "GAME OVER\nYOUR SCORE: "+(int)score);
   
   //restart the game on click
   if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+Screen.height/10+10, Screen.width/2-20, Screen.height/10), "RESTART")){
    Application.LoadLevel(Application.loadedLevel);
   }
   
   //load the main menu, which as of now has not been created
   if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+2*Screen.height/10+10, Screen.width/2-20, Screen.height/10), "MAIN MENU")){
    Application.LoadLevel(1);
   }
   
   //exit the game
   if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+3*Screen.height/10+10, Screen.width/2-20, Screen.height/10), "EXIT GAME")){
    Application.Quit();
   }
  }
 }
The code is fully commented and I believe is quite straightforward (Again, any issues? Comment below)
The only thing, you might not get is the usage of the dimensions of the box, button and labels. Well, may be, I will come up with a post explaining how this has been calculated, soon.

There is another thing that you need to do to get this working, you need to call the two functions- PowerupCollected and AlcoholCollected from your PlayerControl script that we created in the last post.
Follow the below steps to achieve this:
Add a reference to the GameControlScript as below

public GameControlScript control;

If you save this and click on to the 3rd Person Controller gameobject in the Hierarchy, you will notice that there is a Control which is expecting a gameObject.


Drag the GameController object into this empty field or you can even click on the small circle with a dot and a new window will pop up, select the GameController object.


And the OnTriggerEnter function of the PlayerControl will look something like:

void OnTriggerEnter(Collider other)
 {               
  if(other.gameObject.name == "Powerup(Clone)")
  {
   control.PowerupCollected();
  }
  else if(other.gameObject.name == "Obstacle(Clone)" && isGrounded == true)
  {
   control.AlcoholCollected();
  }
  
  Destroy(other.gameObject);
  
 }

Keep in mind, clicking on the main menu button doesn't do anything yet as we have not created a main menu. The Exit button won't do any magic either, because we are trying this in the editor. Don't worry, it will work fine if you build this game and test(If you didn't get some of the words mentioned above, chuck it, as of now.)

If you test this out, you will be surprised, I hope. If not, well, great. You are a champ! But at least be happy.
There is one issue however, on clicking the restart button, the scene loads, but it appears to be dead. To resolve this add a Start function as below:

void Start(){
  Time.timeScale = 1;  // set the time scale to 1, to start the game world. This is needed if you restart the game from the game over menu
 }

That is pretty much it, everything is just about perfect.

The complete GameControlScript will look like:

using UnityEngine;
using System.Collections;

public class GameControlScript : MonoBehaviour {
 
 float timeRemaining = 10;
 float timeExtension = 3f;
 float timeDeduction = 2f;
 float totalTimeElapsed = 0;
 float score=0f;
 public bool isGameOver = false;

 void Start(){
  Time.timeScale = 1;  // set the time scale to 1, to start the game world. This is needed if you restart the game from the game over menu
 }

 void Update () { 
  if(isGameOver)
   return;

  totalTimeElapsed += Time.deltaTime;
  score = totalTimeElapsed*100;
  timeRemaining -= Time.deltaTime;
  if(timeRemaining <= 0){
   isGameOver = true;
  }
 }
 
 public void PowerupCollected()
 {
  timeRemaining += timeExtension;
 }
 
 public void AlcoholCollected()
 {
  timeRemaining -= timeDeduction;
 }

 void OnGUI()
 {
  //check if game is not over, if so, display the score and the time left
  if(!isGameOver)    
  {
   GUI.Label(new Rect(10, 10, Screen.width/5, Screen.height/6),"TIME LEFT: "+((int)timeRemaining).ToString());
   GUI.Label(new Rect(Screen.width-(Screen.width/6), 10, Screen.width/6, Screen.height/6), "SCORE: "+((int)score).ToString());
  }
  //if game over, display game over menu with score
  else
  {
   Time.timeScale = 0; //set the timescale to zero so as to stop the game world
   //display the final score
   GUI.Box(new Rect(Screen.width/4, Screen.height/4, Screen.width/2, Screen.height/2), "GAME OVER\nYOUR SCORE: "+(int)score);
   
   //restart the game on click
   if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+Screen.height/10+10, Screen.width/2-20, Screen.height/10), "RESTART")){
    Application.LoadLevel(Application.loadedLevel);
   }
   
   //load the main menu, which as of now has not been created
   if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+2*Screen.height/10+10, Screen.width/2-20, Screen.height/10), "MAIN MENU")){
    Application.LoadLevel(1);
   }
   
   //exit the game
   if (GUI.Button(new Rect(Screen.width/4+10, Screen.height/4+3*Screen.height/10+10, Screen.width/2-20, Screen.height/10), "EXIT GAME")){
    Application.Quit();
   }
  }
 }
}

The PlayerControl script should be like the below script once you have added the changes:

using UnityEngine;
using System.Collections;
 
public class PlayerControl : MonoBehaviour {
 public GameControlScript control;
 CharacterController controller;
 bool isGrounded= false;
 public float speed = 6.0f;
 public float jumpSpeed = 8.0f;
 public float gravity = 20.0f;
 private Vector3 moveDirection = Vector3.zero;
 
 //start 
 void Start () {
  controller = GetComponent<CharacterController>();
 }
  
 // Update is called once per frame
 void Update (){
  if (controller.isGrounded) {
   animation.Play("run");            //play "run" animation if spacebar is not pressed
   moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, 0);  //get keyboard input to move in the horizontal direction
   moveDirection = transform.TransformDirection(moveDirection);  //apply this direction to the character
   moveDirection *= speed;            //increase the speed of the movement by the factor "speed" 
    
   if (Input.GetButton ("Jump")) {          //play "Jump" animation if character is grounded and spacebar is pressed
    animation.Stop("run");
    animation.Play("jump_pose");
    moveDirection.y = jumpSpeed;         //add the jump height to the character
   }
   if(controller.isGrounded)           //set the flag isGrounded to true if character is grounded
    isGrounded = true;
  }
 
  moveDirection.y -= gravity * Time.deltaTime;       //Apply gravity  
  controller.Move(moveDirection * Time.deltaTime);      //Move the controller
 }
 
 //check if the character collects the powerups or the snags
 void OnTriggerEnter(Collider other)
 {               
  if(other.gameObject.name == "Powerup(Clone)")
  {
   control.PowerupCollected();
  }
  else if(other.gameObject.name == "Obstacle(Clone)" && isGrounded == true)
  {
   control.AlcoholCollected();
  }
   
  Destroy(other.gameObject);
   
 }
}


Note: The Score and the Time left might not be visible because of the sky background. The end menu buttons might overlap. These issues will be resolved in the next post when we create a custom GUI skin.

This is it for this time, I'll catch you the next time around.

Download the completed version of this game from the Resources page.

The Road Runner Tutorial 1: Setting Up The World
The Road Runner Tutorial 2: Setting Up The World Continued
The Road Runner Tutorial 3: Adding A Character To Our Game
The Road Runner Tutorial 4: Set The Ground Moving
The Road Runner Tutorial 5: Adding Snags and Powerups to Our Game
The Road Runner Tutorial 6: Collecting The Snags and Powerups
The Road Runner Tutorial 8: Creating a Custom GUI Skin
The Road Runner Tutorial 9: Creating a Pause Menu
The Road Runner Tutorial 10: Adding Countdown and Main Menu
The Road Runner Tutorial 11: Adding Sound Effects To Our Game
The Road Runner Tutorial 12: Porting The Game To Android
Share on Google+

About Sujit Horakeri

Sujit Horakeri is a game freak just like any other next door guy you would come across. He is a Web Developer by Profession, Game Developer by Choice.
Connect with him on:
    Blogger
    Facebook

26 comments:

  1. error CS1525: Unexpected symbol `timescale', expecting `,', `;', or `='
    /: error CS1525: Unexpected symbol `.', expecting `,', `;', or `='
    error CS1525: Unexpected symbol `.', expecting `,', `;', or `='
    : error CS1525: Unexpected symbol `.', expecting `,', `;', or `='
    error CS1525: Unexpected symbol `GAME OVER
    YOUR SCORE: ', expecting `,', `;', or `='

    i get these 5 errors in line 50 and 53..if you could please help that would be amazing!
    also where do you put the code public GamControlScript control; in the player control script. Thank you this tutorial has been amazing

    ReplyDelete
    Replies
    1. that is due to the comment line, check out the script again and make sure you have copied it correctly..The error will no longer appear.
      I have also added the PlayerControl script for your reference.
      If I were you I would learn the basics of c# programming first and then concentrate on building games.

      Delete
  2. I got rid of the errors but still can't figure out how to get the control to appear in the player script. I placed the code public GamControlScript control; in the GameControlScript like you said but still can't get it to appear. What am i doing wrong?

    ReplyDelete
    Replies
    1. Try copying the complete script posted above and add drag the GameController gameobject to the reference in the Inspector..

      Delete
    2. Also make sure that the GameControlScript is attached to the GameController gameobject...

      Delete
    3. Ok I've tried that and i just get the error the name control doesn't exist in the current context

      Delete
    4. ok it was my typo, I had typed public "GamControlScript control;" instead of "public GameControlScript control;" (note the missing 'e' i.e. Gam instead of Game). I have corrected the script, these are now tested by me and should work fine.
      Remember the Script name in the assets->scripts folder are same as the class name i.e. GameControlScript and PlayerControl...
      And kindly let me know if all the errors were resolved and you were able to get the desired output.

      Delete
    5. I'm havin the same problem , the control won't appear in the gamecontrol script

      Delete
    6. Same problem, I tried updating the other scripts but it is still not there.

      Delete
    7. This comment has been removed by the author.

      Delete
    8. And also the same problem over here, please could you solve this?
      Or do you know another way? maybe like Inheritance (object-oriented programming).

      Delete
  3. Excellent work..very helpful for beginners.Will you please share the unity project of this game.I am having a problem of appearing powerups prefabs

    ReplyDelete
    Replies
    1. The reason why we dont upload the unity package is destroys the hunger to follow these tutorials and learn from them. If you are facing some issue comment here we will together solve it, and perhaps someone else will benefit from it as well..

      Delete
    2. I'm havin the same problem , the control won't appear in the gamecontrol script

      Delete
  4. Please help. Every time I move my character it doesn't stay in one place. it moves with the powerups and obstacles :(

    ReplyDelete
    Replies
    1. I'm sorry, I didn't get you. Could you be more clear so that we can help you out?

      Delete
  5. Please help me. My character can not move horizontal when i tap right or left. The character still look to the right or left and even can look back. How to slove it to make the movement like in your tutorial, Thanks

    ReplyDelete
  6. hi dear...1st problem is...the powerups and obstcls are not showing on screen when i hit play button...and 2nd problem is... the score is contineously increasing...and go to 1001 and then time up...how to control it...

    ReplyDelete
  7. can you make a video tutorial of this page only? i'm having trouble with scripts , reference behavior missing and other errors , please please make a video tutorial for this only

    ReplyDelete
  8. Hi ! Fristly, thanks for putting up this tutorial.

    I have an issue with the timer, which does not update when picking up a powerup or an alcohol. It remains the initial 10 seconds while another timer (updated) gets under the first.

    The problem is the score is always 1000.
    I hope you can help me !

    ReplyDelete
  9. Hi there Got stuck. Cannot drag the GamecontrolScript to control its not allowing me. Please help

    ReplyDelete
  10. Assets/Scripts/OnGUI.cs(5,14): error CS0542: `OnGUI.OnGUI()': member names cannot be the same as their enclosing type.i was facing this problem can you plse help me out of this

    ReplyDelete
  11. from when i started the links do not work

    ReplyDelete
  12. i have added line public GameControlScript in player control and control box has appeared in inspector but i am unable to add gamecontrolscript in this box because it is not accepting it

    ReplyDelete
  13. Hi do you still have the unity package for this thanks

    ReplyDelete