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(); } body = GetComponent(); } // 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(); 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); } }