Every sound in the game is now a ManageableAudio prefab. The prefabs are picked up in an audio library automatically and other game objects can get the sound they need from the AudioManager. They obtain a ManageableAudio instance which offers various methods to interact with the AudioSource. Depending on how they request the ManageableAudio, the AudioSource is attached to the requesting GameObject, to the Scene or the global management Scene. This provides options to play sounds spatially, globally and scene independent. The prefabs are identified by a tag and an ID, so it is easy to swap out sounds globally, by just replacing the prefab.
332 lines
9.8 KiB
C#
332 lines
9.8 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using static AffectingForcesManager;
|
|
using ShipHandling;
|
|
using Managers;
|
|
using GameLogic;
|
|
|
|
public class Ship : MonoBehaviour
|
|
{
|
|
public int InstanceID { get; private set; }
|
|
public ShipProperties props;
|
|
public ShipState state;
|
|
public BoostCapacityUI boostUI;
|
|
// Private variables
|
|
public CameraOperator cameraOperator;
|
|
|
|
private AffectingForcesManager forceManager;
|
|
private Rigidbody body;
|
|
// Saves the current input value for thrust
|
|
private bool canBoost = true;
|
|
private TackleDetection[] tackleDetectors;
|
|
private bool isCriticalTackle = false;
|
|
private bool isTackled = false;
|
|
private float tackledTime = 0f;
|
|
// Current Zone the player occupies
|
|
private Zone zone = Zone.NimbleZone;
|
|
|
|
private ManageableAudio ThrusterSound;
|
|
|
|
void Awake()
|
|
{
|
|
if (forceManager == null)
|
|
{
|
|
forceManager = GameObject.FindGameObjectWithTag("ForceManager").
|
|
GetComponent<AffectingForcesManager>();
|
|
}
|
|
body = GetComponent<Rigidbody>();
|
|
ThrusterSound = AudioManager.G.GetLocalSound("sound", 1, gameObject.transform);
|
|
}
|
|
|
|
// Start is called before the first frame update
|
|
void Start()
|
|
{
|
|
InstanceID = gameObject.GetInstanceID();
|
|
state.boostCapacity = props.maxBoostCapacity;
|
|
boostUI.SetMinBoostRatio(props.minBoostCapacity / props.maxBoostCapacity);
|
|
// GameManager.GM.RegisterPlayer(this);
|
|
cameraOperator.AddPlayer(gameObject);
|
|
|
|
tackleDetectors = GetComponentsInChildren<TackleDetection>();
|
|
foreach (TackleDetection td in tackleDetectors)
|
|
{
|
|
td.TackleResponse.AddListener(StartTackleResponse);
|
|
}
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
foreach (TackleDetection td in tackleDetectors)
|
|
{
|
|
td.TackleResponse.RemoveAllListeners();
|
|
}
|
|
}
|
|
|
|
// Update is called once per frame
|
|
void FixedUpdate()
|
|
{
|
|
if (state.reset)
|
|
{
|
|
state.reset = false;
|
|
body.velocity = Vector3.zero;
|
|
body.transform.rotation = body.transform.parent.rotation;
|
|
MatchManager.G.StartMatch();
|
|
}
|
|
|
|
// TODO: This could be more elegant maybe?
|
|
if (MatchManager.G.matchState != MatchState.Match)
|
|
{
|
|
body.constraints = RigidbodyConstraints.FreezeAll;
|
|
return;
|
|
}
|
|
body.constraints = RigidbodyConstraints.None;
|
|
// TODO: This belongs in the state object
|
|
zone = forceManager.GetZoneOfInstance(InstanceID);
|
|
//BoostStateUpdate(Time.deltaTime);
|
|
// Rotate the vehicle with the current steer velocity
|
|
// Calculate the magnitude of the acceleration with the current thrust
|
|
UpdateMovement();
|
|
BoostStateUpdate(Time.deltaTime);
|
|
UpdateTackleResponse(isCriticalTackle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Movement logic and simulation of the ship.
|
|
/// </summary>
|
|
void UpdateMovement()
|
|
{
|
|
if (state.thrustInput > 0)
|
|
{
|
|
if (!ThrusterSound.AudioSource.isPlaying)
|
|
{
|
|
ThrusterSound.PlayAudio();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ThrusterSound.StopAudio();
|
|
}
|
|
|
|
//Debug.Log("inupdatemove " + currentThrustInput);
|
|
// Player rotation is always possible and same speed
|
|
transform.Rotate(0, 0, -props.steerVelocity * state.steerInput * Time.deltaTime);
|
|
|
|
// Get and apply the current Gravity
|
|
state.currentGravity = forceManager.GetGravityForInstance(InstanceID)(transform);
|
|
body.AddForce(state.currentGravity, ForceMode.Acceleration);
|
|
|
|
float stunFactor = isCriticalTackle ? props.stunLooseControlFactor : 1f;
|
|
|
|
float thrust = IsBoosting() ? 1f : state.thrustInput;
|
|
Vector3 acceleration = props.thrustAcceleration * thrust * Time.deltaTime
|
|
* transform.up * stunFactor;
|
|
|
|
Vector3 currentVelocity = body.velocity;
|
|
|
|
Vector3 boostedAcceleration = BoostAcceleration(acceleration, state.currentGravity);
|
|
|
|
if (!isCriticalTackle)
|
|
{
|
|
// Add drag
|
|
if (zone == Zone.NimbleZone)
|
|
{
|
|
Vector3 dragDecceleration = DragDecceleration(currentVelocity, zone);
|
|
body.AddForce(dragDecceleration, ForceMode.Acceleration);
|
|
|
|
if (!isTackled)
|
|
{
|
|
// Add anti drift acceleration
|
|
Vector3 driftDampeningAcceleration =
|
|
DriftDampeningAcceleration(currentVelocity, zone);
|
|
body.AddForce(driftDampeningAcceleration, ForceMode.Acceleration);
|
|
}
|
|
}
|
|
|
|
if (currentVelocity.magnitude <= props.normalMaxVelocity || IsBoosting()
|
|
|| zone != Zone.NimbleZone)
|
|
{
|
|
body.AddForce(boostedAcceleration, ForceMode.Acceleration);
|
|
}
|
|
if (currentVelocity.magnitude >= props.absolutMaxVelocity && zone == Zone.NimbleZone)
|
|
{
|
|
body.velocity = body.velocity.normalized * props.absolutMaxVelocity;
|
|
}
|
|
}
|
|
|
|
// Default torque drag
|
|
body.AddRelativeTorque(body.angularVelocity * -props.torqueDrag, ForceMode.Acceleration);
|
|
|
|
|
|
Debug.DrawRay(transform.position, transform.up * (currentVelocity.magnitude + 3) * 0.5f,
|
|
Color.black);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates a vector to mitigate the ship drifting when it's changing direction.
|
|
/// </summary>
|
|
/// <param name="currentVelocity">Current velocity of the ship</param>
|
|
/// <param name="zone">Zone which the ship is in</param>
|
|
/// <returns></returns>
|
|
Vector3 DriftDampeningAcceleration(Vector3 currentVelocity, Zone zone)
|
|
{
|
|
Vector3 antiDriftVelocity;
|
|
float antiDriftFactor;
|
|
// Cancel out inertia/drifting
|
|
Vector3 up = transform.up;
|
|
Vector3 driftVelocity = currentVelocity - Vector3.Project(currentVelocity, up);
|
|
if (driftVelocity.magnitude < 0.1)
|
|
{
|
|
return Vector3.zero;
|
|
}
|
|
|
|
antiDriftVelocity = Vector3.Reflect(-driftVelocity, up) - driftVelocity;
|
|
antiDriftFactor = Mathf.InverseLerp(props.absolutMaxVelocity, props.normalMaxVelocity,
|
|
currentVelocity.magnitude);
|
|
|
|
antiDriftFactor = Mathf.Max(antiDriftFactor, props.minAntiDriftFactor);
|
|
|
|
Debug.DrawRay(transform.position, currentVelocity.normalized * currentVelocity.magnitude * 2, Color.cyan);
|
|
Debug.DrawRay(transform.position, driftVelocity.normalized * 5, Color.red);
|
|
Debug.DrawRay(transform.position, antiDriftVelocity.normalized * 5, Color.green);
|
|
|
|
return antiDriftVelocity * props.antiDriftAmount * antiDriftFactor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates drag on the ship depending on it's velocity and inhabited zone.
|
|
/// </summary>
|
|
/// <param name="currentVelocity">Velocity of the ship</param>
|
|
/// <param name="zone">Zone which the ship is in</param>
|
|
/// <returns></returns>
|
|
Vector3 DragDecceleration(Vector3 currentVelocity, Zone zone)
|
|
{
|
|
Vector3 drag = new Vector3();
|
|
float minDragFactor = Mathf.InverseLerp(props.absolutMaxVelocity, props.normalMaxVelocity,
|
|
currentVelocity.magnitude);
|
|
|
|
float normalDragFactor = Mathf.InverseLerp(props.normalMaxVelocity, 0,
|
|
currentVelocity.magnitude);
|
|
|
|
if (!IsBoosting() && zone == Zone.NimbleZone)
|
|
{
|
|
drag -= currentVelocity.normalized * props.normalDrag;
|
|
}
|
|
if (currentVelocity.magnitude >= props.normalMaxVelocity && zone == Zone.NimbleZone)
|
|
{
|
|
drag -= currentVelocity.normalized * props.maximumDrag;
|
|
}
|
|
return drag;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the boost input pressed and boosting possible?
|
|
/// </summary>
|
|
/// <returns>Boosting state</returns>
|
|
bool IsBoosting()
|
|
{
|
|
return state.boostInput > 0 && canBoost;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies boost to an acceleration vector.
|
|
/// This includes increasing acceleration and mitigating
|
|
/// the gravity.
|
|
/// </summary>
|
|
/// <param name="acceleration">Current acceleration vector</param>
|
|
/// <param name="currentGravity">Gravity vector which is in force</param>
|
|
/// <returns></returns>
|
|
Vector3 BoostAcceleration(Vector3 acceleration, Vector3 currentGravity)
|
|
{
|
|
if (IsBoosting())
|
|
{
|
|
acceleration *= props.boostMagnitude;
|
|
acceleration -= currentGravity * props.boostAntiGravityFactor;
|
|
}
|
|
return acceleration;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logic which depletes boost capacity when boost conditions are met.
|
|
/// </summary>
|
|
/// <param name="deltaTime">Time delta of the current frame</param>
|
|
void BoostStateUpdate(float deltaTime)
|
|
{
|
|
boostUI.UpdateFill(Math.Min(state.boostCapacity / props.maxBoostCapacity, 1));
|
|
if (IsBoosting() && state.thrustInput != 0)
|
|
{
|
|
state.boostCapacity -= deltaTime;
|
|
}
|
|
if (canBoost && zone == Zone.OutsideZone)
|
|
{
|
|
state.boostCapacity -= deltaTime * props.outsideBoostRate;
|
|
}
|
|
if (state.boostCapacity <= 0)
|
|
{
|
|
canBoost = false;
|
|
}
|
|
|
|
if ((state.boostInput <= 0 || state.thrustInput == 0 || !canBoost)
|
|
&& zone == Zone.NimbleZone
|
|
&& state.boostCapacity <= props.maxBoostCapacity)
|
|
{
|
|
state.boostCapacity += deltaTime;
|
|
}
|
|
// When your boost capacity is still critical, you can't start boosting immediately again.
|
|
// TODO: This is not tested well enough with players.
|
|
if (canBoost == false && state.boostCapacity >= props.minBoostCapacity)
|
|
{
|
|
canBoost = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logic which sets the isTackled state.
|
|
/// </summary>
|
|
/// <param name="gotTackled">Use true to process a tackle hit</param>
|
|
void UpdateTackleResponse(bool gotTackled = false)
|
|
{
|
|
if (gotTackled && !isTackled)
|
|
{
|
|
isTackled = true;
|
|
tackledTime = isCriticalTackle ? props.tackleCriticalStunTime :
|
|
props.tackleBodyStunTime;
|
|
return;
|
|
}
|
|
tackledTime -= Time.deltaTime;
|
|
if (tackledTime <= 0)
|
|
{
|
|
isTackled = false;
|
|
isCriticalTackle = false;
|
|
tackledTime = 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the collision regions which detect tackling.
|
|
/// Adds resulting forces to the ship and intiates the tackle
|
|
/// response.
|
|
/// </summary>
|
|
/// <param name="tackleKind">Kind of the tackle. Depends on collision region.</param>
|
|
/// <param name="collider">Object which has collided with the collision region.</param>
|
|
void StartTackleResponse(TackleKind tackleKind, Collider collider)
|
|
{
|
|
float tacklePowerFactor = props.criticalTacklePowerFactor;
|
|
if (tackleKind == TackleKind.Critical)
|
|
{
|
|
isCriticalTackle = true;
|
|
}
|
|
else
|
|
{
|
|
isCriticalTackle = false;
|
|
tacklePowerFactor = props.normalTacklePowerFactor;
|
|
}
|
|
Vector3 colliderVelocity = collider.attachedRigidbody.velocity;
|
|
Vector3 tackleDirection = transform.position - collider.transform.position;
|
|
tackleDirection = Vector3.ProjectOnPlane(tackleDirection, Vector3.Cross(transform.up, transform.right));
|
|
body.AddForce(colliderVelocity.magnitude * tackleDirection * tacklePowerFactor,
|
|
ForceMode.Acceleration);
|
|
UpdateTackleResponse(true);
|
|
}
|
|
|
|
}
|