Last active
December 5, 2016 03:50
-
-
Save keiranlovett/8804828ad5f6ae938b29 to your computer and use it in GitHub Desktop.
For use with VisionPunks Ultimate First Person Shooter. This is an extension of vp_Interactable that allows players to board interactive items - such as computer terminals or vehicles via input. The script allows for different 'seat' positions and permissions Check for the method InputInteract() in the vp_FPInput class.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///////////////////////////////////////////////////////////////////////////////// | |
// | |
// Vehiclemanager.cs | |
// https://twitter.com/keiranlovett | |
// http://www.keiranlovett.com | |
// | |
// Description: This is an extension of vp_Interactable that allows players | |
// to board interactive items - such as computer terminals or | |
// vehicles via input. The script allows for different 'seat' | |
// positions and permissions Check for the method InputInteract() | |
// in the vp_FPInput class. | |
// | |
// Setup: Requires VisionPunk UFPS to work (from version 1.4.9). | |
// Append to vp_PlayerEventHandler.cs the following: | |
// | |
// public vp_Activity Board; | |
// public vp_Activity<int> SetSeat; | |
// public vp_Attempt SetPrevSeat; | |
// public vp_Attempt SetNextSeat; | |
// | |
// Attach script to top level of "vehicle" GameObject. | |
// Define seat types, and attach child object of Vehicle GO | |
// to act as "seat position", and define transform in SeatAllocations. | |
// | |
// Known Issues: 1) *RESOLVED* Problem with array sorting, when a player first enters the seat it'll skip past 0 and go to the second seat. | |
// 2) *RESOLVED* Even if the boardable has no empty seats it will still try to place a player in it. | |
// 3) *RESOLVED* Figure out the best way to indicate Player control privileges. | |
// 4) Make SeatPosition a Vector3 using widgets to indicate position instead of a gameobject? | |
// 5) Add in WeaponsAllowed and MovementAllowed scripts | |
// 6) Add variable to determine if full instead of array lookup? | |
// 8) *RESOLVED* FATAL CRASH - When a Player enters a vehicle that has just been destroyed. Crashes Unity itself. | |
// | |
///////////////////////////////////////////////////////////////////////////////// | |
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class vehicleManager: vp_Interactable { | |
public vehicleController m_vehicleController; | |
[System.Serializable] | |
public class seatAllocations { | |
public enum controlType { | |
Pilot, Passenger, Gunner, Custom | |
} | |
public controlType ControlType; //Useful for passing information to other scripts on control over vehicles. Example: Passenger or Driver | |
public Transform SeatPosition; //The Transform reference position of the player when in the vehicle. | |
public Transform SeatOccupied; //The Player currently occupying the position. | |
public bool WeaponsAllowed; //Determines if weapons should be equipped, for example weapons should be allowed in a passenger seat of a boat, but not pilot. | |
public bool MovementAllowed; //Determines if the Player can move around while parented to the vehicle, for example moving around a troop bay. | |
public seatAllocations(controlType controlType, Transform seatPosition, Transform seatOccupied, bool weaponsAllowed, bool movementAllowed) { | |
ControlType = controlType; | |
SeatPosition = seatPosition; | |
SeatOccupied = seatOccupied; | |
WeaponsAllowed = weaponsAllowed; | |
MovementAllowed = movementAllowed; | |
} | |
} | |
public List < seatAllocations > SeatAllocations = new List < seatAllocations > (); | |
// Audio | |
protected AudioSource AudioSource = null; | |
[System.Serializable] | |
public class vp_ClimbingSounds { | |
public AudioSource AudioSource = null; | |
public List < AudioClip > MountSounds = new List < AudioClip > (); | |
public List < AudioClip > DismountSounds = new List < AudioClip > (); | |
} | |
public vp_ClimbingSounds Sounds; | |
protected AudioClip m_SoundToPlay = null; // the current sound to be played | |
protected AudioClip m_LastPlayedSound = null; // used to make sure we don't place the same sound twice in a row | |
protected vp_Timer.Handle m_ClimbingSoundTimer = new vp_Timer.Handle(); | |
public float MountAgainTimeout = 1; // Time in seconds before climbing on this climbable is allowed again | |
public float DismountForce = 0.2f; | |
public bool m_IsBoarded = false; // is this object in switched mode | |
protected int m_CurrentSeatAllocationsIndex; | |
protected int m_LastWeaponEquipped = 0; // used to store our weapon so we can reequip it | |
protected float m_CanMountAgain = 0; // timer for ClimbAgainThreshold | |
protected Vector3 m_CachedDirection = Vector3.zero; // cache the direction to keep proper distance from climbable | |
protected Vector2 m_CachedRotation = Vector2.zero; // cache the rotation so we can limit yaw | |
protected override void Start() { | |
m_CanMountAgain = Time.time; | |
base.Start(); | |
if (AudioSource == null) AudioSource = GetComponent < AudioSource > () == null ? gameObject.AddComponent < AudioSource > () : GetComponent < AudioSource > (); | |
// Set up the reference to the aeroplane controller. | |
m_vehicleController = GetComponent < UnitySampleAssets.Vehicles.Aeroplane.AeroplaneUserControl4Axis > (); | |
} | |
/// <summary> | |
/// try to interact with this object | |
/// </summary> | |
public override bool TryInteract(vp_FPPlayerEventHandler player) { | |
if (!enabled) return false; | |
if (Time.time < m_CanMountAgain) return false; | |
if (m_Player == null) m_Player = player; | |
if (m_Player.Interactable.Get() != null) return false; | |
if (m_Controller == null) m_Controller = m_Player.GetComponent < vp_FPController > (); | |
if (m_Camera == null) m_Camera = m_Player.GetComponentInChildren < vp_FPCamera > (); | |
if (Sounds.AudioSource == null) Sounds.AudioSource = m_Player.GetComponent < AudioSource > (); | |
m_Player.Register(this); | |
m_Player.Interactable.Set(this); // sets what the player is currently interacting with | |
//Quickly make sure there is an empty seat before trying to board | |
for (int i = 0; i < SeatAllocations.Count; i++) { | |
if (SeatAllocations[i].SeatOccupied != null) { | |
return false; | |
} | |
} | |
return m_Player.Board.TryStart(); | |
} | |
/// <summary> | |
/// Sets up some things to allow boarding to work correctly. For one | |
/// we need to override normal input so we can use our custom input. | |
/// </summary> | |
protected virtual void OnStart_Board() { | |
if (m_Player == null) return; | |
m_CachedDirection = m_Camera.Transform.forward; | |
m_CachedRotation = m_Player.Rotation.Get(); | |
//Move this later, but for now we disable the CharacterController | |
m_Controller.CharacterController.enabled = false; | |
// turn physics off | |
m_Controller.PhysicsGravityModifier = 0.0f; | |
// reset camera initial rotation (otherwise the camera | |
// will be rotated with the offset it had at spawn-time) | |
m_Camera.SetRotation(m_Camera.Transform.eulerAngles, false); | |
m_Player.Register(this); | |
//Firstly, we'll locally set the m_IsBoarded variable to TRUE, this will be a simple way to locally allow control of the vehicle and such. | |
m_IsBoarded = true; | |
// stop jumping | |
m_Player.Jump.Stop(); | |
// disallow normal input while climbing | |
m_Player.InputAllowGameplay.Set(false); | |
// stop any movement on our controller. this is helpful in case we jumped onto the climbable. | |
m_Player.Stop.Send(); | |
// let's unequip our weapon | |
m_LastWeaponEquipped = m_Player.CurrentWeaponIndex.Get(); | |
m_Player.SetWeapon.TryStart(0); | |
m_Player.Interactable.Set(null); | |
if (m_Controller.Transform.GetComponent < Collider > ().enabled && m_Transform.GetComponent < Collider > ().enabled) Physics.IgnoreCollision(m_Controller.Transform.GetComponent < Collider > (), m_Transform.GetComponent < Collider > (), true); // ignore collisions with this object | |
//Child the player | |
m_Player.transform.parent = this.transform; | |
PlaySound(Sounds.MountSounds); | |
m_Player.SetNextSeat.Try(); | |
} | |
/// <summary> | |
/// Actions to perform when the player is leaving the boardable. | |
/// Reverts our player back to a normal state, and empties from boardable. Used for exiting and death. | |
/// </summary> | |
protected virtual void OnStop_Board() { | |
if (m_Player == null) return; | |
m_Player.gameObject.SetActive(true); | |
m_Controller.GetComponent < CharacterController > ().enabled = true; | |
m_Player.Interactable.Set(null); | |
m_Player.InputAllowGameplay.Set(true); | |
m_Player.Unregister(this); | |
m_Player.SetWeapon.TryStart(m_LastWeaponEquipped); | |
//The local player is no longer in the vehicle, lets update m_IsBoarded to FALSE | |
m_IsBoarded = false; | |
// re-allow normal input | |
m_Player.SetState("Pilot", false); // restore default state on all components | |
// When the player leaves the boardable we'll strike them from SeatAllocations where they were last, then empty from the cache | |
SeatAllocations[m_CurrentSeatAllocationsIndex].SeatOccupied = null; | |
m_CurrentSeatAllocationsIndex = 0; | |
// Orphan the player from the boardable, lets do this early on to avoid any conflicts. | |
m_Player.transform.parent = null; | |
m_CanMountAgain = Time.time + MountAgainTimeout; | |
if (m_Controller.Transform.GetComponent < Collider > ().enabled && m_Transform.GetComponent < Collider > ().enabled) Physics.IgnoreCollision(m_Controller.Transform.GetComponent < Collider > (), m_Transform.GetComponent < Collider > (), false); // allow player to collide with this object again | |
PlaySound(Sounds.DismountSounds); | |
// add a force repelling player from climbable upon dismount. Add slight up-force (push forward & up) | |
Vector3 force = m_Controller.Transform.forward * DismountForce; | |
force *= 2.0f; | |
force.y = DismountForce * 0.5f; | |
m_Player.Stop.Send(); | |
m_Controller.AddForce(force); | |
// interpolate camera pitch to look straight ahead | |
StartCoroutine("RestorePitch"); | |
m_Player = null; | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
public override void FinishInteraction() { | |
if (m_IsBoarded) m_Player.Climb.TryStop(); | |
} | |
/// <summary> | |
/// this callback is triggered right after the activity in question | |
/// has been approved for activation. it moves the current weapon | |
/// model to its exit offset, changes the weapon model and moves | |
/// the new weapon into view. this message is usually broadcast | |
/// by vp_FPInput, but may also be sent by things that have given | |
/// weapons to the player, e.g. weapon pickups | |
/// </summary> | |
protected virtual void OnStart_SetSeat() { | |
m_Player.SetSeat.AutoDuration = 0; | |
} | |
/// <summary> | |
/// adds a condition (a rule set) that must be met for the | |
/// event handler 'SetSeat' activity to successfully activate. | |
/// NOTE: other scripts may have added conditions to this | |
/// activity as well. | |
/// </summary> | |
protected virtual bool CanStart_SetSeat() { | |
// fetch weapon index from when 'SetSeat.TryStart' was called | |
int seatIndex = (int) m_Player.SetSeat.Argument; | |
// can't set an unexisting weapon | |
if (seatIndex < 0 || seatIndex > SeatAllocations.Count - 1) return false; | |
// See if the seat is already occupied. | |
if (SeatAllocations[seatIndex].SeatOccupied != null) return false; | |
return true; | |
} | |
/// <summary> | |
/// toggles to the previous seat if currently allowed, | |
/// otherwise attempts to skip past it | |
/// </summary> | |
protected virtual bool OnAttempt_SetPrevSeat() { | |
int i = m_CurrentSeatAllocationsIndex; | |
while (!m_Player.SetSeat.TryStart(i)) { | |
i--; | |
if (i < 0) i = SeatAllocations.Count; | |
} | |
return true; | |
} | |
/// <summary> | |
/// toggles to the next seat if currently allowed, | |
/// otherwise attempts to skip past it | |
/// </summary> | |
protected virtual bool OnAttempt_SetNextSeat() { | |
int i = m_CurrentSeatAllocationsIndex; | |
while (!m_Player.SetSeat.TryStart(i)) { | |
if (i > SeatAllocations.Count) i = -1; //Since we'll increment i in the next line regardless for the if statement lets send it to -1 | |
i++; | |
} | |
return true; | |
} | |
/// <summary> | |
/// this callback is triggered when the activity in question | |
/// deactivates | |
/// </summary> | |
protected virtual void OnStop_SetSeat() { | |
// fetch seat index from when 'SetSeat.TryStart' was called | |
int seatIndex = (int) m_Player.SetSeat.Argument; | |
m_CurrentSeatAllocationsIndex = seatIndex; | |
for (int i = 0; i < SeatAllocations.Count; i++) { | |
if (SeatAllocations[i].SeatOccupied == m_Player.transform) { | |
SeatAllocations[i].SeatOccupied = null; | |
break; | |
} | |
} | |
SeatAllocations[seatIndex].SeatOccupied = m_Player.transform; | |
if (SeatAllocations[seatIndex].WeaponsAllowed == true) { | |
//Players are allowed to show weapons | |
} | |
m_Player.SetState("Pilot", true); // restore default state on all components | |
m_vehicleController.GoTo((int) SeatAllocations[seatIndex].ControlType); | |
PlaySound(Sounds.MountSounds); | |
// If there's no gameobject to determine the position transform than exit early. Alert dev. | |
if (!SeatAllocations[seatIndex].SeatPosition) { | |
UnityEngine.Debug.Log("There is no gameObject for" + SeatAllocations[seatIndex] + " seatPosition transform."); | |
return; | |
} | |
m_Player.Position.Set(SeatAllocations[seatIndex].SeatPosition.position); | |
m_Player.Rotation.Set(Vector3.zero); | |
m_Player.gameObject.SetActive(false); | |
} | |
/// <summary> | |
/// this callback is triggered right after the activity in | |
/// question has been approved for activation. it strips the | |
/// player from the seat array. | |
/// </summary> | |
protected virtual void OnStart_Dead() { | |
//Eject the player from the vehicle | |
m_Player.Board.TryStop(); | |
} | |
/// <summary> | |
/// This restores player pitch after dismounting the climbable. | |
/// </summary> | |
protected virtual IEnumerator RestorePitch() { | |
float t = 0; | |
// rotates the camera pitch to straight ahead over a short period | |
while (t < 1 && vp_Input.GetAxisRaw("Mouse Y") == 0.0f) // just don't interfere with mouselook | |
{ | |
t += Time.deltaTime; | |
//m_Player.Rotation.Set(Vector2.Lerp(m_Player.Rotation.Get(), new Vector2(0.0f, m_Player.Rotation.Get().y), t)); | |
yield | |
return new WaitForEndOfFrame(); | |
} | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
protected virtual void PlaySound(List < AudioClip > sounds) { | |
if (Sounds.AudioSource == null) return; | |
if (sounds == null || sounds.Count == 0) return; | |
reroll: m_SoundToPlay = sounds[Random.Range(0, sounds.Count)]; | |
if (m_SoundToPlay == null) return; | |
if (m_SoundToPlay == m_LastPlayedSound && sounds.Count > 1) goto reroll; | |
Sounds.AudioSource.PlayOneShot(m_SoundToPlay); | |
m_LastPlayedSound = m_SoundToPlay; | |
} | |
/** | |
* Destroy the vehicle | |
*/ | |
public virtual void Die() { | |
//If player is set, eject them. Explosions should then also kill them. | |
if (m_Player != null) m_Player.Board.Stop(); | |
} | |
/* | |
Move this to a separate script later, used for reference. | |
*/ | |
protected virtual void FixedUpdate() { | |
if(vp_Input.GetButtonDown("Interact")){ | |
m_Player.Board.TryStop(); | |
} | |
if (Input.GetKeyDown(KeyCode.Alpha1)) m_Player.SetSeat.TryStart(0); | |
if (Input.GetKeyDown(KeyCode.Alpha2)) m_Player.SetSeat.TryStart(1); | |
if (Input.GetKeyDown(KeyCode.Alpha3)) m_Player.SetSeat.TryStart(2); | |
if (Input.GetKeyDown(KeyCode.Alpha4)) m_Player.SetSeat.TryStart(3); | |
if (Input.GetKeyDown(KeyCode.Alpha5)) m_Player.SetSeat.TryStart(5); | |
if (Input.GetKeyDown(KeyCode.Alpha6)) m_Player.SetSeat.TryStart(6); | |
if (Input.GetKeyDown(KeyCode.Alpha7)) m_Player.SetSeat.TryStart(7); | |
if (Input.GetKeyDown(KeyCode.Alpha8)) m_Player.SetSeat.TryStart(8); | |
if (Input.GetKeyDown(KeyCode.Alpha9)) m_Player.SetPrevSeat.Try(); | |
if (Input.GetKeyDown(KeyCode.Alpha0)) m_Player.SetNextSeat.Try(); | |
if (m_IsBoarded == true && vp_Input.GetButtonDown("Jump")) { | |
m_Player.SetNextSeat.Try(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Row 37 should be updated for your specific controller - so Realistic Car Controller would be public CarControllerV2 m_vehicleController;
Row 95 - similar edit as above for your specific controller
m_vehicleController = GetComponent < CarControllerV2 > ();