using System; using Unity.Mathematics; using UnityEditor.ShaderGraph.Internal; 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(); } body = GetComponent(); playerInput = GetComponent(); } // 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(); 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 stunFactor = isCriticalTackle ? stunLooseControlFactor : 1f; currentThrustInput = IsBoosting() ? 1f : currentThrustInput; Vector3 acceleration = thrustAcceleration * currentThrustInput * Time.deltaTime * Vector3.up * stunFactor; Vector3 currentVelocity = body.velocity; Vector3 boostedAcceleration = BoostAcceleration(acceleration, 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 <= normalMaxVelocity || IsBoosting() || zone != Zone.NimbleZone) { body.AddRelativeForce(boostedAcceleration, ForceMode.Acceleration); } if (currentVelocity.magnitude >= absolutMaxVelocity && zone == Zone.NimbleZone) { 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; float antiDriftFactor; // Cancel out inertia/drifting 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(); } public void OnSteer(InputAction.CallbackContext context) { currentSteerInput = context.ReadValue(); } public void OnBoost(InputAction.CallbackContext context) { currentBoostInput = context.ReadValue(); } 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); } }