Space-Smash-Out/Assets/Scripts/PlayerController.cs

297 lines
9.4 KiB
C#

using System;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using static AffectingForcesManager;
public class PlayerController : MonoBehaviour
{
[SerializeField] public int playerId = 1;
[SerializeField] public string playerName = "gray";
// Private variables
[SerializeField] private CameraOperator cameraOperator;
[SerializeField] private float thrustAcceleration = 400;
[SerializeField] private float steerVelocity = 30;
[SerializeField] private float normalMaxVelocity = 10;
[SerializeField] private float absolutMaxVelocity = 20;
[SerializeField] private float antiDriftAmount = 20;
[SerializeField] private float minAntiDriftFactor = 0.2f;
[SerializeField] private float normalDrag = 0.1f;
[SerializeField] private float maximumDrag = 0.3f;
[SerializeField] private float torqueDrag = 0.2f;
// The time in Seconds the player is allowed to boost for
[SerializeField] private float maxBoostCapacity = 2f;
[SerializeField] private float minBoostCapacity = 0.3f * 2f;
[SerializeField] private float boostMagnitude = 1.5f;
[SerializeField] private float outsideBoostRate = 0.5f;
[SerializeField, Range(0, 1)] private float boostAntiGravityFactor = 0.2f;
[SerializeField] private float stunLooseControlFactor = 0.1f;
[SerializeField] private float tackleCriticalStunTime = 0.6f;
[SerializeField] private float tackleBodyStunTime = 0.3f;
[SerializeField, Range(0, 300)] private float criticalTacklePowerFactor = 10f;
[SerializeField, Range(0, 300)] private float normalTacklePowerFactor = 10f;
[SerializeField] private BoostCapacityUI boostUI;
private AffectingForcesManager forceManager;
public PlayerInput playerInput;
private Rigidbody body;
private Vector3 currentGravity = new Vector3();
// Saves the current input value for thrust
private float currentThrustInput = 0;
private float currentBoostInput = 0;
private float boostCapacity;
// Saves the current input for steering
private float currentSteerInput = 0;
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;
public int instanceID;
void Awake()
{
if (forceManager == null)
{
forceManager = GameObject.FindGameObjectWithTag("ForceManager").GetComponent<AffectingForcesManager>();
}
body = GetComponent<Rigidbody>();
playerInput = GetComponent<PlayerInput>();
}
// Start is called before the first frame update
void Start()
{
instanceID = gameObject.GetInstanceID();
boostCapacity = maxBoostCapacity;
boostUI.SetMinBoostRatio(minBoostCapacity / 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 (GameManager.GM.currentState == GameState.Starting)
{
return;
}
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();
// TODO Always full thrust when boosting?
BoostStateUpdate(Time.deltaTime);
UpdateTackleResponse(isCriticalTackle);
}
void UpdateMovement()
{
//Debug.Log("inupdatemove " + currentThrustInput);
// Player rotation is always possible and same speed
transform.Rotate(0, 0, -steerVelocity * currentSteerInput * Time.deltaTime);
// Get and apply the current Gravity
currentGravity = forceManager.GetGravityForInstance(instanceID)(transform.position);
body.AddForce(currentGravity, ForceMode.Acceleration);
float tackleFactor = isCriticalTackle ? stunLooseControlFactor : 1f;
Vector3 acceleration = thrustAcceleration * currentThrustInput * Time.deltaTime
* Vector3.up * tackleFactor;
Vector3 currentVelocity = body.velocity;
Vector3 boostedAcceleration = BoostAcceleration(acceleration, currentGravity);
if (!isCriticalTackle)
{
// Add drag
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 <= normalMaxVelocity || IsBoosting() || zone != Zone.NimbleZone)
{
body.AddRelativeForce(boostedAcceleration, ForceMode.Acceleration);
}
if (currentVelocity.magnitude >= absolutMaxVelocity)
{
body.velocity = body.velocity.normalized * absolutMaxVelocity;
}
}
// Default torque drag
body.AddTorque(body.angularVelocity * -torqueDrag, ForceMode.Acceleration);
Debug.DrawRay(transform.position, transform.up * currentVelocity.magnitude * 0.5f, Color.black);
}
Vector3 DriftDampeningAcceleration(Vector3 currentVelocity, Zone zone)
{
Vector3 antiDriftVelocity = new Vector3();
float antiDriftFactor = 1f;
// Cancel out inertia/drifting
if (zone == Zone.NimbleZone)
{
Vector3 rotation = transform.rotation.eulerAngles;
Vector3 driftVelocity = currentVelocity - Vector3.Project(currentVelocity, rotation);
antiDriftVelocity = Vector3.Reflect(-driftVelocity, transform.up) - driftVelocity;
antiDriftFactor = Mathf.InverseLerp(absolutMaxVelocity, normalMaxVelocity, currentVelocity.magnitude);
antiDriftFactor = Mathf.Max(antiDriftFactor, minAntiDriftFactor);
Debug.DrawRay(transform.position, driftVelocity.normalized * 5, Color.red);
}
Debug.DrawRay(transform.position, antiDriftVelocity.normalized * 5, Color.green);
return antiDriftVelocity * antiDriftAmount * antiDriftFactor;
}
Vector3 DragDecceleration(Vector3 currentVelocity, Zone zone)
{
Vector3 drag = new Vector3();
bool isBoosting = currentBoostInput > 0 && canBoost == true;
float minDragFactor = Mathf.InverseLerp(absolutMaxVelocity, normalMaxVelocity, currentVelocity.magnitude);
float normalDragFactor = Mathf.InverseLerp(normalMaxVelocity, 0, currentVelocity.magnitude);
if (!isBoosting && zone == Zone.NimbleZone)
{
drag -= currentVelocity.normalized * normalDrag;
}
if (currentVelocity.magnitude >= normalMaxVelocity && zone == Zone.NimbleZone)
{
drag -= currentVelocity.normalized * maximumDrag;
}
return drag;
}
bool IsBoosting()
{
return currentBoostInput > 0 && canBoost;
}
Vector3 BoostAcceleration(Vector3 acceleration, Vector3 currentGravity)
{
if (IsBoosting())
{
acceleration *= boostMagnitude;
acceleration -= currentGravity * boostAntiGravityFactor;
}
return acceleration;
}
void BoostStateUpdate(float deltaTime)
{
boostUI.UpdateFill(Math.Min(boostCapacity / maxBoostCapacity, 1));
if (IsBoosting() && currentThrustInput != 0)
{
// Debug.Log("Boost Kapazität wird verbraucht: " + boostCapacity);
boostCapacity -= deltaTime;
}
if (canBoost && zone == Zone.OutsideZone)
{
// Debug.Log("Boost Kapazität wird passiv verbraucht: " + boostCapacity);
boostCapacity -= deltaTime * outsideBoostRate;
}
if (boostCapacity <= 0)
{
canBoost = false;
// Debug.Log("Boost aufgebraucht");
}
// TODO this will be spam abused
if ((currentBoostInput <= 0 || currentThrustInput == 0 || !canBoost)
&& zone == Zone.NimbleZone
&& boostCapacity <= maxBoostCapacity)
{
// Debug.Log("Boost wird aufgeladen: " + boostCapacity);
boostCapacity += deltaTime;
}
if (canBoost == false && boostCapacity >= minBoostCapacity)
{
canBoost = true;
// Debug.Log("Boost verfügbar");
}
}
void UpdateTackleResponse(bool gotTackled = false)
{
if (gotTackled && !isTackled)
{
isTackled = true;
tackledTime = isCriticalTackle ? tackleCriticalStunTime : tackleBodyStunTime;
return;
}
tackledTime -= Time.deltaTime;
if (tackledTime <= 0)
{
isTackled = false;
isCriticalTackle = false;
tackledTime = 0;
}
}
public void OnThrust(InputAction.CallbackContext context)
{
currentThrustInput = context.ReadValue<float>();
}
public void OnSteer(InputAction.CallbackContext context)
{
currentSteerInput = context.ReadValue<float>();
}
public void OnBoost(InputAction.CallbackContext context)
{
currentBoostInput = context.ReadValue<float>();
}
public void OnResetScene(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
Debug.Log("reload triggered");
Destroy(GameManager.GM);
string currentSceneName = SceneManager.GetActiveScene().name;
SceneManager.LoadScene(currentSceneName);
}
}
void StartTackleResponse(TackleKind tackleKind, Collider collider)
{
float tacklePowerFactor = criticalTacklePowerFactor;
if (tackleKind == TackleKind.Critical)
{
isCriticalTackle = true;
}
else
{
isCriticalTackle = false;
tacklePowerFactor = normalTacklePowerFactor;
}
Vector3 colliderVelocity = collider.attachedRigidbody.velocity;
Vector3 tackleDirection = body.transform.position - collider.transform.position;
body.AddForce(colliderVelocity.magnitude * tackleDirection * tacklePowerFactor, ForceMode.Acceleration);
UpdateTackleResponse(true);
}
}