Space-Smash-Out/Assets/Scripts/Ship.cs
Jakob Feldmann 64162cb4a1 feat: whole project restructuring
This can be seen as the initial state of the project after the released demo.

The changes include:
- New ship models
- Singleton manager structure to keep project scaleable in the future
     - Managing players, their settings, character choices, statistics, match setups, controls etc. in a separate decoupled scene
- Main menu with transitions to the arena scene
- Beginnings of a custom audio solution
- Logging with Log4Net

It is really a complete overhaul of the projects structure and management.
2024-04-01 23:06:39 +02:00

280 lines
8.0 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);
}
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);
}
Vector3 DriftDampeningAcceleration(Vector3 currentVelocity, Zone zone)
{
// TODO: add in ifs for ignoring small deviations
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;
}
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;
}
bool IsBoosting()
{
return state.boostInput > 0 && canBoost;
}
Vector3 BoostAcceleration(Vector3 acceleration, Vector3 currentGravity)
{
if (IsBoosting())
{
acceleration *= props.boostMagnitude;
acceleration -= currentGravity * props.boostAntiGravityFactor;
}
return acceleration;
}
void BoostStateUpdate(float deltaTime)
{
boostUI.UpdateFill(Math.Min(state.boostCapacity / props.maxBoostCapacity, 1));
if (IsBoosting() && state.thrustInput != 0)
{
// Debug.Log("Boost Kapazität wird verbraucht: " + boostCapacity);
state.boostCapacity -= deltaTime;
}
if (canBoost && zone == Zone.OutsideZone)
{
// Debug.Log("Boost Kapazität wird passiv verbraucht: " + boostCapacity);
state.boostCapacity -= deltaTime * props.outsideBoostRate;
}
if (state.boostCapacity <= 0)
{
canBoost = false;
// Debug.Log("Boost aufgebraucht");
}
// TODO this will be spam abused
if ((state.boostInput <= 0 || state.thrustInput == 0 || !canBoost)
&& zone == Zone.NimbleZone
&& state.boostCapacity <= props.maxBoostCapacity)
{
// Debug.Log("Boost wird aufgeladen: " + boostCapacity);
state.boostCapacity += deltaTime;
}
if (canBoost == false && state.boostCapacity >= props.minBoostCapacity)
{
canBoost = true;
// Debug.Log("Boost verfügbar");
}
}
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;
}
}
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);
}
}