doc: Added and improved comments + project readme

This commit is contained in:
Jakob Feldmann 2024-04-03 16:48:59 +02:00
parent 64162cb4a1
commit cbfd16941c
15 changed files with 232 additions and 54 deletions

View File

@ -20,7 +20,9 @@ public class CameraOperator : MonoBehaviour
players.Remove(id);
}
// Update is called once per frame
/// <summary>
/// Rotates the camera to point at the center between the players.
/// </summary>
void LateUpdate()
{
if (players.Count < 1)

View File

@ -30,8 +30,12 @@ namespace Managers
void Start()
{
centralAudioSource = audioLibrary.audios.First(a => a.tag == "music").audioSource;
centralAudioSource.Play();
if(audioLibrary == null){
return;
}
centralAudioSource = audioLibrary.audios.First(a => a.tag == "music")?.audioSource;
if(centralAudioSource != null)
centralAudioSource.Play();
}
}

View File

@ -23,8 +23,6 @@ public enum Scenes
namespace Managers
{
// public enum GameState { Starting, Match, End, Paused }
/// <summary>
/// Starts up the game and it's managers.
/// Provides methods for loading additional scenes and triggering custom events when loaded.
@ -88,7 +86,7 @@ namespace Managers
}
/// <summary>
/// Instantiates the managers needed to use the main menu.
/// Instantiates the managers needed to play the game.
/// </summary>
void InstantiateBaseManagers()
{
@ -148,6 +146,9 @@ namespace Managers
}
}
/// <summary>
/// Starts the camera, for showing an image before the main menu is loaded.
/// </summary>
void ShowStartScreen()
{
startCamera.SetActive(true);
@ -169,8 +170,8 @@ namespace Managers
}
/// <summary>
/// Initiates a local battle by giving relavant information to the match manager
/// and starting the match managers appropriate process.
/// Initiates a local battle by giving relavant information to the responsible managers
/// and starting their appropriate processes.
/// </summary>
/// <param name="sceneId">Scene id of the chosen arena.</param>
public IEnumerator SetupLocalMatchFromMainMenu()
@ -246,6 +247,10 @@ namespace Managers
}
/// <summary>
/// Custom delegate which can be executed after scene load.
/// </summary>
/// <param name="args">Data of the loaded scene</param>
public delegate void CustomOnSceneLoaded(SceneLoadEventArgs args);
public class SceneLoadEventArgs : EventArgs

View File

@ -7,9 +7,6 @@ using PrimeTween;
using UnityEditor;
using UnityEngine;
using GameLogic;
using Unity.VisualScripting;
using System.Xml;
using System.Collections;
using System.Threading.Tasks;
namespace Managers
@ -24,7 +21,6 @@ namespace Managers
private static string arenaAssetsPath = "Assets/ScriptedAssets/Arenas";
private static string ruleAssetsPath = "Assets/ScriptedAssets/Rules";
private bool needsStartConfirmation = false;
public event Action OnStartPressed;
/// <summary>
/// Globally accessible member to use manager with.
@ -132,6 +128,9 @@ namespace Managers
}
}
/// <summary>
/// Creates statistics for the players participating in the match.
/// </summary>
public void SetupMatchPlayerStatistics()
{
matchPlayerStatistics.Clear();
@ -190,6 +189,11 @@ namespace Managers
matchState = MatchState.CharacterSelect;
}
/// <summary>
/// Announcement of who won.
/// TODO: Also restarts the match right now, no matter what.
/// </summary>
/// <param name="mr">Result data of the completed match.</param>
async public void AnnounceWinner(MatchResult mr)
{
UIManager.G.announcments.QueueAnnounceText($"{mr.Winner.playerName}" +
@ -205,6 +209,10 @@ namespace Managers
RestartMatch();
}
/// <summary>
/// Starts the match anew.
/// Resetting player positions and statistics.
/// </summary>
public void RestartMatch()
{
ResetMatchCharacters();
@ -212,6 +220,10 @@ namespace Managers
matchState = MatchState.Match;
}
/// <summary>
/// Initializes the match, waits for match begin
/// confirmation by the players and counts down to start.
/// </summary>
async public void StartMatch()
{
foreach (Player p in matchPlayers)

View File

@ -1,16 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Managers;
using TMPro;
using UnityEditor;
using UnityEngine.InputSystem;
namespace GameLogic
{
/// <summary>
/// Updates and checks a matches rules according to the state of the match.
/// </summary>
public static class MatchLogic
{
public static MatchRule currentRule;
/// <summary>
/// Update match conditions and check for match conclusion.
/// </summary>
/// <param name="p">Affected player</param>
/// <param name="args">Update to the matches conditions</param>
/// <param name="mps">Dictionary of the players in the match and their current stats</param>
/// <returns></returns>
public static MatchResult UpdateMatchResult(Player p, MatchConditionUpdate args, Dictionary<Player, MatchPlayerStatistic> mps)
{
switch (args.Condition)

View File

@ -27,6 +27,7 @@ public class NimbleZoneDetection : MonoBehaviour
private Zone zone = Zone.NimbleZone;
// Ripple properties
// These influence the shader on the nimble zone
[SerializeField]
private float rippleFrequency = 3f;
[SerializeField]
@ -39,10 +40,10 @@ public class NimbleZoneDetection : MonoBehaviour
private float rippleDuration = 1f;
[SerializeField]
private float impactVelocityModifier = 1f;
[SerializeField]
[SerializeField, Tooltip("Minimum ripple effect intensity.")]
[Range(0, 1)]
private float minImpact = 0.2f;
[SerializeField]
[SerializeField, Tooltip("Velocity which makes the highest/most intense ripples.")]
private float maxVelocity = 45f;
private int maxRippleAmount = 5;
private MeshRenderer meshRenderer;
@ -55,19 +56,37 @@ public class NimbleZoneDetection : MonoBehaviour
ResetRippleShaderProperties();
}
/// <summary>
/// Array of the available gravities.
/// </summary>
private Func<Transform, Vector3>[] gravityFunctions =
{ DownGravity, NoGravity, InwardsGravity, OutwardsGravity };
/// <summary>
/// Function which returns a gravity zero vector.
/// </summary>
private static Func<Transform, Vector3> NoGravity =
new(transform => new Vector3());
/// <summary>
/// Function which returns a gravity vector downwards, depending
/// on the parent transforms rotation.
/// The parenting transform for a ship is the arena it's in.
/// </summary>
private static Func<Transform, Vector3> DownGravity =
new(transform => transform.parent.rotation * Vector3.down * gravityFactor);
// TODO: This is too specific maybe?
/// <summary>
/// Function which returns a gravity vector towards the center of the parenting transform.
/// The parenting transform for a ship is the arena it's in.
/// </summary>
private static Func<Transform, Vector3> InwardsGravity =
new(transform => (transform.position - transform.parent.position).normalized * -gravityFactor);
/// <summary>
/// Function which returns a gravity vector outwards from the center of the parenting transform.
/// The parenting transform for a ship is the arena it's in.
/// </summary>
private static Func<Transform, Vector3> OutwardsGravity =
new Func<Transform, Vector3>(transform =>
(transform.position - transform.parent.position).normalized * gravityFactor);
@ -113,12 +132,24 @@ public class NimbleZoneDetection : MonoBehaviour
material.SetFloat("_ShaderTime", Time.timeSinceLevelLoad);
}
private float ImpactVelocityEffect(float duration, float velocity)
/// <summary>
/// Calculates the effect which a given velocity has on a ripple property.
/// </summary>
/// <param name="duration">Initial value of the ripple property</param>
/// <param name="velocity">Velocity of the impact</param>
/// <returns></returns>
private float ImpactVelocityEffect(float initial, float velocity)
{
return math.max(math.smoothstep(0, maxVelocity, velocity), minImpact)
* impactVelocityModifier * duration;
* impactVelocityModifier * initial;
}
/// <summary>
/// Spawns ripples on the shader of the nimble zone.
/// Up to 5 parallel ripples.
/// </summary>
/// <param name="collider"></param>
/// <param name="isOutwardsRipple"></param>
private void SpawnRipple(Collider collider, bool isOutwardsRipple)
{
Rigidbody body = collider.attachedRigidbody;
@ -174,6 +205,9 @@ public class NimbleZoneDetection : MonoBehaviour
}
}
/// <summary>
/// Resets the ripple shaders exposed properties to 0
/// </summary>
private void ResetRippleShaderProperties()
{
Vector4[] rippleOrigins = new Vector4[maxRippleAmount];

View File

@ -6,6 +6,11 @@ using UnityEngine;
public class PlayingFieldDetection : MonoBehaviour
{
private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Updates the match conditions, when a ship leaves the playing field.
/// </summary>
/// <param name="collider"></param>
private void OnTriggerExit(Collider collider)
{
if (collider.tag == "Ship")

View File

@ -87,6 +87,9 @@ public class Ship : MonoBehaviour
UpdateTackleResponse(isCriticalTackle);
}
/// <summary>
/// Movement logic and simulation of the ship.
/// </summary>
void UpdateMovement()
{
@ -144,9 +147,14 @@ public class Ship : MonoBehaviour
Color.black);
}
/// <summary>
/// Calculates a vector to mitigate the ship drifting when it's changing direction.
/// </summary>
/// <param name="currentVelocity">Current velocity of the ship</param>
/// <param name="zone">Zone which the ship is in</param>
/// <returns></returns>
Vector3 DriftDampeningAcceleration(Vector3 currentVelocity, Zone zone)
{
// TODO: add in ifs for ignoring small deviations
Vector3 antiDriftVelocity;
float antiDriftFactor;
// Cancel out inertia/drifting
@ -170,6 +178,12 @@ public class Ship : MonoBehaviour
return antiDriftVelocity * props.antiDriftAmount * antiDriftFactor;
}
/// <summary>
/// Calculates drag on the ship depending on it's velocity and inhabited zone.
/// </summary>
/// <param name="currentVelocity">Velocity of the ship</param>
/// <param name="zone">Zone which the ship is in</param>
/// <returns></returns>
Vector3 DragDecceleration(Vector3 currentVelocity, Zone zone)
{
Vector3 drag = new Vector3();
@ -190,11 +204,23 @@ public class Ship : MonoBehaviour
return drag;
}
/// <summary>
/// Is the boost input pressed and boosting possible?
/// </summary>
/// <returns>Boosting state</returns>
bool IsBoosting()
{
return state.boostInput > 0 && canBoost;
}
/// <summary>
/// Applies boost to an acceleration vector.
/// This includes increasing acceleration and mitigating
/// the gravity.
/// </summary>
/// <param name="acceleration">Current acceleration vector</param>
/// <param name="currentGravity">Gravity vector which is in force</param>
/// <returns></returns>
Vector3 BoostAcceleration(Vector3 acceleration, Vector3 currentGravity)
{
if (IsBoosting())
@ -205,39 +231,44 @@ public class Ship : MonoBehaviour
return acceleration;
}
/// <summary>
/// Logic which depletes boost capacity when boost conditions are met.
/// </summary>
/// <param name="deltaTime">Time delta of the current frame</param>
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;
}
// When your boost capacity is still critical, you can't start boosting immediately again.
// TODO: This is not tested well enough with players.
if (canBoost == false && state.boostCapacity >= props.minBoostCapacity)
{
canBoost = true;
// Debug.Log("Boost verfügbar");
}
}
/// <summary>
/// Logic which sets the isTackled state.
/// </summary>
/// <param name="gotTackled">Use true to process a tackle hit</param>
void UpdateTackleResponse(bool gotTackled = false)
{
if (gotTackled && !isTackled)
@ -256,6 +287,13 @@ public class Ship : MonoBehaviour
}
}
/// <summary>
/// Called by the collision regions which detect tackling.
/// Adds resulting forces to the ship and intiates the tackle
/// response.
/// </summary>
/// <param name="tackleKind">Kind of the tackle. Depends on collision region.</param>
/// <param name="collider">Object which has collided with the collision region.</param>
void StartTackleResponse(TackleKind tackleKind, Collider collider)
{
float tacklePowerFactor = props.criticalTacklePowerFactor;

View File

@ -2,6 +2,9 @@ using UnityEngine;
namespace ShipHandling
{
/// <summary>
/// Variables for the ship which can also be under the players control.
/// </summary>
public class ShipState
{
public float thrustInput = 0;

View File

@ -1,10 +1,18 @@
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Used on vulnerable trigger zones for ships.
/// </summary>
public class TackleDetection : MonoBehaviour
{
[SerializeField] private TackleKind tackleKind;
public UnityEvent<TackleKind, Collider> TackleResponse;
/// <summary>
/// Invokes the fitting tackle response on trigger entered.
/// </summary>
/// <param name="collider"></param>
void OnTriggerEnter(Collider collider)
{
if (collider.tag != "Spike" && collider.tag != "Bumper")

View File

@ -1,10 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
/// <summary>
/// Provides methods for displaying text on a text mesh pro.
/// Features a queue for messages which dissapear after a set time.
/// </summary>
public class Announcments : MonoBehaviour
{
[SerializeField] TextMeshProUGUI announcementText;
@ -12,7 +14,10 @@ public class Announcments : MonoBehaviour
public Queue<Tuple<string, float>> announcementQueue = new Queue<Tuple<string, float>>();
private bool workingOnQueue = false;
private float remainingTime;
/// <summary>
/// Update the message queue.
/// </summary>
void Update()
{
if (!workingOnQueue && announcementQueue.Count != 0)
@ -38,20 +43,10 @@ public class Announcments : MonoBehaviour
}
}
public void AnnounceText(string text, float time)
{
announcementText.text = text;
announcementText.enabled = true;
remainingTime = time;
enabled = true;
}
public void QueueAnnounceText(string text, float time)
{
announcementQueue.Enqueue(new Tuple<string, float>(text, time));
enabled = true;
}
/// <summary>
/// Shows the text without time limit.
/// </summary>
/// <param name="text">Text to be shown.</param>
public void AnnounceText(string text)
{
announcementText.text = text;
@ -60,6 +55,33 @@ public class Announcments : MonoBehaviour
enabled = false;
}
/// <summary>
/// Set the announcement text for a certain time.
/// </summary>
/// <param name="text"></param>
/// <param name="time">Time in seconds</param>
private void AnnounceText(string text, float time)
{
announcementText.text = text;
announcementText.enabled = true;
remainingTime = time;
enabled = true;
}
/// <summary>
/// Add an announcement to the queue.
/// </summary>
/// <param name="text">Text to be shown.</param>
/// <param name="time">Time of the announcement in seconds.</param>
public void QueueAnnounceText(string text, float time)
{
announcementQueue.Enqueue(new Tuple<string, float>(text, time));
enabled = true;
}
/// <summary>
/// Stop the announcement and clear the queue.
/// </summary>
public void StopAnnouncement()
{
announcementQueue.Clear();

View File

@ -1,8 +1,10 @@
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// HUD element which displays a ships remaining boost capacity.
/// </summary>
public class BoostCapacityUI : MonoBehaviour
{
public Color goodColor = Color.green;
@ -12,6 +14,11 @@ public class BoostCapacityUI : MonoBehaviour
[SerializeField] private TextMeshProUGUI hint;
private float minBoostRatio = 0.3f;
/// <summary>
/// Ratio of capacity to max capacity under which the
/// boost capacity is considered critically low.
/// </summary>
/// <param name="minBoostRatio"></param>
public void SetMinBoostRatio(float minBoostRatio)
{
this.minBoostRatio = minBoostRatio;
@ -22,6 +29,10 @@ public class BoostCapacityUI : MonoBehaviour
hint.SetText($"Boost Capacity \n {p.playerName} \n {p.character.shipName}");
}
/// <summary>
/// Updates the color and fill of the capacity meter.
/// </summary>
/// <param name="fill">Fill percentage</param>
public void UpdateFill(float fill)
{
fillImage.fillAmount = fill;

View File

@ -1,13 +1,17 @@
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
/// <summary>
/// Colleciton class for referencing and controlling HUD elements.
/// </summary>
public class HUD : MonoBehaviour
{
public BoostCapacityUI[] boostCapacities;
public JoinPrompt[] joinPrompts;
/// <summary>
/// Start a join prompt.
/// </summary>
/// <param name="player">Player to which the prompt belongs</param>
public void StartJoinPrompt(Player player)
{
foreach (JoinPrompt jp in joinPrompts)

View File

@ -89,10 +89,6 @@ namespace SlimUI.ModernMenu
[Tooltip("The GameObject holding the Audio Source component for the SWOOSH SOUND when switching to the Settings Screen")]
public AudioSource swooshSound;
// [Header("Options")]
// public bool flyCameraLoadTransition = true;
void Start()
{
GM = GameManager.G;
@ -212,11 +208,11 @@ namespace SlimUI.ModernMenu
}
/// <summary>
/// Loads the Gameplay UI and unloads the Main Menu once thats loaded.
/// Loads the Gameplay UI and after that unloads the Main Menu.
/// Also Initiates the setup of the local match once the Gameplay UI is
/// loaded.
/// </summary>
/// <returns></returns>
/// <returns>IEnumerator for async statemachine</returns>
private IEnumerator SwitchToIngameUI()
{
AsyncOperation o = SceneManager.LoadSceneAsync((int)Scenes.GameplayUserInterface,

26
README.md Normal file
View File

@ -0,0 +1,26 @@
When using this repo a game can be started from the Unity Editor,
by starting the GameManagement scene from the Assets/Scenes folder.
The GameManager is a sort of bootloader, which sets up all the
other managers and orchestrates the match start process.
Once the main menu is loaded, the only real option is to start a
local 2v2 match.
I use SlimUI, as the main menu for now, it is a store asset.
Starting this match (using the button in SlimUI) sets off the async scene loading,
transition to the arena camera and match setup in the GameManager.
The GameManager provides a method which invokes an event once a
specific scene has been loaded.
Here I'm using that to trigger the camera transition once the arena scene was loaded.
After the camera transition the InGameUI scene is loaded and when the UI is there,
the match setup process in the GameManager is started.
To look into this process see:
Assets\SlimUI\Modern Menu 1\Scripts\Managers\SlimUIMainMenu Line 162 onwards
Assets\Scripts\Managers\GameManager Line 176 onwards
Also noteworthy is the Ship Movement logic in which I change the flying behavior and gravity
for the ship depending which zone it is in
and the shader for the ripple effect of the inner/green/nimble zone, which is an older simple
ripple shader converted to URP (in code not shader graph), featuring up to 5 simulateously animated ripples.