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.
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,
}
}
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)
{
}
}
}
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);
}
}
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;
}
}
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()
{
}
}
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);
}
}
}
}
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;
}
}
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";
}
}
}
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;
}
}
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;
}
}
}
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);
}
}
}
}
}
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;
}
}
}
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;
}
}