318 lines
9.5 KiB
C#
318 lines
9.5 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;
|
|
|
|
void Awake()
|
|
{
|
|
if (forceManager == null)
|
|
{
|
|
forceManager = GameObject.FindGameObjectWithTag("ForceManager").
|
|
GetComponent<AffectingForcesManager>();
|
|
}
|
|
body = GetComponent<Rigidbody>();
|
|
}
|
|
|
|
// 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()
|
|
{
|
|
|
|
//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);
|
|
}
|
|
|
|
}
|