boost rework wip

This commit is contained in:
ntr committed 2023-08-15 14:35:49 +10:00
1 parent 46d742dfa8
commit fddf8552c2
3 files changed
+286 -340

No files matched your search

+50 -70
View File
@@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine; using UnityEngine;
public enum Output { public enum Output {
@@ -9,6 +11,10 @@ public enum Output {
} }
public class PowerUnit { public class PowerUnit {
static float stablisation = 75f;
public float Stabilisation => stablisation;
// Outputs
static float baseOutput = 25.0f; static float baseOutput = 25.0f;
static float initialAcceleration = 2f * baseOutput; static float initialAcceleration = 2f * baseOutput;
static float initialDeceleration = 4f * baseOutput; static float initialDeceleration = 4f * baseOutput;
@@ -18,94 +24,71 @@ public class PowerUnit {
float deceleration = initialDeceleration; float deceleration = initialDeceleration;
float strafing = initialStrafing; float strafing = initialStrafing;
// public float Acceleration => acceleration;
// public float Deceleration => deceleration;
// public float Strafing => strafing;
// Shields
static float initialShieldMax = 5000f; static float initialShieldMax = 5000f;
float shieldCurrent = initialShieldMax; float shieldCurrent = initialShieldMax;
float shieldMax = initialShieldMax; float shieldMax = initialShieldMax;
float shieldRegen = 10f; // * 100 ticks per second = 5s fully recharge float shieldRegen = 10f; // * 100 ticks per second = 5s fully recharge
int boostMax = 2;
int boostCharges = 0;
static float stablisation = 75f;
public float Acceleration => acceleration;
public float Deceleration => deceleration;
public float Strafing => strafing;
public float Shield => shieldCurrent; public float Shield => shieldCurrent;
public float ShieldMax => shieldMax; public float ShieldMax => shieldMax;
public float Stabilisation => stablisation;
public string ShieldPct => $"{((shieldCurrent / shieldMax) * 100f):0}"; public string ShieldPct => $"{((shieldCurrent / shieldMax) * 100f):0}";
public string BoostsAvailable => $"{boostCharges}"; float ShieldRegen => (shieldMax / 5f) * Time.fixedDeltaTime;
public bool BoostRecharging => boostCharges < boostMax;
public void Init() { // Boost
boostCharges = 0; List<(Output output, double expiresAt)> boostActive = new();
static double boostFullChargeSeconds = 10f;
static double boostDurationSeconds = 2f;
double boostMax = 2f * boostDurationSeconds;
double boostAvailable = 0f;
// public string BoostAvailable => $"{boostAvailable}";
public string BoostAvailable => $"{((boostAvailable / boostMax) * 100f):0}";
public float Acceleration =>
acceleration + boostActive.Count(b => b.output == Output.Acceleration) * baseOutput;
public float Deceleration =>
deceleration + boostActive.Count(b => b.output == Output.Deceleration) * baseOutput;
public float Strafing =>
strafing + boostActive.Count(b => b.output == Output.Strafing) * baseOutput;
double BoostRegen => (boostMax / boostFullChargeSeconds) * Time.fixedDeltaTime;
public void Reset() {
shieldCurrent = initialShieldMax; shieldCurrent = initialShieldMax;
acceleration = initialAcceleration; acceleration = initialAcceleration;
deceleration = initialDeceleration; deceleration = initialDeceleration;
strafing = initialStrafing; strafing = initialStrafing;
boostActive = new();
} }
// Regeneration of boosts is tracked by the ShipControls public void Deploy(Output output) {
// it has to handle tracking the coroutines etc boostActive.Add((output, Time.timeAsDouble + boostDurationSeconds));
public void BoostGenerate() {
boostCharges = Math.Min(boostCharges + 1, boostMax);
Debug.Log($"Boosts available {boostCharges}");
} }
public bool BoostDeploy(Output output) { public void Tick() {
if (boostCharges < 1) // remove expired boosts
return false; boostActive = boostActive.Where(b => b.expiresAt > Time.timeAsDouble).ToList();
float newOutput = output switch { double delta = boostActive.Count switch {
Output.Acceleration => BoostDeployAcceleration(), // if there are no boosts active we can increase the store
Output.Deceleration => BoostDeployDeceleration(), 0 => BoostRegen,
Output.Strafing => BoostDeployStrafing(), // if there are active boosts we deplete it
Output.Shield => BoostDeployShields(), int count => count * Time.fixedDeltaTime,
_ => 0f,
}; };
// Boosts are immediately used and tracked here double next = Math.Min(boostAvailable + delta, boostMax);
boostCharges--; boostAvailable = Math.Max(0f, next);
Debug.Log($"Set {output} to {newOutput}"); shieldMax = initialShieldMax + boostActive.Count(b => b.output == Output.Shield) * 1000f;
shieldCurrent = Math.Min(shieldCurrent + ShieldRegen, shieldMax);
return true;
}
float BoostDeployAcceleration() => acceleration += baseOutput;
float BoostDeployDeceleration() => deceleration += baseOutput;
float BoostDeployStrafing() => strafing += baseOutput;
float BoostDeployShields() {
float shieldFrac = shieldMax / shieldCurrent;
shieldMax = 7000f;
shieldCurrent = shieldMax * shieldFrac;
ShieldRegenRecalculate();
return shieldMax;
}
public void BoostEnd(Output output) {
float newOutput = output switch {
Output.Acceleration => BoostEndAcceleration(),
Output.Deceleration => BoostEndDeceleration(),
Output.Strafing => BoostEndStrafing(),
Output.Shield => BoostEndShields(),
_ => 0f,
};
Debug.Log($"Set {output} to {newOutput}");
}
float BoostEndAcceleration() => acceleration -= baseOutput;
float BoostEndDeceleration() => deceleration -= baseOutput;
float BoostEndStrafing() => strafing -= baseOutput;
float BoostEndShields() {
float shieldFrac = shieldMax / shieldCurrent;
shieldMax = 5000f;
shieldCurrent = shieldMax * shieldFrac;
ShieldRegenRecalculate();
return shieldMax;
} }
// returns whether shield depleted // returns whether shield depleted
@@ -113,7 +96,4 @@ public class PowerUnit {
shieldCurrent = shieldCurrent - amt; shieldCurrent = shieldCurrent - amt;
return shieldCurrent < 0f; return shieldCurrent < 0f;
} }
public void ShieldRegenerate() => shieldCurrent = Math.Min(shieldCurrent + shieldRegen, shieldMax);
void ShieldRegenRecalculate() => shieldRegen = (shieldMax / 5f) * Time.fixedDeltaTime;
} }
+235 -269
View File
@@ -1,270 +1,236 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
public class ShipControls : MonoBehaviour { public class ShipControls : MonoBehaviour {
public Camera aim; public Camera aim;
public Timing timing; public Timing timing;
SpaceRaceInput input; SpaceRaceInput input;
Rigidbody rigidBody; Rigidbody rigidBody;
Vector3 initialPosition; Vector3 initialPosition;
Vector3 initialForce; Vector3 initialForce;
Quaternion initialAim; Quaternion initialAim;
ForceMode forceMode; ForceMode forceMode;
Vector3 thrust; Vector3 thrust;
Vector3 strafe; Vector3 strafe;
// Camera Controls // Camera Controls
float camPitch; float camPitch;
float camYaw; float camYaw;
float clampAngle = 90.0f; float clampAngle = 90.0f;
public PowerUnit powerUnit = new(); public PowerUnit powerUnit = new();
IEnumerator boostDuration; int boostDurationSeconds = 2;
IEnumerator boostCooldown; int boostCooldownSeconds = 5;
int boostDurationSeconds = 2;
int boostCooldownSeconds = 5; void Start() {
Cursor.lockState = CursorLockMode.Locked;
void Start() { rigidBody = GetComponent<Rigidbody>();
Cursor.lockState = CursorLockMode.Locked;
rigidBody = GetComponent<Rigidbody>(); // just stop the body from being affected by rotation for now
// until the 3p camera is in to explain why
// just stop the body from being affected by rotation for now rigidBody.freezeRotation = true;
// until the 3p camera is in to explain why
rigidBody.freezeRotation = true; initialPosition = rigidBody.transform.position;
initialAim = rigidBody.transform.rotation;
initialPosition = rigidBody.transform.position; initialForce = Vector3.forward;
initialAim = rigidBody.transform.rotation;
initialForce = Vector3.forward; thrust = Vector3.zero;
strafe = Vector3.zero;
thrust = Vector3.zero;
strafe = Vector3.zero; camPitch = initialAim.eulerAngles.x;
camYaw = initialAim.eulerAngles.y;
camPitch = initialAim.eulerAngles.x; forceMode = ForceMode.Acceleration;
camYaw = initialAim.eulerAngles.y;
forceMode = ForceMode.Acceleration; SetupActions();
Respawn();
SetupActions(); }
Respawn();
} // to minimise input lag we poll for the thrust vector every frame and set it
// same with the camera, we always look around
// to minimise input lag we poll for the thrust vector every frame and set it void Update() {
// same with the camera, we always look around UpdateCamera();
void Update() { thrust = getThrust();
UpdateCamera(); }
thrust = getThrust();
} void FixedUpdate() {
// when you hit the trigger... GO
void FixedUpdate() { // needs to be done here in fixedUpdate because otherwise the input is checked on every frame
// when you hit the trigger... GO // so they will have already hit the button before this tick and they actually start moving
// needs to be done here in fixedUpdate because otherwise the input is checked on every frame // aka ripoff
// so they will have already hit the button before this tick and they actually start moving bool shouldBegin = !timing.timerEnabled && (thrust != Vector3.zero || strafe != Vector3.zero);
// aka ripoff if (shouldBegin) Begin();
bool shouldBegin = !timing.timerEnabled && (thrust != Vector3.zero || strafe != Vector3.zero);
if (shouldBegin) Begin(); // the ship being propelled forward
// this is in world space because of the xform from the camera pos
// the ship being propelled forward //
// this is in world space because of the xform from the camera pos rigidBody.AddForce(thrust, forceMode);
//
rigidBody.AddForce(thrust, forceMode); // moving side to side
// this is relative because it's easy to just multiply by the strafe vector
// moving side to side rigidBody.AddRelativeForce(strafe, forceMode);
// this is relative because it's easy to just multiply by the strafe vector
rigidBody.AddRelativeForce(strafe, forceMode); // rotating towards the camera's orientation
// not being done by a force because of strange behaviour
// rotating towards the camera's orientation // needs investigation
// not being done by a force because of strange behaviour float step = powerUnit.Stabilisation * Time.fixedDeltaTime;
// needs investigation
float step = powerUnit.Stabilisation * Time.fixedDeltaTime; // stop the ship jiggling because of micro camera movement
if (Quaternion.Angle(aim.transform.rotation, rigidBody.rotation) > 1f)
// stop the ship jiggling because of micro camera movement rigidBody.rotation = Quaternion.RotateTowards(transform.rotation, aim.transform.rotation, step);
if (Quaternion.Angle(aim.transform.rotation, rigidBody.rotation) > 1f)
rigidBody.rotation = Quaternion.RotateTowards(transform.rotation, aim.transform.rotation, step); if (timing.timerEnabled) {
powerUnit.Tick();
powerUnit.ShieldRegenerate(); }
} }
void UpdateCamera() { void UpdateCamera() {
// there's some hokey scaling that went on with the old input // there's some hokey scaling that went on with the old input
// we were originally using // we were originally using
// in the action mapping the mouse is scaled by 0.05 // in the action mapping the mouse is scaled by 0.05
// delta *= 0.5f; // Account for scaling applied directly in Windows code by old input system. // delta *= 0.5f; // Account for scaling applied directly in Windows code by old input system.
// delta *= 0.1f; // Account for sensitivity setting on old Mouse X and Y axes. // delta *= 0.1f; // Account for sensitivity setting on old Mouse X and Y axes.
// def needs to be configurable // def needs to be configurable
Vector2 lookDelta = input.ship.look.ReadValue<Vector2>(); Vector2 lookDelta = input.ship.look.ReadValue<Vector2>();
camYaw += lookDelta.x; camYaw += lookDelta.x;
camPitch -= lookDelta.y; camPitch -= lookDelta.y;
camPitch = Mathf.Clamp(camPitch, -clampAngle, clampAngle); camPitch = Mathf.Clamp(camPitch, -clampAngle, clampAngle);
aim.transform.eulerAngles = new Vector3(camPitch, camYaw, 0.0f); aim.transform.eulerAngles = new Vector3(camPitch, camYaw, 0.0f);
} }
// if you are decelerating you can't accelerate as well // if you are decelerating you can't accelerate as well
// otherwise you can flip the ship around and // otherwise you can flip the ship around and
// decelerate + accelerate against the velocity vector // decelerate + accelerate against the velocity vector
// aka cheating? // aka cheating?
Vector3 getThrust() { Vector3 getThrust() {
float accelerateInput = input.ship.accelerate.ReadValue<float>(); float accelerateInput = input.ship.accelerate.ReadValue<float>();
float decelerateInput = input.ship.decelerate.ReadValue<float>(); float decelerateInput = input.ship.decelerate.ReadValue<float>();
// decelerates the ship against the direction it is moving // decelerates the ship against the direction it is moving
if (decelerateInput > 0f) { if (decelerateInput > 0f) {
// the velocity is already in world space so we just invert it // the velocity is already in world space so we just invert it
return rigidBody.velocity.normalized * -1f * powerUnit.Deceleration * decelerateInput; return rigidBody.velocity.normalized * -1f * powerUnit.Deceleration * decelerateInput;
} }
// thrust in the direction of the camera // thrust in the direction of the camera
if (accelerateInput > 0f) if (accelerateInput > 0f)
// transform from the camera's forward vector into world space // transform from the camera's forward vector into world space
return aim.transform.TransformDirection(Vector3.forward) * powerUnit.Acceleration * accelerateInput; return aim.transform.TransformDirection(Vector3.forward) * powerUnit.Acceleration * accelerateInput;
return Vector3.zero; return Vector3.zero;
} }
public void Respawn() { public void Respawn() {
// IMPORTANT // IMPORTANT
// stop the timer before we move the ship or anything // stop the timer before we move the ship or anything
// otherwise it might generate a checkpoint time // otherwise it might generate a checkpoint time
// for the final sector // for the final sector
timing.StopTimer(); timing.StopTimer();
thrust = new Vector3(0.0f, 0.0f, 0.0f); thrust = new Vector3(0.0f, 0.0f, 0.0f);
transform.position = initialPosition; transform.position = initialPosition;
rigidBody.transform.forward = initialForce; rigidBody.transform.forward = initialForce;
rigidBody.velocity = new Vector3(0f, 0f, 0f); rigidBody.velocity = new Vector3(0f, 0f, 0f);
rigidBody.angularVelocity = new Vector3(0f, 0f, 0f); rigidBody.angularVelocity = new Vector3(0f, 0f, 0f);
rigidBody.transform.rotation = initialAim; rigidBody.transform.rotation = initialAim;
aim.transform.rotation = initialAim; aim.transform.rotation = initialAim;
camPitch = initialAim.eulerAngles.x; camPitch = initialAim.eulerAngles.x;
camYaw = initialAim.eulerAngles.y; camYaw = initialAim.eulerAngles.y;
if (boostCooldown != null) StopCoroutine(boostCooldown); powerUnit.Reset();
if (boostDuration != null) StopCoroutine(boostDuration); }
powerUnit.Init(); // stuff we have to do right as they come off the line
} void Begin() {
timing.StartTimer();
// stuff we have to do right as they come off the line }
void Begin() {
timing.StartTimer(); public float Speed() {
return rigidBody.velocity.magnitude;
// start with 0 cooldowns and regen them }
boostCooldown = BoostCooldown();
StartCoroutine(boostCooldown); public void Damage(float amt, string src) {
} Debug.Log($"Damage {amt} from {src}");
public float Speed() { bool destroyed = powerUnit.ShieldReduce(amt);
return rigidBody.velocity.magnitude; if (destroyed) {
} Debug.Log($"Ddddestroyed by {src}");
Respawn();
public void Damage(float amt, string src) { }
Debug.Log($"Damage {amt} from {src}"); }
bool destroyed = powerUnit.ShieldReduce(amt); void Deploy(Output output) {
if (destroyed) { powerUnit.Deploy(output);
Debug.Log($"Ddddestroyed by {src}"); }
Respawn();
} // I prefer doing it this way than registering the callbacks etc
} // the control of the actions is more granular
// plus the callbacks don't have to be public, bonus
void BoostDeploy(Output output) { void SetupActions() {
bool used = powerUnit.BoostDeploy(output); if (input == null)
if (!used) return; input = new SpaceRaceInput();
// stops the cooldown until after our boost finishes input.ship.Enable();
if (boostCooldown != null) StopCoroutine(boostCooldown);
boostDuration = BoostDuration(output); // we have to update strafe the whole time it's being pressed
StartCoroutine(boostDuration); // including setting it back to zero when released
} input.ship.strafe.started += OnStrafe;
input.ship.strafe.performed += OnStrafe;
IEnumerator BoostDuration(Output output) { input.ship.strafe.canceled += OnStrafe;
Debug.Log($"BoostDuration {output}...");
yield return new WaitForSeconds(boostDurationSeconds); // these we only care about the initial press
BoostEnd(output); input.ship.respawn.started += OnRespawn;
} input.ship.quit.started += OnQuit;
input.ship.powerUnit1.started += OnPowerUnit1;
void BoostEnd(Output output) { input.ship.powerUnit2.started += OnPowerUnit2;
Debug.Log($"BoostEnd {output}..."); input.ship.powerUnit3.started += OnPowerUnit3;
powerUnit.BoostEnd(output); input.ship.powerUnit4.started += OnPowerUnit4;
}
if (boostCooldown != null) StopCoroutine(boostCooldown);
boostCooldown = BoostCooldown(); void OnStrafe(InputAction.CallbackContext ctx) {
StartCoroutine(boostCooldown); Vector2 str = ctx.ReadValue<Vector2>();
} strafe = new Vector3(str.x, str.y, 0f) * powerUnit.Strafing;
}
IEnumerator BoostCooldown() {
while (powerUnit.BoostRecharging) { void OnQuit(InputAction.CallbackContext ctx) => Application.Quit();
yield return new WaitForSeconds(boostCooldownSeconds); void OnRespawn(InputAction.CallbackContext ctx) => Respawn();
powerUnit.BoostGenerate();
} void OnPowerUnit1(InputAction.CallbackContext ctx) => Deploy(Output.Acceleration);
} void OnPowerUnit2(InputAction.CallbackContext ctx) => Deploy(Output.Deceleration);
void OnPowerUnit3(InputAction.CallbackContext ctx) => Deploy(Output.Strafing);
// I prefer doing it this way than registering the callbacks etc void OnPowerUnit4(InputAction.CallbackContext ctx) => Deploy(Output.Shield);
// the control of the actions is more granular
// plus the callbacks don't have to be public, bonus void OnDrawGizmosSelected() {
void SetupActions() { if (Application.isPlaying == false)
if (input == null) return;
input = new SpaceRaceInput();
Gizmos.color = Color.red;
input.ship.Enable(); Vector3 right = aim.transform.TransformDirection(Vector3.right) * 10;
Gizmos.DrawRay(transform.position, right);
// we have to update strafe the whole time it's being pressed
// including setting it back to zero when released Gizmos.color = Color.green;
input.ship.strafe.started += OnStrafe; Vector3 direction = aim.transform.TransformDirection(Vector3.up) * 10;
input.ship.strafe.performed += OnStrafe; Gizmos.DrawRay(transform.position, direction);
input.ship.strafe.canceled += OnStrafe;
Gizmos.color = Color.blue;
// these we only care about the initial press Vector3 forward = aim.transform.TransformDirection(Vector3.forward) * 10;
input.ship.respawn.started += OnRespawn; Gizmos.DrawRay(transform.position, forward);
input.ship.quit.started += OnQuit;
input.ship.powerUnit1.started += OnPowerUnit1; Gizmos.color = Color.yellow;
input.ship.powerUnit2.started += OnPowerUnit2; Vector3 velocity = rigidBody.velocity;
input.ship.powerUnit3.started += OnPowerUnit3; Gizmos.DrawRay(transform.position, velocity);
input.ship.powerUnit4.started += OnPowerUnit4;
} Gizmos.color = Color.cyan;
Gizmos.DrawRay(transform.position, thrust);
void OnStrafe(InputAction.CallbackContext ctx) { }
Vector2 str = ctx.ReadValue<Vector2>();
strafe = new Vector3(str.x, str.y, 0f) * powerUnit.Strafing;
}
void OnQuit(InputAction.CallbackContext ctx) => Application.Quit();
void OnRespawn(InputAction.CallbackContext ctx) => Respawn();
void OnPowerUnit1 (InputAction.CallbackContext ctx) => BoostDeploy(Output.Acceleration);
void OnPowerUnit2 (InputAction.CallbackContext ctx) => BoostDeploy(Output.Deceleration);
void OnPowerUnit3 (InputAction.CallbackContext ctx) => BoostDeploy(Output.Strafing);
void OnPowerUnit4 (InputAction.CallbackContext ctx) => BoostDeploy(Output.Shield);
void OnDrawGizmosSelected() {
if (Application.isPlaying == false)
return;
Gizmos.color = Color.red;
Vector3 right = aim.transform.TransformDirection(Vector3.right) * 10;
Gizmos.DrawRay(transform.position, right);
Gizmos.color = Color.green;
Vector3 direction = aim.transform.TransformDirection(Vector3.up) * 10;
Gizmos.DrawRay(transform.position, direction);
Gizmos.color = Color.blue;
Vector3 forward = aim.transform.TransformDirection(Vector3.forward) * 10;
Gizmos.DrawRay(transform.position, forward);
Gizmos.color = Color.yellow;
Vector3 velocity = rigidBody.velocity;
Gizmos.DrawRay(transform.position, velocity);
Gizmos.color = Color.cyan;
Gizmos.DrawRay(transform.position, thrust);
}
} }
+1 -1
View File
@@ -45,7 +45,7 @@ public class ControlsUI : MonoBehaviour {
speed.text = $"{ship.Speed():0}"; speed.text = $"{ship.Speed():0}";
shields.text = ship.powerUnit.ShieldPct; shields.text = ship.powerUnit.ShieldPct;
boosts.text = ship.powerUnit.BoostsAvailable; boosts.text = ship.powerUnit.BoostAvailable;
puAccel.text = ship.powerUnit.Acceleration.ToString(); puAccel.text = ship.powerUnit.Acceleration.ToString();
puDecel.text = ship.powerUnit.Deceleration.ToString(); puDecel.text = ship.powerUnit.Deceleration.ToString();
puStrafe.text = ship.powerUnit.Strafing.ToString(); puStrafe.text = ship.powerUnit.Strafing.ToString();