THE ROAD RUNNER TUTORIAL 12: PORTING THE GAME TO ANDROID

Check out the gameplay of the finished game below:



What would this tutorial be good if we didn't had the luxury to play the game that we learned to develop in a mobile device, moreover, our tutorial series was inspired by the very successful games Temple Run, Subway Surfers.

We will be adding this game, the ability to be controlled by accelerometer/touch of a mobile device. In brief we will be learning how to port this game from a Windows platform to Android platform, of course you can follow the same procedure to port it ot iOS.

All we have to do to port this game to Android is change the PlayerControl script.

Before we go on to add some code to make it work with Android input, we will understand some facts about the Android/mobile input.
The Unity Input script reference says that, it assumes the X-axis parallel to the horizontal(shorter) side Y axis parallel to the vertical(longer) side and Z-axis is the one which pierces through the phone.


Now, we will see how to convert the Input.GetAxis to accelerometer control.When the game Starts we will save the Input.acceleration as a reference, zero reference that is. During the gameplay, we subtract the Input.acceleration with this zero reference and use the resulting X for the Input.GetAxis("Horizontal").

To implement this in our script we need to add some variables:


Vector3 zeroAcc;  //zero reference input.acceleration
 Vector3 currentAcc;  //In-game input.acceleration
 float sensitivityH = 3; //alter this to change the sensitivity of the accelerometer
 float smooth = 0.5f; //determines how smooth the acceleration(horizontal movement, in our case) control is
 float GetAxisH = 0;  //variable used to hold the value equivalent to Input.GetAxis("Horizontal")

In the Start function, we will capture the Input.acceleration in the reference variable and set the current acceleration to zero.
zeroAcc = Input.acceleration;
currentAcc = Vector3.zero;

The Update function is where we track the accelerometer input and the touch(tap to jump) input.

currentAcc = Vector3.Lerp(currentAcc, Input.acceleration-zeroAcc, Time.deltaTime/smooth);
GetAxisH = Mathf.Clamp(currentAcc.x * sensitivityH, -1, 1);
int fingerCount = 0;
foreach (Touch touch in Input.touches) {
 if (touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled)
  fingerCount++; 
 }

We get the current acceleration by using the Lerp function, Lerp function is basically used to find a point between the two end points, the result of which is being clamped by the value of the third parameter i.e. between 0 to 1.
Since we are interested in only the horizontal movement we only calculate the X-axis movement by clamping the value between -1 and 1.

Next we check if the screen is tapped by the user, this tap makes the character jump. There are various touch phases like TouchPhase.Began, TouchPhase.Ended and others.
For the touch/tap to be counted, the phase has to be other than Ended and Cancelled, for instance, it can be Began, Stationary.

This is all the logic we need. Now we need to use these values instead of the standard Input class.

The moveDirection will get it's value from

moveDirection = new Vector3(GetAxisH, 0, 0);

Finally, we make the character jump if the fingerCount value is greater than zero i.e., if there is a tap detected/swipe.
if (fingerCount >= 1){
    animation.Stop("run");
    animation.Play("jump_pose");
    
    jumpSound.Play();
    gameObject.GetComponent().enabled = false;
    moveDirection.y = jumpSpeed;
   }

Add these changes in the existing script. You might ask, won't there be a conflict between the Touch and the Keyboard input? Well, this is when the Preprocessor Directives come into picture.
We use the

#if UNITY_ANDROID
      //some code that we want to be executed for Android platform only
#endif

The complete script with both the Android and Keyboard input is as below:

using UnityEngine;
using System.Collections;

public class PlayerControl : MonoBehaviour {
 // Use this for initialization
 
 public GameControlScript control;
 CharacterController controller;
 public float speed = 6.0f;
 public float jumpSpeed = 8.0f;
 public float gravity = 20.0f;
 private Vector3 moveDirection = Vector3.zero;

 public CountdownScript count;  //CountdownScript instance
 public PauseMenuScript pause;  //PauseMenuScript instance
 //audio source reference variables
 public AudioSource powerupCollectSound;
 public AudioSource jumpSound;
 public AudioSource snagCollectSound;

 #if UNITY_ANDROID
 Vector3 zeroAcc;  //zero reference input.acceleration
 Vector3 currentAcc;  //In-game input.acceleration
 float sensitivityH = 3; //alter this to change the sensitivity of the accelerometer
 float smooth = 0.5f; //determines how smooth the acceleration(horizontal movement, in our case) control is
 float GetAxisH = 0;  //variable used to hold the value equivalent to Input.GetAxis("Horizontal")
#endif

 //start 
 void Start () {
  //Debug.Log("Inside player control script start");
  controller = GetComponent<CharacterController>();
  #if UNITY_ANDROID
  zeroAcc = Input.acceleration;
  currentAcc = Vector3.zero;
#endif
 }
 
 // Update is called once per frame
 void  Update (){
  //accelerometer and touch detection
#if UNITY_ANDROID
  currentAcc = Vector3.Lerp(currentAcc, Input.acceleration-zeroAcc, Time.deltaTime/smooth);
  GetAxisH = Mathf.Clamp(currentAcc.x * sensitivityH, -1, 1);
  int fingerCount = 0;
  foreach (Touch touch in Input.touches) {
   if (touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled)
    fingerCount++; 
  }
#endif

  //check if grounded and countdown is done with
  if (controller.isGrounded && count.isCountDown  ) {
    // We are grounded, so recalculate
    // move direction directly from axes
    animation.Play("run");
    //check if game is paused
    if(pause.paused==false)
     gameObject.GetComponent<AudioSource>().enabled = true;
    else
     gameObject.GetComponent<AudioSource>().enabled = false;
    moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, 0);
    #if UNITY_ANDROID
    moveDirection = new Vector3(GetAxisH, 0, 0);
#endif
    moveDirection = transform.TransformDirection(moveDirection);
    moveDirection *= speed;

    jumpSound.Stop();
    #if UNITY_ANDROID
   if (fingerCount >= 1){
    animation.Stop("run");
    animation.Play("jump_pose");
    
    jumpSound.Play();
    gameObject.GetComponent<AudioSource>().enabled = false;
    moveDirection.y = jumpSpeed;
   }
#endif
    if (Input.GetButton ("Jump")) {
     animation.Stop("run");
     animation.Play("jump_pose");
     
     jumpSound.Play();
     gameObject.GetComponent<AudioSource>().enabled = false;
     moveDirection.y = jumpSpeed;
    }

   }
  //disable run sound if game is over
  if(control.isGameOver){ 
   gameObject.GetComponent<AudioSource>().enabled = false;
  }
  // Apply gravity
  moveDirection.y -= gravity * Time.deltaTime;
  
  // Move the controller
  controller.Move(moveDirection * Time.deltaTime);
 }
 
 void OnTriggerEnter(Collider other){
  if(other.gameObject.name == "Powerup(Clone)")
  {
   powerupCollectSound.Play();  //play powerup collected sound
   control.PowerupCollected();
  }
  else if(other.gameObject.name == "Obstacle(Clone)")
  {
   snagCollectSound.Play();  //play snag collected sound
   control.AlcoholCollected(); 
  }
  Destroy(other.gameObject); 
 } 
}

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 7: Adding Gameplay Logic
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
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

11 comments:

  1. Why when i add the #if android stuffs in the play mode he cant walk to the left or right?

    ReplyDelete
  2. Hi! Sujit please help us to switch controls in this game fro gyro to swipe e.g.if we swipe up player jumps ,swipe down player slides ,swipe right player moves right and swipe left. Thanx.

    ReplyDelete
    Replies
    1. Check the below link on Swipe Controls. This should give provide you a platform of what you want to achieve..
      http://www.thegamecontriver.com/2014/08/unity3d-swipe-input-for-touch-screen.html

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

    ReplyDelete
  4. Hai Sujith Your tutorial is awesome, Its very helpful for me and I am a noob in unity, I am trying to do a racing game in unity android for that how can i destroy my car when it collide another vehicles or wall ?? Please help me

    ReplyDelete
  5. How to port this game to Windows phone platform ???

    ReplyDelete
  6. did you mean "if(count is CountDownScript){}"

    ReplyDelete
  7. This post gives best material to learn the processes of porting the game to Android. As a Gaming software developer, I get many knowledge from this post. Event App Android

    ReplyDelete
  8. do you still have your unitypackaage file?

    ReplyDelete