Skip to content

Instantly share code, notes, and snippets.

@keiranlovett
Last active December 5, 2016 03:50
Show Gist options
  • Save keiranlovett/8804828ad5f6ae938b29 to your computer and use it in GitHub Desktop.
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.
/////////////////////////////////////////////////////////////////////////////////
//
// 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();
}
}
}
@redhwk
Copy link

redhwk commented Feb 20, 2015

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 > ();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment