This documentation is intented for developers who are familiar with C# or java

Gamming Framework Base Classes

Below are the base classes that make up the foundation for the framework.
If you find this code useful, buy me a coffee :)

ExtendedCustomMonoBehaviour



    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;


        /// 
        /// This class is used to add some common variables to
        /// MonoBehavior, rather than constantly repeating
        /// the same
        ///
    public class ExtendedCustomMonoBehaviour : MonoBehaviour
    {
        public Transform myTransform;
        public GameObject myGO;
        public Rigidbody myBody;

        public bool didnit;
        public bool canControl;

        public int id;
        [System.NonSerialized]
        public Vector3 tempVec;


        public virtual void SetID(int anID)
        {
            id = anID;

        }
    }


    using UnityEngine;

    namespace AIStates
    {
        public enum AIState
        {
            moving_looking_for_target,
            chasing_target,
            backing_up_looking_for_target,
            stopped_turning_left,
            stopped_turning_right,
            paused_looking_for_target,
            translate_along_waypoint_path,
            paused_no_target,
            steer_to_waypoint,
            steer_to_target,
        }

    }

    using UnityEngine;

    namespace AIAttackStates
    {
        public enum AIAttackState
        {
            random_fire,
            look_and_destroy,
            no_attack,
        }

    }




BaseCameraController


    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using UnityEngine;

    namespace GameEngine.FrameWork.Base
    {
        public class BaseCameraController  : MonoBehaviour
        {

            public virtual void SetTarget(Transform aTarget)
            {

            }
        }
    }

  

BasePlayerManager



    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;


    public class BasePlayerManager : MonoBehaviour
    {
        public bool didInit;

        //the user manager and AI controllers are publically accessible so that
        // our individual control scripts can them easily
        public BaseUserManager DataManager;

        //Note that we initiate on wake in the this class so that 
        //it is ready for other classes to access our details when
        // they initialize on start
        public virtual void Awake()
        {
            didInit = false;

            //rather than clutter up the start() func, we call init to init()
            //to do any startup specifics
            Init();
                
        }

        public virtual void Init()
        {
            //cashe ref to our user manager
            DataManager = gameObject.GetComponent();

            if (DataManager == null)
            {
                DataManager = gameObject.AddComponent();

                didInit = true;
            }

        }

            public virtual void GameFinished()
            {
            DataManager.SetIsFinished(true);

            }

            public virtual void GameStart()
            {
            DataManager.SetIsFinished(false);

            }

    }



BaseUserManager



    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;


    /// 
    /// gameplay specific data
    /// we keep these private and provide methods to modify them
    /// instead, just to prevent any accidental corruption
    /// or invalid data coming out
    ///
    public class BaseUserManager : MonoBehaviour
    {
        private int score;
        private int highScore;
        private int level;
        private int health;
        private bool isFinished;

        //this is the display name of the player
        public string playerName = "Joseph";

        public virtual void GetDefaultData()
        {
            playerName = "Joseph";
            score = 0;
            level = 1;
            health = 3;
            highScore = 0;
            isFinished = false;

    }

        public string GetName()
        {
            return playerName;

        }

        public void SetName(string aName)
        {
            playerName = aName;

        }

        public int GetLevel()
        {
            return level;

        }

        public void SetLevel(int num)
        {
            level = num;

        }
        public int GetHighScore()
        {
            return highScore;

        }

        public int GetScore()
        {
        return score;

        }

        public virtual void AddScore(int anAmount)
        {
            score += anAmount;

        }

        public void LostScore(int num)
        {
            score -= num;

        }

        public void SetScore(int num)
        {
            score = num;

        }

        public int GetHealth()
        {
            return health;

        }

        public void AddHealth(int num)
        {
            health += num;

        }

        public void ReduceHealth(int num)
        {
        health -= num;

        }

        public void SetHealth(int num)
        {
            health = num;

        }

        public bool GetIsFinished(bool aVal)
        {
            return isFinished;

        }

        public void SetIsFinished(bool aVal)
        {
            isFinished = aVal;

        }


    }




BaseGameController



    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class BaseGameController : MonoBehaviour
    {

        bool paused;
        public GameObject explosionPrefab;

        public bool Paused
        {
        get
        {
        //get paused
        return paused;
        }
        set
        {
        //set paused
        paused = value;

        if (paused)
        {
        //pause time
        Time.timeScale = 0f;
        }
        else
        {
        //unpause time
        Time.timeScale = 1f;

        }
    }
    }


        /// 
        /// deal with restart button (default behavior re-loads the currently loaded scene)
        ///
        [System.Obsolete]
        public virtual void RestartGameButtonPressed()
        {
            Application.LoadLevel(Application.loadedLevelName);

        }

        /// 
        /// deal with the end of a boss battle
        ///
        public virtual void BossDestroyed()
        {

        }

        /// 
        /// deal with enemy destroyed
        ///
        /// 
        /// 
        /// 
        public virtual void EnemyDestroyed(Vector3 aPosition, int pointsValue,int hitByID)
        {

        }

        /// 
        /// instantiate an explosion at the position passed into this function
        ///
        /// 
        public void Explode(Vector3 aPosition)
        {
        Instantiate(explosionPrefab, aPosition, Quaternion.identity);
        }

        /// 
        /// do start game functions
        ///
        public virtual void StartGame()
        {

        }

        /// 
        /// Player is respawning
        ///
        public virtual void Respawn()
        {

        }

        /// 
        /// Player needs to be spawned
        ///
        public virtual void SpawnPlayer()
        {

        }

        /// 
        /// deal with player life lost (update U.I. etc)
        ///
        public virtual void PlayerLostLife()
        {

        }
    }





BaseTopDownSpaceShip



    using GameEngine.FrameWork.Common.Inputs;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using UnityEngine;

    namespace GameEngine.FrameWork.Base
    {
        public class BaseTopDownSpaceShip      : ExtendedCustomMonoBehaviour
        {

        private Quaternion targetRotation;

        private float thePos;
        private float moveXAmount;
        private float moveZAmount;

        public float moveXSpeed = 40f;
        public float moveZSpeed = 15f;

        public float limitX = 15f;
        public float limitZ = 15f;

        private float originZ;

        [System.NonSerialized]
        public Keyboard_Input default_input;

        public float horizontal_input;
        public float vertical_input;

        public virtual void Start()
        {
        //we are overriding Start() so as not to call Init, as we want the game controller to do this in this game
        didnit = false;
        this.Init();

        }

        public virtual void Init()
        {
        //cashe refs to our transform and gameObject
        myTransform = transform;
        myGO = gameObject;
        myBody = GetComponent();

        //add default keyboard input
            default_input = myGO.AddComponent();

        //grab the starting z position to use as a baseline for z position limiting
            originZ = myTransform.localPosition.z;

        //set a flag so that our update function knows when we are ok to use
            didnit = true;


        }

        public virtual void GameStart()
        {
            //we are good to go, so let's get moving!
            canControl = true;

        }

        public virtual void GetInput()
        {
            //this is just a 'default' function that (if needs be) should be overridden in the glue code
            horizontal_input = default_input.GetHorizontal();
            vertical_input = default_input.GetVertical();

        }

        public virtual void Update()
        {
            UpdateShip();

        }

        public virtual void UpdateShip()
        {
        //don't do anything until init() has been run
            if (!didnit)
            {
                return;

            }

            //check to see if we're supposed to be controlling the player before moving it
            if (!canControl)
            {
            return;

            }
            GetInput();

            //calculate movement amounts for X and Z axis
            moveXAmount = horizontal_input * Time.deltaTime * moveXSpeed;
            moveZAmount = vertical_input * Time.deltaTime * moveZSpeed;

            Vector3 tempRotation = myTransform.eulerAngles;
            tempRotation.z = horizontal_input * -30f;
            myTransform.eulerAngles = tempRotation;

            //move our transform to its updated position
            myTransform.localPosition += new Vector3(moveXAmount, 0, moveZAmount);

            //check the position to make sure that it is within boundaries
            if(myTransform.localPosition.x <=-limitX || myTransform.localPosition.x >= limitX)
            {
            thePos = Mathf.Clamp(myTransform.localPosition.x, -limitX, limitX);
            myTransform.localPosition = new Vector3(thePos, myTransform.localPosition.y, myTransform.localPosition.z);

            }
            //we also check the z position to make sure that it is within bounderies
            if (myTransform.localPosition.z <= originZ || myTransform.localPosition.z >= limitZ)
            {
            thePos = Mathf.Clamp(myTransform.localPosition.z, originZ, limitZ);
            myTransform.localPosition = new Vector3(myTransform.localPosition.x, myTransform.localPosition.y, thePos);

            }
        }

    }
}






BaseInputController



    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class BaseInputController : MonoBehaviour
    {
        //directional buttons
        public bool Up;
        public bool Down;
        public bool Left;
        public bool Right;

        //Fire / action buttons
        public bool Fire1;

        //weapon slots
        public bool Slot1;
        public bool Slot2;
        public bool Slot3;
        public bool Slot4;
        public bool Slot5;
        public bool Slot6;
        public bool Slot7;
        public bool Slot8;
        public bool Slot9;

        public float vert;
        public float horz;
        public bool shouldRespawn;

        public Vector3 TEMPVec3;
        private Vector3 zeroVector = new Vector3(0, 0, 0);

        public virtual void CheckInput()
        {
        //override with your own code to deal with input
            horz = Input.GetAxis("Horizontal");
            vert = Input.GetAxis("Vertical");

        }

        public virtual float GetHorizontal()
        {
        //return our cashed horizontal input axis value
            return horz;
        }

        public virtual float GetVertical()
        {
        //returns our cashed vertical imput axis value
            return vert;

        }

        public virtual bool GetFire()
        {
            return Fire1;

        }

        public bool GetRespawn()
        {
            return shouldRespawn;

        }

        public virtual Vector3 GetMovementDirectionVector()
        {
        //temp vector for movement dir gets set to the value of an otherwise
        //unused vector that always has the value of 0,0,0
            TEMPVec3 = zeroVector;
        //if we'er going left or right, set the velocity vectors X
        //to our horizontal input value
            if (Left || Right)
            {
            TEMPVec3.x = horz;

            }
        //if we're going up or down, set the velocity vectors x to
        //our vertical input value
            if (Up || Down)
            {
                TEMPVec3.y = vert;

            }
        //return the movement vector
            return TEMPVec3;

        }
    }






BaseTopDown



    using GameEngine.FrameWork.Common.Inputs;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using UnityEngine;

    namespace GameEngine.FrameWork.Base
    {
    public class BaseTopDown : ExtendedCustomMonoBehaviour
    {
        public AnimationClip idleAnimation;
        public AnimationClip walkAnimation;

        public float walkMaxAnimationSpeed = 0.75f;
        public float runMaxAnimationSpeed = 1.0f;

        //when did the user start walking (used for going into run after a while)
        private float walkTimeStart = 0.0f;

        //we've made the following variable public so that we can use an animation on a different gameobject if needed
        public Animation _animation;

        enum CharacterState
        {
        Idle = 0,
        Walking = 1,
        Running = 2,
        }

        private CharacterState _characterState;

        //the speed when walking
        public float walkSpeed = 2.0f;

        //after runAfterSeconds of walking we run with runspeed
        public float runSpeed = 4.0f;

        public float speedSmoothing = 10.0f;
        public float rotateSpeed = 500.0f;
        public float runAfterSeconds = 3.0f;

        //the current move direction in x-z
        private Vector3 moveDirection = Vector3.zero;

        //the current vertical speed
        private float verticalSpeed = 0.0f;

        //the current x-z move speed
        public float moveSpeed = 0.0f;

        //the last collision flag returned from controller.move
        private CollisionFlags collisionFlags;

        public BasePlayerManager myPlayerController;

        [System.NonSerialized]
        public Keyboard_Input default_input;

        public float horz;
        public float vert;

        private CharacterController controller;

        void Awake()
        {
        //we need to do this before anything happens to the script
        //Or object, so it happens in Awake.
        //if you need to add specific set-up, consider adding it to
        //the Init() function instead to keep this function limited only
        //to things we need to do before anything  else happens

            moveDirection = transform.InverseTransformDirection(Vector3.forward);

        //if _animation has not been set up in the inspector, we'll
        //try to find it on the current gameobject
            if (_animation == null)
            {
                _animation = GetComponent();

            }

            if (!_animation)
            {
                Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");

            }

            if (!idleAnimation)
            {
                _animation = null;
                Debug.Log("No idle animation found. Turning off animations.");

            }

            if (!walkAnimation)
            {
                _animation = null;
                Debug.Log("No walk animation found. Turning off animations.");

            }

                controller = GetComponent();

            }

            public virtual void Start()
            {
                Init();

            }

            public virtual void Init()
            {
            //cashe the usual supects
                myBody = GetComponent();
                myGO = gameObject;
                myTransform = transform;

            //add default keyboard input
                default_input = myGO.AddComponent();

            //cashe a reference to the player controller
                myPlayerController = myGO.GetComponent();

                if (myPlayerController != null)
                {
                    myPlayerController.Init();

                }
                }

                public void SetUserInput(bool setInput)
                {
                    canControl = setInput;

                }

                public virtual void GetInput()
                {
                    horz = Mathf.Clamp(default_input.GetHorizontal(), -1, 1);
                    vert = Mathf.Clamp(default_input.GetVertical(), -1, 1);

                }

                public virtual void LateUpdate()
                {
            //we check for input in the lateUpdate because Unity recomends this
                if (canControl)
                {
                    GetInput();

                }

                }

                public bool moveDirectionally;

                private Vector3 targetDirection;
                private float curSmooth;
                private float targetSpeed;
                private float curSpeed;
                private Vector3 forward;
                private Vector3 right;

                void UpdateSmoothedMovementDirection()
                {
                if (moveDirectionally)
                {
                    UpdateDirectionalMovement();

                }
                else
                {
                    UpdateRotationalMovement();

                }
                }

                void UpdateDirectionalMovement()
                        {
            //find target direction
                   targetDirection = horz * Vector3.right;
                   targetDirection += vert * Vector3.forward;

            //we store speed and direction seperately, so that when the character stands still we still have a valid forward direction
            //movement direction is always normalized, and we only update it if there is user input
                if(targetDirection != Vector3.zero)
                {
                    moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
                    moveDirection = moveDirection.normalized;

                }

            //smooth the speed based on the current target direction
                   curSmooth = speedSmoothing * Time.deltaTime;

            //choose target speed
            //*we want to suport analog input but make sure you can't
            //walk faster diagonally than just forward or sideways
                   targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);

                   _characterState = CharacterState.Idle;

            //decide on animation state and adjust move speed
                if (Time.time - runAfterSeconds > walkTimeStart)
                {
                    targetSpeed *= runSpeed;
                    _characterState = CharacterState.Running;

                }
                else
                {
                    targetSpeed *= walkSpeed;
                    _characterState = CharacterState.Walking;

                }
                    moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);

                    //reset walk time start when we slow down
                if(moveSpeed < walkSpeed * 0.3f)
                {
                    walkTimeStart = Time.time;

                }

            //calculate actual motion
                    Vector3 movement = moveDirection * moveSpeed;
                    movement *= Time.deltaTime;


            //Move the controller
                    collisionFlags = controller.Move(movement);

            //set rotation to the move direction
                    myTransform.rotation = Quaternion.LookRotation(moveDirection);

                }


            void UpdateRotationalMovement()
            {
           //this character movement is based on the code in the unity help files
           //for characterController.simpleMove
           //http://docs.unity3d.com/documentation/scriptReference/charactercontroller.simpleMove.html
                    myTransform.Rotate(0, horz * rotateSpeed * Time.deltaTime, 0);
                    curSpeed = moveSpeed * vert;
                    controller.SimpleMove(myTransform.forward * curSpeed);

           //target direction (the max we want to move, used for calculating target speed
                    targetDirection = vert * myTransform.forward;

           //smooth the speed based on the current target direction
                    float curSmooth = speedSmoothing * Time.deltaTime;

           //choose target speed
           //*we want to suport analog input but make sure you can't
           //walk faster diagonally than just forward or sideways
                   targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);

                   _characterState = CharacterState.Idle;

           //decide on animation state and adjust move speed
                if(Time.time - runAfterSeconds > walkTimeStart)
                {
                   targetSpeed *= runSpeed;
                   _characterState = CharacterState.Running;

                }
                else
                {
                    targetSpeed *= walkSpeed;
                    _characterState = CharacterState.Walking;

                }

                    moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);

            //reset walk time start when we slow down
                if(moveSpeed < walkSpeed * 0.3f)
                {
                    walkTimeStart = Time.time;

                }

                }

                void Update()
                {
                if (!canControl)
                {
            //kill all inputs if not controllable
                    Input.ResetInputAxes();

                }

                UpdateSmoothedMovementDirection();

            //animation sector
                if (_animation)
                {
                if(controller.velocity.sqrMagnitude < 0.1f)
                {
                    _animation.CrossFade(idleAnimation.name);

                }
                else
                {
                if(_characterState == CharacterState.Running)
                {
                    _animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, runMaxAnimationSpeed);
                    _animation.CrossFade(walkAnimation.name);

                }
                else if(_characterState == CharacterState.Walking)
                {
                    _animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, walkMaxAnimationSpeed);
                    _animation.CrossFade(walkAnimation.name);

                }
                }
            }
        }

                public float GetSpeed()
                {
                    return moveSpeed;

                }

                public Vector3 GetDirection()
                {
                    return moveDirection;

                }

                public bool IsMoving()
                {
                    return Mathf.Abs(vert) + Mathf.Abs(horz) > 0.5f;

                }

                public void Reset()
                {
                    gameObject.tag = "Player";

                }
    }
}


SceneManager



    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class SceneManager : MonoBehaviour
    {
        public string[] levelNames;
        public int gameLevelNum;

        /// 
        /// keep this object alive
        ///
        public void Start()
        {
            DontDestroyOnLoad(this.gameObject);
        }

        [System.Obsolete]
        public void LoadLevel(string sceneName)
        {
            Application.LoadLevel(sceneName);

        }

        [Obsolete]
        public void GoNextLevel()
        {
        // if our index goes over the total number of levels in the array, we reset it
        if (gameLevelNum >= levelNames.Length)
        {
            gameLevelNum = 0;

        }

        //load the level (the array index starts at 0, but we start counting game levels at 1 for clarity's sake)

        LoadLevel(gameLevelNum);

        //increase our game level index counter
        gameLevelNum++;

        }

        /// 
        /// Load the game level
        ///
        /// 
        [Obsolete]
        private void LoadLevel(int indexNum)
        {
        LoadLevel(levelNames[indexNum]);

        }

        /// 
        /// reset the level index counter
        ///
        public void ResetGame()
        {
        gameLevelNum = 0;

        }
    }

BaseSoundController



    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using UnityEngine;



    namespace GameEngine.FrameWork.Base
    {

    [AddComponentMenu("Base/Sound Controller")]

    public class SoundObject
    {
        public AudioSource source;
        public GameObject sourceGO;
        public Transform sourceTR;

        public AudioClip clip;
        public string name;


    public SoundObject(AudioClip aClip, string aName, float aVolume)
    {
        // in this (the constructor) we create a new audio source and store the details of the sound itself
        sourceGO = new GameObject("AudioSource_" + aName);
        sourceTR = sourceGO.transform;
        source = sourceGO.AddComponent();
        source.name = "AudioSource_" + aName;
        source.playOnAwake = false;
        source.clip = aClip;
        source.volume = aVolume;
        clip = aClip;
        name = aName;
    }

    public void PlaySound(Vector3 atPosition)
    {
        sourceTR.position = atPosition;
        source.PlayOneShot(clip);
    }
}


    public class BaseSoundController : MonoBehaviour
    {
        public static BaseSoundController Instance;

        public AudioClip[] GameSounds;

        private int totalSounds;
        private ArrayList soundObjectList;
        private SoundObject tempSoundObj;

        public float volume = 1;
        public string gamePrefsName = "DefaultGame"; // DO NOT FORGET TO SET THIS IN THE EDITOR!!

    public void Awake()
    {
        Instance = this;
    }

    void Start()
    {
        // we will grab the volume from PlayerPrefs when this script first starts
        volume = PlayerPrefs.GetFloat(gamePrefsName + "_SFXVol");
        Debug.Log("BaseSoundController gets volume from prefs " + gamePrefsName + "_SFXVol at " + volume);
        soundObjectList = new ArrayList();

        // make sound objects for all of the sounds in GameSounds array
        foreach (AudioClip theSound in GameSounds)
        {
            tempSoundObj = new SoundObject(theSound, theSound.name, volume);
            soundObjectList.Add(tempSoundObj);
            totalSounds++;
        }
    }

    public void PlaySoundByIndex(int anIndexNumber, Vector3 aPosition)
    {
        // make sure we're not trying to play a sound indexed higher than exists in the array
        if (anIndexNumber > soundObjectList.Count)
        {
            Debug.LogWarning("BaseSoundController>Trying to do PlaySoundByIndex with invalid index number. Playing last sound in array, instead.");
            anIndexNumber = soundObjectList.Count - 1;
        }

            tempSoundObj = (SoundObject)soundObjectList[anIndexNumber];
            tempSoundObj.PlaySound(aPosition);
        }
    }

 }

        using UnityEngine;
        using System.Collections;


        namespace GameEngine.FrameWork.Base
        {
    public class MusicController : MonoBehaviour
    {

        private float volume;
        public string gamePrefsName = "DefaultGame"; // DO NOT FORGET TO SET THIS IN THE EDITOR!!

        public AudioClip music;

        public bool loopMusic;

        private AudioSource source;
        private GameObject sourceGO;

        private int fadeState;
        private int targetFadeState;

        private float volumeON;
        private float targetVolume;

        public float fadeTime = 15f;
        public bool shouldFadeInAtStart = true;

    void Start()
    {
        // we will grab the volume from PlayerPrefs when this script first starts
        volumeON = PlayerPrefs.GetFloat(gamePrefsName + "_MusicVol");

        // create a game object and add an AudioSource to it, to play music on
        sourceGO = new GameObject("Music_AudioSource");
        source = sourceGO.AddComponent
            ();
            source.name = "MusicAudioSource";
            source.playOnAwake = true;
            source.clip = music;
            source.volume = volume;

            // the script will automatically fade in if this is set
            if (shouldFadeInAtStart)
            {
                fadeState = 0;
                volume = 0;
            }
            else
            {
                fadeState = 1;
                volume = volumeON;
            }

            // set up default values
            targetFadeState = 1;
            targetVolume = volumeON;
            source.volume = volume;
            }

        void Update()
        {
            // if the audiosource is not playing and it's supposed to loop, play it again (Sam?)
            if (!source.isPlaying && loopMusic)
                source.Play();

            // deal with volume fade in/out
            if (fadeState != targetFadeState)
            {
                if (targetFadeState == 1)
            {
                if (volume == volumeON)
                    fadeState = 1;
            }
                else
            {
                    if (volume == 0)
                    fadeState = 0;
            }

                volume = Mathf.Lerp(volume, targetVolume, Time.deltaTime * fadeTime);
                source.volume = volume;
            }
        }

        public void FadeIn(float fadeAmount)
        {
            volume = 0;
            fadeState = 0;
            targetFadeState = 1;
            targetVolume = volumeON;
            fadeTime = fadeAmount;
        }

        public void FadeOut(float fadeAmount)
        {
            volume = volumeON;
            fadeState = 1;
            targetFadeState = 0;
            targetVolume = 0;
            fadeTime = fadeAmount;
        }
    }
}






BaseVehicle



    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;


        ///
        /// This class is used to add some common variables to
        /// MonoBehavior, rather than constantly repeating
        /// the same
        ///
    public class ExtendedCustomMonoBehaviour : MonoBehaviour
    {
        public Transform myTransform;
        public GameObject myGO;
        public Rigidbody myBody;

        public bool didnit;
        public bool canControl;

        public int id;
        [System.NonSerialized]
        public Vector3 tempVec;


        public virtual void SetID(int anID)
        {
            id = anID;
        }
    }


    using GameEngine.FrameWork.Common.Inputs;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using UnityEngine;

    namespace GameEngine.FrameWork.Base
    {
    class BaseVehicle : ExtendedCustomMonoBehaviour
    {
        public WheelCollider frontWheelLeft;
        public WheelCollider frontWheelRight;
        public WheelCollider rearWheelLeft;
        public WheelCollider rearWheelRight;

        public float steerMax = 30f;
        public float accelMax = 5000f;
        public float brakeMax = 5000f;

        public float steer = 0f;

        public float motor = 0f;

        public float brake = 0f;

        public float mySpeed;

        public bool isLocked;

        [System.NonSerialized]
        public Vector3 flatVelo;

        public BasePlayerManager myPlayerController;

        [System.NonSerialized]
        public Keyboard_Input default_input;

        public AudioSource engineSoundSource;

        public virtual void Start()
        {
            Init();
        }

        public virtual void Init()
        {
        //cashe the usual suspects
            myBody = GetComponent();
            myGO = gameObject;
            myTransform = transform;

        //add default keyboard input
            default_input = myGO.AddComponent();

        //cashe a reference to the player controller
            myPlayerController = myGO.GetComponent();

            // call base class init()
            myPlayerController.Init();

           //with this simple vehicle code, we set the center 
           //of mass low to try to keep the car from toppling over
            myBody.centerOfMass = new Vector3(0, -4f, 0);

            //see if we can find an engine sound source, if we need to
                if(engineSoundSource == null)
                {
                    engineSoundSource = myGO.GetComponent();

        }

    }

    public virtual void SetUserInput(bool setInput)
    {
        canControl = setInput;

    }

    public void SetLock(bool lockState)
    {
        isLocked = lockState;

    }

    public virtual void LateUpdate()
    {
        //we check for input in LateUpdate() because Unity recomends this
        if (canControl)
        {
            GetInput();

            //update the audio
            UpdateEngineAudio();

        }
    }

    public virtual void FixedUpdate()
    {
        UpdatePhysics();

    }

    public virtual void UpdateEngineAudio()
    {
        //this is just a 'made up' multiplier value applied to myspeed
        engineSoundSource.pitch = 0.5f + (Mathf.Abs(mySpeed) * 0.005f);

    }

    public virtual void UpdatePhysics()
    {
        CheckLock();

        //grab the velocity of the rigidbody and convert it into flat velocity (remove the Y)
        var velo = myBody.angularVelocity;

        //convert the velocity to local space so we can see how fast we are moving forward (along the local z axis)
        velo = transform.InverseTransformDirection(myBody.velocity);
        flatVelo.x = velo.x;
        flatVelo.y = 0;
        flatVelo.z = velo.z;

        //work out our current forwad speed
        mySpeed = velo.z;

        //if we're moving slow,  we reverse motorTorque and remove brake Torque so that the car will reverse
        if (mySpeed < 2)
        {
            //that is we're pressing down the break key
            //(making brake> 0)
            if(brake > 0)
            {
                rearWheelLeft.motorTorque = -brakeMax * brake;
                rearWheelRight.motorTorque = -brakeMax * brake;
                rearWheelLeft.brakeTorque = 0;
                rearWheelRight.brakeTorque = 0;

                frontWheelLeft.steerAngle = steerMax * steer;
                frontWheelRight.steerAngle = steerMax * steer;

                //drop out of this function before applying the regular non-reversed values to the wheels
                return;

        }
    }

        //apply regular movement values to the wheels
        rearWheelLeft.motorTorque = accelMax * motor;
        rearWheelRight.motorTorque = accelMax * motor;

        rearWheelLeft.brakeTorque = brakeMax * brake;
        rearWheelRight.brakeTorque = brakeMax * brake;

        frontWheelLeft.steerAngle = steerMax * steer;
        frontWheelRight.steerAngle = steerMax * steer;

    }
    public void CheckLock()
    {
        if (isLocked)
        {
            //control is locked out and we should be stopped
            steer = 0;
            brake = 0;
            motor = 0;

            //hold our rigidbody in place (but allow the Y to move so the car may drop to the ground if it is not exactly matched to the terrain
            Vector3 tempVec = myBody.velocity;
            tempVec.x = 0;
            tempVec.y = 0;
            myBody.velocity = tempVec;

        }
    }

    public virtual void GetInput()
    {
        //calculate steering amount
        steer = Mathf.Clamp(default_input.GetHorizontal(), -1, 1);
        //how much accelerator?
        motor = Mathf.Clamp(default_input.GetVertical(), 0, 1);
        //how much brake?
        brake = -1 * Mathf.Clamp(default_input.GetVertical(), -1, 0);

    }

    }
    }




    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using UnityEngine;
    using System.Collections;


    namespace GameEngine.FrameWork.Base
    {
    public class BaseWheelAlignment : MonoBehaviour
    {
        //Define the variables used in the script, the corresponding collider is the wheel collider at the position of the visible wheel,
        //the slip prefab is a prefab instantiated when the wheels slide, the rotation value is the value used to rotate the wheel around its axle.
        public WheelCollider correspondingCollider;
        public GameObject slipPrefab;
        public float slipAmountForTireSmoke = 50f;

        private float rotationValue = 0.0f;
        private Transform myTransform;
        private Quaternion zeroRotation;
        private Transform colliderTransform;
        private float suspensionDistance;

    void Start()
    {
        //cashe some commoly used things...
        myTransform = transform;
        zeroRotation = Quaternion.identity;
        colliderTransform = correspondingCollider.transform;

    }

    void Update()
    {
        //define a hit point for the raycast collision
        RaycastHit hit;
        //Find the colliders center point, you need to do this because the center of the collider might not actually be the real position if the transforms off
        Vector3 ColliderCenterPoint = colliderTransform.TransformPoint(correspondingCollider.center);

        //Now cast a ray out from the wheel collider's center the distance of the suspension, if it hit something, then use the "hit" variable's data to find where the wheel
        //hit, if it didn't , then set the wheel to be fully extended along the suspension.
        if (Physics.Raycast(ColliderCenterPoint,-colliderTransform.up, out hit,correspondingCollider.suspensionDistance + correspondingCollider.radius))
        {
            myTransform.position = hit.point + (colliderTransform.up * correspondingCollider.radius);
        }
        else
        {
            myTransform.position = ColliderCenterPoint - (colliderTransform.up * correspondingCollider.suspensionDistance);
        }

        //now set the wheel rotation to the rotation of the collider combined with a new rotation value.
        //this new value is the rotation around the axle, and the rotation from steering input
        myTransform.rotation = colliderTransform.rotation * Quaternion.Euler(rotationValue, correspondingCollider.steerAngle, 0);
        //increase the rotation value by the rotation speed (in degrees per second)
        rotationValue += correspondingCollider.rpm * (360 / 60) * Time.deltaTime;
        //define a wheelhit object, this stores all of the data from the wheel collider and will allow us to determine the slip of the tire
        WheelHit correspondingGroundHit = new WheelHit();
        correspondingCollider.GetGroundHit(out correspondingGroundHit);

        //if the slip of the tire is greater than 2.0f, and the slip prefab exists, create an instance of it on the ground at a zero rotation
        if (Mathf.Abs(correspondingGroundHit.sidewaysSlip) > slipAmountForTireSmoke)
        {
        if (slipPrefab)
        {
            SpawnController.Instance.Spawn(slipPrefab, correspondingGroundHit.point, zeroRotation);

        }
    }

    }
    }
    }





BaseWeaponController



    using System;
    using System.Collections;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using UnityEngine;

    namespace GameEngine.FrameWork.Base
    {
    public  class BaseWeaponController : MonoBehaviour
    {
        public GameObject[] weapons;

        public int selectedWeaponSlot;
        public int lastSelectedWeaponSlot;

        public Vector3 offsetWeaponSpawnPosition;

        public Transform forceParent;

        private ArrayList weaponSlots;
        private ArrayList weaponScripts;
        private BaseWeaponScript TEMPWeapon;
        private Vector3 TEMPvector3;
        private Quaternion TEMProtation;
        private GameObject TEMPgameObject;

        private Transform myTransform;
        private int ownerNum;

        public bool useForceVectorDirection;
        public Vector3 forceVector;
        private Vector3 theDir;

    public void Start()
    {
        //default to the first weapon slot
        selectedWeaponSlot = 0;
        lastSelectedWeaponSlot = -1;

        //initialize weapon list array
        weaponSlots = new ArrayList();

        //initialize weapon srcipts
        weaponScripts = new ArrayList();

        //cashe a reference to the transform (looking up a transform each step can be expensive, so this is important
        myTransform = transform;

        if (forceParent == null)
        {
            forceParent = myTransform;

        }

        //rather than look up the transform position and rotation of the player each iteration of the loop below,
        //we cashe them first into temporary variables
        TEMPvector3 = forceParent.position;
        TEMProtation = forceParent.rotation;

        //we instantiate all of the weapons and hide them so that we can activate and use them when needed
        for (int i = 0; i< weapons.Length; i++)
        {
        //instantiate the item from the weapons list
        TEMPgameObject = (GameObject)Instantiate(weapons[i], TEMPvector3 + offsetWeaponSpawnPosition, TEMProtation);

        //make this gameobject that our weapon controller script is attached to, to be the parent of the weapon so that the weapon will move around with the player
        //Note: if you need projectiles to be on a different layer from the main gameobject, set the layer of the forceparent object to the layer you want projectiles to be on
        TEMPgameObject.transform.parent = forceParent;
        TEMPgameObject.layer = forceParent.gameObject.layer;
        TEMPgameObject.transform.position = forceParent.position;
        TEMPgameObject.transform.rotation = forceParent.rotation;

        //store a reference to the gameobject in an arrayList
        weaponSlots.Add(TEMPgameObject);

        //grab a reference to the weapons script attached to the weapon and store the reference in an arrayList
        TEMPWeapon = TEMPgameObject.GetComponent();
        weaponScripts.Add(TEMPWeapon);

        //disable the weapon
        TEMPgameObject.SetActive(false);

        }

        //now we set the default selected weapon to visible
        SetWeaponSlot(0);

        }

        public void SetOwner(int aNum)
        {
        //used to identify the object firing, if required
        ownerNum = aNum;

        }

        public void SetWeaponSlot(int slotNum)
        {
        //if the selected weapon is already this one, drop out!
        if (slotNum == lastSelectedWeaponSlot)
        {
            return;

        }

        //disable the current weapon
        DisableCurrentWeapon();

        //set our current weapon to the one passed in
        selectedWeaponSlot = slotNum;

        //make sure sensible values are getting passed in
        if (selectedWeaponSlot < 0)
        {
            selectedWeaponSlot = weaponSlots.Count - 1;

        }

        //make sure the weapon slot isn't higher than the total number of weapons in our list
        if(selectedWeaponSlot>weaponSlots.Count - 1)
        {
            selectedWeaponSlot = weaponSlots.Count - 1;

        }

        //we store this selected slot to use to prevent duplicate weapon slot setting
        lastSelectedWeaponSlot = selectedWeaponSlot;
        //enable the newly selected weapon
        EnableCurrentWeapon();

        }

        public virtual void NextWeaponSlot(bool shouldLoop)
        {
        //disable the current weapon
        DisableCurrentWeapon();

        //next slot
        selectedWeaponSlot++;

        //make sure that the slot isn't higher than the total number of weapons in the list
        if (selectedWeaponSlot == weaponScripts.Count)
        {
            if (shouldLoop)
        {
            selectedWeaponSlot = 0;

        }
        else
        {
            selectedWeaponSlot = weaponScripts.Count - 1;

        }
        }

        //we store this selected slot to use to prevent duplication weapon slot settings
        lastSelectedWeaponSlot = selectedWeaponSlot;

        //enable the newly selected weapon
        EnableCurrentWeapon();

        }

        public virtual void PrevWeaponSlot(bool shouldLoop)
        {
        //disable the current weapon
        DisableCurrentWeapon();

        //prev slot
        selectedWeaponSlot--;

        //make sure that the slot is a sensible number
        if (selectedWeaponSlot < 0)
        {
            if (shouldLoop)
        {
                selectedWeaponSlot = weaponScripts.Count - 1;

        }
            else
        {
                selectedWeaponSlot = 0;

        }
        }

        //we store this selected slot to use to prevent duplicate weapon slot setting
        lastSelectedWeaponSlot = selectedWeaponSlot;

        //enable the newly selected weapon
        EnableCurrentWeapon();

        }

        public virtual void DisableCurrentWeapon()
        {
        if (weaponScripts.Count == 0)
        {
            return;

        }

        //grab refernce to currently selected weapon script
        TEMPWeapon = (BaseWeaponScript)weaponScripts[selectedWeaponSlot];

        //now tell the script to disable itself
        TEMPWeapon.Disable();

        //grab refernce to the weapon's gameobject and disable that, too
        TEMPgameObject = (GameObject)weaponSlots[selectedWeaponSlot];
        TEMPgameObject.SetActive(false);


        }

        public virtual void EnableCurrentWeapon()
        {
        if (weaponScripts.Count == 0)
        {
            return;

        }

        //grab reference to currently selected weapon
        TEMPWeapon = (BaseWeaponScript)weaponScripts[selectedWeaponSlot];

        //now tell the script to enable itself
        TEMPWeapon.Enable();

        TEMPgameObject = (GameObject)weaponSlots[selectedWeaponSlot];
        TEMPgameObject.SetActive(true);

        }

        public virtual void Fire()
        {
            if (weaponScripts == null)
        {
                return;

        }

            if (weaponScripts.Count == 0)
        {
                return;

        }

        //find the weapon in the currently selected slot
        TEMPWeapon = (BaseWeaponScript)weaponScripts[selectedWeaponSlot];

        theDir = myTransform.forward;

        if (useForceVectorDirection)
        {
            theDir = forceVector;

        }

        //fire the projectile
        TEMPWeapon.Fire(theDir, ownerNum);


        }

        }
        }



        using System;
        using System.Collections;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks;
        using UnityEngine;

        namespace GameEngine.FrameWork.Base
        {
    public class BaseWeaponScript : MonoBehaviour
    {
        [System.NonSerialized]
        public bool canFire;

        public int ammo = 100;
        public int maxAmmo = 100;

        public bool isInfiniteAmmo;
        public GameObject projectileGo;
        public Collider parentCollider;

        private Vector3 fireVector;

        [System.NonSerialized]
        public Transform myTransform;

        private int myLayer;

        public Vector3 spawnPosOffset;
        public float forwardOffset = 1.5f;
        public float reloadTime = 0.2f;
        public float projectileSpeed = 10f;
        public bool inheritVelocity;

        [System.NonSerialized]
        public Transform theProjectile;

        private GameObject theProjectileGo;
        private bool isLoaded;
        private ProjectileController theProjectileController;


    public virtual void Start()
    {
        Init();
    }

    public virtual void Init()
    {
        //cashe the transform
        myTransform = transform;

        //cashe the layer (we'll set all projectiles to avoid this layer in collision so that things don't shoot themselves
        myLayer = gameObject.layer;

        //load the weapon
        Reloaded();

    }

    public virtual void Enable()
    {
        //drop out if firing is disabled
        if (canFire == true)
        {
            return;

        }

        //enable weapon (do things like show the weapon mesh etc.)
        canFire = true;

        }

        public virtual void Disable()
        {
            if (canFire == false)
        {
                return;

        }

        //hide weapon (do things like hide the weapon mesh etc.)
        canFire = false;

        }

    public virtual void Reloaded()
    {
        //the isloaded var tells us if this weapon is loaded and ready to fire
        isLoaded = true;

    }

    public virtual void SetCollider(Collider aCollider)
    {
        parentCollider = aCollider;

    }

    public virtual void Fire(Vector3 aDirection,int ownerId)
    {
        //be sure to check canfire so that the weapon can be enabled or disabled as required
        if (!canFire)
        {
            return;

        }

        //if the weapon is not loaded, drop out
            if (!isLoaded)
        {
                return;

        }

        //if we're out of ammo and we do not have infinite ammo, drop out
            if(ammo<=0 && !isInfiniteAmmo)
        {
                return;

        }

        //decrease ammo
        ammo--;

        //generate the actual projectile
        FireProjectile(aDirection, ownerId);


        //we need to reload before we can fire again
        isLoaded = false;

        //schedule a completion of the reloading in 
            seconds:
            CancelInvoke("Reloaded");
            Invoke("Reloaded", reloadTime);


            }

        public virtual void FireProjectile(Vector3 fireDirection, int ownerID)
        {
            //make the first projectile
            theProjectile = MakeProjectile(ownerID);

            //direct the projectile towards the direction of fire
            theProjectile.LookAt(theProjectile.position + fireDirection);

            // add some force to move our projectile

            theProjectile.GetComponent().velocity = fireDirection * projectileSpeed;

                }

                public virtual Transform MakeProjectile(int ownerID)
                {
                //create a projectile
                theProjectile = SpawnController.Instance.Spawn(projectileGo, myTransform.position + spawnPosOffset + (myTransform.forward * forwardOffset), myTransform.rotation);
                theProjectileGo = theProjectile.gameObject;
                theProjectileGo.layer = myLayer;

                //grab a ref to the projectiles controller so we can pass on some information about it
                theProjectileController = theProjectileGo.GetComponent();

                    //set ownerID so we know who sent it
                    theProjectileController.SetOwnerType(ownerID);

                    Physics.IgnoreLayerCollision(myTransform.gameObject.layer, myLayer);


                    //Note: Make sure that the parentCollider is a collision mesh which represents the firing object
                    //or a collision mesh likely to be hit by a projectile as it is being fired from the vehicle.
                    //One limitation with this system is that it only reliably supports a single collision mesh

                    if (parentCollider != null)
                    {
                        //disable collision between 'us' and our projectile so as not to hit ourselves with it!
                        Physics.IgnoreCollision(theProjectile.GetComponent(), parentCollider);

                        }

                            //return this projectile in case we want to do something else to it
                            return theProjectile;
                        }



        }

    }


BaseAIController



    using UnityEngine;
    using System.Collections;
    using AIStates;
    using GameEngine.FrameWork.Common.Utility;

    [AddComponentMenu("Base/AI Controller")]

    public class BaseAIController : ExtendedCustomMonoBehaviour
    {

    // AI states are defined in the AIStates namespace

        private Transform proxyTarget;
        private Vector3 relativeTarget;
        private float targetAngle;
        private RaycastHit hit;
        private Transform tempTransform;
        private Vector3 tempDirVec;
        private bool didInit;

        public float horz;
        public float vert;

        private int obstacleHitType;

    // editor changeable / visible
        public bool isStationary;

        public AIState currentAIState;

        public float patrolSpeed = 5f;
        public float patrolTurnSpeed = 10f;
        public float wallAvoidDistance = 40f;

        public Transform followTarget;

        public float modelRotateSpeed = 15f;
        public int followTargetMaxTurnAngle = 120;

        public float minChaseDistance = 2f;
        public float maxChaseDistance = 10f;
        public float visionHeightOffset = 1f;

        [System.NonSerialized]
        public Vector3 moveDirection;

    // waypoint following related variables
        public WayPointsController myWayControl;

        public int currentWaypointNum;

        [System.NonSerialized]
        public Transform currentWaypointTransform;

        private int totalWaypoints;

        private Vector3 nodePosition;
        private Vector3 myPosition;
        private Vector3 diff;
        private float currentWayDist;

        [System.NonSerialized]
        public bool reachedLastWaypoint;
        private Vector3 moveVec;
        private Vector3 targetMoveVec;
        private float distanceToChaseTarget;

        public float waypointDistance = 5f;
        public float moveSpeed = 30f;
        public float pathSmoothing = 2f;
        public bool shouldReversePathFollowing;
        public bool loopPath;
        public bool destroyAtEndOfWaypoints;

        public bool faceWaypoints;
        public bool startAtFirstWaypoint;

        [System.NonSerialized]
        public bool isRespawning;

        private int obstacleFinderResult;
        public Transform rotateTransform;

        [System.NonSerialized]
        public Vector3 RelativeWaypointPosition;

        public bool AIControlled;

        public void Start()
        {
            Init();
        }

        public virtual void Init()
        {
            // cache ref to gameObject
            myGO = gameObject;

            // cache ref to transform
            myTransform = transform;

            // rotateTransform may be set if the object we rotate is different to the main transform
        if (rotateTransform == null)
            rotateTransform = myTransform;

            // cache a ref to our rigidbody
            myBody = myTransform.GetComponent();

            // init done!
            didInit = true;
        }

        public void SetAIControl(bool state)
        {
            AIControlled = state;
        }

        // set up AI parameters --------------------

        public void SetPatrolSpeed(float aNum)
        {
            patrolSpeed = aNum;
        }

        public void SetPatrolTurnSpeed(float aNum)
        {
            patrolTurnSpeed = aNum;
        }

        public void SetWallAvoidDistance(float aNum)
        {
        wallAvoidDistance = aNum;
        }

        public void SetWaypointDistance(float aNum)
        {
            waypointDistance = aNum;
        }

        public void SetMoveSpeed(float aNum)
        {
            moveSpeed = aNum;
        }

        public void SetMinChaseDistance(float aNum)
        {
            minChaseDistance = aNum;
        }

        public void SetMaxChaseDistance(float aNum)
        {
            maxChaseDistance = aNum;
        }

        public void SetPathSmoothing(float aNum)
        {
            pathSmoothing = aNum;
        }

        // -----------------------------------------

        public virtual void SetAIState(AIState newState)
        {
        // update AI state
            currentAIState = newState;
        }

        public virtual void SetChaseTarget(Transform theTransform)
        {
        // set a target for this AI to chase, if required
            followTarget = theTransform;
        }

        public virtual void Update()
        {
        // make sure we have initialized before doing anything
        if (!didInit)
            Init();

        // check to see if we're supposed to be controlling the player
        if (!AIControlled)
            return;

        // do AI updates
            UpdateAI();
        }

        public virtual void UpdateAI()
        {
        // reset our inputs
            horz = 0;
            vert = 0;

        int obstacleFinderResult = IsObstacleAhead();

        switch (currentAIState)
        {
        // -----------------------------
            case AIState.moving_looking_for_target:
        // look for chase target
            if (followTarget != null)
                LookAroundFor(followTarget);

        // the AvoidWalls function looks to see if there's anything in-front. If there is,
        // it will automatically change the value of moveDirection before we do the actual move
            if (obstacleFinderResult == 1)
            { // GO LEFT
                SetAIState(AIState.stopped_turning_left);
            }
            if (obstacleFinderResult == 2)
            { // GO RIGHT
            SetAIState(AIState.stopped_turning_right);
            }

            if (obstacleFinderResult == 3)
            { // BACK UP
                SetAIState(AIState.backing_up_looking_for_target);
            }

            // all clear! head forward
            MoveForward();
            break;
            case AIState.chasing_target:
            // chasing
            // in case mode, we point toward the target and go right at it!

            // quick check to make sure that we have a target (if not, we drop back to patrol mode)
            if (followTarget == null)
            SetAIState(AIState.moving_looking_for_target);

            // the TurnTowardTarget function does just that, so to chase we just throw it the current target
            TurnTowardTarget(followTarget);

            // find the distance between us and the chase target to see if it is within range
            distanceToChaseTarget = Vector3.Distance(myTransform.position, followTarget.position);

            // check the range
            if (distanceToChaseTarget > minChaseDistance)
            {
            // keep charging forward
                MoveForward();
            }

        // here we do a quick check to test the distance between AI and target. If it's higher than
        // our maxChaseDistance variable, we drop out of chase mode and go back to patrolling.
            if (distanceToChaseTarget > maxChaseDistance || CanSee(followTarget) == false)
            {
            // set our state to 1 - moving_looking_for_target
            SetAIState(AIState.moving_looking_for_target);
            }

                break;
        // -----------------------------

        case AIState.backing_up_looking_for_target:

        // look for chase target
            if (followTarget != null)
                LookAroundFor(followTarget);

            // backing up
            MoveBack();

            if (obstacleFinderResult == 0)
            {
            // now we've backed up, lets randomize whether to go left or right
            if (Random.Range(0, 100) > 50)
            {
                SetAIState(AIState.stopped_turning_left);
            }
            else
            {
                SetAIState(AIState.stopped_turning_right);
            }
        }
                break;
        case AIState.stopped_turning_left:
        // look for chase target
            if (followTarget != null)
                LookAroundFor(followTarget);

                // stopped, turning left
                TurnLeft();

            if (obstacleFinderResult == 0)
            {
                SetAIState(AIState.moving_looking_for_target);
            }
                break;

                case AIState.stopped_turning_right:
                // look for chase target
            if (followTarget != null)
                LookAroundFor(followTarget);

                // stopped, turning right
                TurnRight();

            // check results from looking, to see if path ahead is clear
            if (obstacleFinderResult == 0)
            {
                SetAIState(AIState.moving_looking_for_target);
            }
                break;
        case AIState.paused_looking_for_target:
        // standing still, with looking for chase target
        // look for chase target
            if (followTarget != null)
                LookAroundFor(followTarget);
                break;

            case AIState.translate_along_waypoint_path:
        // following waypoints (moving toward them, not pointing at them) at the speed of
        // moveSpeed

        // make sure we have been initialized before trying to access waypoints
            if (!didInit && !reachedLastWaypoint)
                return;

            UpdateWaypoints();

        // move the ship
            if (!isStationary)
            {
            targetMoveVec = Vector3.Normalize(currentWaypointTransform.position - myTransform.position);
            moveVec = Vector3.Lerp(moveVec, targetMoveVec, Time.deltaTime * pathSmoothing);
            myTransform.Translate(moveVec * moveSpeed * Time.deltaTime);
            MoveForward();

            if (faceWaypoints)
            {
                TurnTowardTarget(currentWaypointTransform);
            }
        }
            break;

        case AIState.steer_to_waypoint:

        // make sure we have been initialized before trying to access waypoints
            if (!didInit && !reachedLastWaypoint)
            return;

            UpdateWaypoints();

            if (currentWaypointTransform == null)
            {
        // it may be possible that this function gets called before waypoints have been set up, so we catch any nulls here
                return;
            }

        // now we just find the relative position of the waypoint from the car transform,
        // that way we can determine how far to the left and right the waypoint is.
            RelativeWaypointPosition = myTransform.InverseTransformPoint(currentWaypointTransform.position);

        // by dividing the horz position by the magnitude, we get a decimal percentage of the turn angle that we can use to drive the wheels
            horz = (RelativeWaypointPosition.x / RelativeWaypointPosition.magnitude);

        // now we do the same for torque, but make sure that it doesn't apply any engine torque when going around a sharp turn...
            if (Mathf.Abs(horz) < 0.5f)
            {
                vert = RelativeWaypointPosition.z / RelativeWaypointPosition.magnitude - Mathf.Abs(horz);
            }
            else
            {
                NoMove();
            }
                break;

            case AIState.steer_to_target:

            // make sure we have been initialized before trying to access waypoints
            if (!didInit)
                return;

            if (followTarget == null)
            {
            // it may be possible that this function gets called before a targer has been set up, so we catch any nulls here
                return;
            }

        // now we just find the relative position of the waypoint from the car transform,
        // that way we can determine how far to the left and right the waypoint is.
            RelativeWaypointPosition = transform.InverseTransformPoint(followTarget.position);

        // by dividing the horz position by the magnitude, we get a decimal percentage of the turn angle that we can use to drive the wheels
            horz = (RelativeWaypointPosition.x / RelativeWaypointPosition.magnitude);

        // if we're outside of the minimum chase distance, drive!
            if (Vector3.Distance(followTarget.position, myTransform.position) > minChaseDistance)
            {
                MoveForward();
            }
            else
            {
                NoMove();
            }

            if (followTarget != null)
                LookAroundFor(followTarget);

        // the AvoidWalls function looks to see if there's anything in-front. If there is,
        // it will automatically change the value of moveDirection before we do the actual move
            if (obstacleFinderResult == 1)
            { // GO LEFT
                TurnLeft();
            }

            if (obstacleFinderResult == 2)
            { // GO RIGHT
                TurnRight();
            }

            if (obstacleFinderResult == 3)
            { // BACK UP
                MoveBack();
            }

            break;

            case AIState.paused_no_target:
        // paused_no_target
            break;

            default:
        // idle (do nothing)
            break;
            }
        }

        public virtual void TurnLeft()
        {
            horz = -1;
        }

        public virtual void TurnRight()
        {
            horz = 1;
        }

        public virtual void MoveForward()
        {
            vert = 1;
        }

        public virtual void MoveBack()
        {
            vert = -1;
        }

        public virtual void NoMove()
        {
            vert = 0;
        }

        public virtual void LookAroundFor(Transform aTransform)
        {
        // here we do a quick check to test the distance between AI and target. If it's higher than
        // our maxChaseDistance variable, we drop out of chase mode and go back to patrolling.
            if (Vector3.Distance(myTransform.position, aTransform.position) < maxChaseDistance)
            {
        // check to see if the target is visible before going into chase mode
            if (CanSee(followTarget) == true)
            {
                // set our state to chase the target
                SetAIState(AIState.chasing_target);
            }
            }
        }

        private int obstacleFinding;

        public virtual int IsObstacleAhead()
        {
            obstacleHitType = 0;

            // quick check to make sure that myTransform has been set
            if (myTransform == null)
            {
                return 0;
            }

        // draw this raycast so we can see what it is doing
            Debug.DrawRay(myTransform.position, ((myTransform.forward + (myTransform.right * 0.5f)) * wallAvoidDistance));
            Debug.DrawRay(myTransform.position, ((myTransform.forward + (myTransform.right * -0.5f)) * wallAvoidDistance));

        // cast a ray out forward from our AI and put the 'result' into the variable named hit
            if (Physics.Raycast(myTransform.position, myTransform.forward + (myTransform.right * 0.5f), out hit, wallAvoidDistance))
            {
            // obstacle
            // it's a left hit, so it's a type 1 right now (though it could change when we check on the other side)
            obstacleHitType = 1;
            }

            if (Physics.Raycast(myTransform.position, myTransform.forward + (myTransform.right * -0.5f), out hit, wallAvoidDistance))
            {
            // obstacle
            if (obstacleHitType == 0)
            {
            // if we haven't hit anything yet, this is a type 2
            obstacleHitType = 2;
            }
            else
            {
            // if we have hits on both left and right raycasts, it's a type 3
            obstacleHitType = 3;
            }
            }

                return obstacleHitType;
        }

        public void TurnTowardTarget(Transform aTarget)
        {
            if (aTarget == null)
            return;

        // Calculate the target position relative to the
        // target this transforms coordinate system.
        // eg. a positive x value means the target is to
        // to the right of the car, a positive z means
        // the target is in front of the car
        relativeTarget = rotateTransform.InverseTransformPoint(aTarget.position); // note we use rotateTransform as a rotation object rather than myTransform!

        // Calculate the target angle
        targetAngle = Mathf.Atan2(relativeTarget.x, relativeTarget.z);

        // Atan returns the angle in radians, convert to degrees
        targetAngle *= Mathf.Rad2Deg;

        // The wheels should have a maximum rotation angle
        targetAngle = Mathf.Clamp(targetAngle, -followTargetMaxTurnAngle - targetAngle, followTargetMaxTurnAngle);

        // turn towards the target at the rate of modelRotateSpeed
        rotateTransform.Rotate(0, targetAngle * modelRotateSpeed * Time.deltaTime, 0);
        }

        public bool CanSee(Transform aTarget)
        {
            // first, let's get a vector to use for raycasting by subtracting the target position from our AI position
            tempDirVec = Vector3.Normalize(aTarget.position - myTransform.position);

            // lets have a debug line to check the distance between the two manually, in case you run into trouble!
            Debug.DrawLine(myTransform.position, aTarget.position);

            // cast a ray from our AI, out toward the target passed in (use the tempDirVec magnitude as the distance to cast)
            if (Physics.Raycast(myTransform.position + (visionHeightOffset * myTransform.up), tempDirVec, out hit, maxChaseDistance))
            {
                // check to see if we hit the target
            if (hit.transform.gameObject == aTarget.gameObject)
            {
                return true;
            }
        }

        // nothing found, so return false
        return false;
        }

        public void SetWayController(WayPointsController aControl)
        {
            myWayControl = aControl;
            aControl = null;

            // grab total waypoints
            totalWaypoints = myWayControl.GetTotal();

            // make sure that if you use SetReversePath to set shouldReversePathFollowing that you
            // call SetReversePath for the first time BEFORE SetWayController, otherwise it won't set the first waypoint correctly

            if (shouldReversePathFollowing)
            {
            currentWaypointNum = totalWaypoints - 1;
            }
            else
            {
            currentWaypointNum = 0;
            }

            Init();

            // get the first waypoint from the waypoint controller
            currentWaypointTransform = myWayControl.GetWaypoint(currentWaypointNum);

            if (startAtFirstWaypoint)
            {
            // position at the currentWaypointTransform position
            myTransform.position = currentWaypointTransform.position;
            }
        }

        public void SetReversePath(bool shouldRev)
        {
            shouldReversePathFollowing = shouldRev;
        }

        public void SetSpeed(float aSpeed)
        {
            moveSpeed = aSpeed;
        }

        public void SetPathSmoothingRate(float aRate)
        {
            pathSmoothing = aRate;
        }

        public void SetRotateSpeed(float aRate)
        {
            modelRotateSpeed = aRate;
        }

        void UpdateWaypoints()
        {
        // If we don't have a waypoint controller, we safely drop out
            if (myWayControl == null)
                return;

            if (reachedLastWaypoint && destroyAtEndOfWaypoints)
            {
                // destroy myself(!)
                Destroy(gameObject);
                return;
            }
            else if (reachedLastWaypoint)
            {
                currentWaypointNum = 0;
                reachedLastWaypoint = false;
            }

        // because of the order that scripts run and are initialised, it is possible for this function
        // to be called before we have actually finished running the waypoints initialization, which
        // means we need to drop out to avoid doing anything silly or before it breaks the game.
            if (totalWaypoints == 0)
            {
                // grab total waypoints
                totalWaypoints = myWayControl.GetTotal();
                return;
            }

            if (currentWaypointTransform == null)
            {
                // grab our transform reference from the waypoint controller
                currentWaypointTransform = myWayControl.GetWaypoint(currentWaypointNum);
            }

        // now we check to see if we are close enough to the current waypoint
        // to advance on to the next one

            myPosition = myTransform.position;
            myPosition.y = 0;

        // get waypoint position and 'flatten' it
            nodePosition = currentWaypointTransform.position;
            nodePosition.y = 0;

        // check distance from this to the waypoint

            currentWayDist = Vector3.Distance(nodePosition, myPosition);

            if (currentWayDist < waypointDistance)
            {
                // we are close to the current node, so let's move on to the next one!

            if (shouldReversePathFollowing)
            {
                currentWaypointNum--;
                // now check to see if we have been all the way around
            if (currentWaypointNum < 0)
            {
                // just incase it gets referenced before we are destroyed, let's keep it to a safe index number
                currentWaypointNum = 0;
                // completed the route!
                reachedLastWaypoint = true;
                // if we are set to loop, reset the currentWaypointNum to 0
            if (loopPath)
            {
                currentWaypointNum = totalWaypoints;

                // the route keeps going in a loop, so we don't want reachedLastWaypoint to ever become true
                reachedLastWaypoint = false;
            }
        // drop out of this function before we grab another waypoint into currentWaypointTransform, as
        // we don't need one and the index may be invalid
                return;
            }
            }
            else
            {
                currentWaypointNum++;
        // now check to see if we have been all the way around
            if (currentWaypointNum >= totalWaypoints)
            {
                // completed the route!
                reachedLastWaypoint = true;
                // if we are set to loop, reset the currentWaypointNum to 0
            if (loopPath)
            {
                currentWaypointNum = 0;

            // the route keeps going in a loop, so we don't want reachedLastWaypoint to ever become true
                reachedLastWaypoint = false;
            }
        // drop out of this function before we grab another waypoint into currentWaypointTransform, as
        // we don't need one and the index may be invalid
                return;
            }
        }

        // grab our transform reference from the waypoint controller
        currentWaypointTransform = myWayControl.GetWaypoint(currentWaypointNum);

        }
        }

        public float GetHorizontal()
        {
            return horz;
        }

        public float GetVertical()
        {
            return vert;
        }

    }