diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..ddb6ff85a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "visualstudiotoolsforunity.vstuc" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..da60e25ae --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,10 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Unity", + "type": "vstuc", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..9c57597a4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "files.exclude": { + "**/.DS_Store": true, + "**/.git": true, + "**/.vs": true, + "**/.gitmodules": true, + "**/.vsconfig": true, + "**/*.booproj": true, + "**/*.pidb": true, + "**/*.suo": true, + "**/*.user": true, + "**/*.userprefs": true, + "**/*.unityproj": true, + "**/*.dll": true, + "**/*.exe": true, + "**/*.pdf": true, + "**/*.mid": true, + "**/*.midi": true, + "**/*.wav": true, + "**/*.gif": true, + "**/*.ico": true, + "**/*.jpg": true, + "**/*.jpeg": true, + "**/*.png": true, + "**/*.psd": true, + "**/*.tga": true, + "**/*.tif": true, + "**/*.tiff": true, + "**/*.3ds": true, + "**/*.3DS": true, + "**/*.fbx": true, + "**/*.FBX": true, + "**/*.lxo": true, + "**/*.LXO": true, + "**/*.ma": true, + "**/*.MA": true, + "**/*.obj": true, + "**/*.OBJ": true, + "**/*.asset": true, + "**/*.cubemap": true, + "**/*.flare": true, + "**/*.mat": true, + "**/*.meta": true, + "**/*.prefab": true, + "**/*.unity": true, + "build/": true, + "Build/": true, + "Library/": true, + "library/": true, + "obj/": true, + "Obj/": true, + "Logs/": true, + "logs/": true, + "ProjectSettings/": true, + "UserSettings/": true, + "temp/": true, + "Temp/": true + }, + "dotnet.defaultSolution": "ECS-Network-Racing-Sample.sln" +} \ No newline at end of file diff --git a/Assets/Fonts/Rajdhani/Rajdhani-Medium SDF.asset b/Assets/Fonts/Rajdhani/Rajdhani-Medium SDF.asset index 209254bdd..ee432cbd1 100644 --- a/Assets/Fonts/Rajdhani/Rajdhani-Medium SDF.asset +++ b/Assets/Fonts/Rajdhani/Rajdhani-Medium SDF.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8431a52f3c1cfd1b99429bda6cd85f5c856a6eb07c3500936138be359d243d3 -size 2128699 +oid sha256:08aa1d2e006e0c0a67450a41863daf57feeb60506690e493788ee3a5d900b49b +size 2127255 diff --git a/Assets/New Terrain.asset b/Assets/New Terrain.asset new file mode 100644 index 000000000..25090c96d --- /dev/null +++ b/Assets/New Terrain.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a30e65224c6ef755703667bf1c9b5fe627634cecdabeda716b9737201cf0985 +size 557360 diff --git a/Assets/New Terrain.asset.meta b/Assets/New Terrain.asset.meta new file mode 100644 index 000000000..d0c7e5f63 --- /dev/null +++ b/Assets/New Terrain.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f146b8ae4b04bb7478e4e1f65a76972d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 15600000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Authoring/Car/VehicleAuthoring.cs b/Assets/Scripts/Authoring/Car/VehicleAuthoring.cs index 520d0181d..cf7fecd8d 100644 --- a/Assets/Scripts/Authoring/Car/VehicleAuthoring.cs +++ b/Assets/Scripts/Authoring/Car/VehicleAuthoring.cs @@ -27,6 +27,7 @@ public class VehicleAuthoring : MonoBehaviour [Header("Wheels")] public float WheelsRadius; public CollisionCategories WheelsCollisionMask; + public float MaxSafeVelocity = 50f; // Maximum safe velocity for wheel raycast public WheelAuthoringHelper[] Wheels; [Header("Engine Sound")] public float MinAudioVolume = 0.4f; @@ -160,6 +161,7 @@ protected override void OnUpdate() GripFactor = wheelAuthoring.GripFactor, Placement = wheelAuthoring.Placement, VisualMesh = visualMesh, + MaxSafeVelocity = vehicleAuthoring.MaxSafeVelocity, }; var wheelEntity = i switch diff --git a/Assets/Scripts/Components/Wheel.cs b/Assets/Scripts/Components/Wheel.cs index 455a7e8b0..3821f2b50 100644 --- a/Assets/Scripts/Components/Wheel.cs +++ b/Assets/Scripts/Components/Wheel.cs @@ -23,6 +23,7 @@ public struct Wheel : IComponentData [GhostField(Quantization = 10000)] public float DriveForce; [GhostField(Quantization = 10000)] public float SidewaysForce; public BlobAssetReference DriveTorqueCurve; + public float MaxSafeVelocity; // Maximum safe velocity for wheel raycast, above which raycast is skipped public void Reset() { diff --git a/Assets/Scripts/Gameplay/Connection/ServerConnectionUtils.cs b/Assets/Scripts/Gameplay/Connection/ServerConnectionUtils.cs index babdf7010..b61f6abb0 100644 --- a/Assets/Scripts/Gameplay/Connection/ServerConnectionUtils.cs +++ b/Assets/Scripts/Gameplay/Connection/ServerConnectionUtils.cs @@ -13,6 +13,7 @@ public class ServerConnectionUtils private static World m_Server; private static World m_Client; public static bool IsTryingToConnect = false; + public static string LastConnectionError = string.Empty; /// /// Start a Client and Server in your local IP @@ -20,9 +21,17 @@ public class ServerConnectionUtils public static void StartClientServer(string port) { IsTryingToConnect = true; + LastConnectionError = string.Empty; + + RaceLogger.LogSection("NETWORK CONNECTION"); + RaceLogger.Network("Starting Client/Server mode"); + if (ClientServerBootstrap.RequestedPlayType != ClientServerBootstrap.PlayType.ClientAndServer) { - Debug.LogError($"Creating client/server worlds is not allowed if playmode is set to {ClientServerBootstrap.RequestedPlayType}"); + LastConnectionError = $"Creating client/server worlds is not allowed if playmode is set to {ClientServerBootstrap.RequestedPlayType}"; + Debug.LogError(LastConnectionError); + RaceLogger.Error(LastConnectionError); + IsTryingToConnect = false; return; } @@ -32,28 +41,43 @@ public static void StartClientServer(string port) if (m_Client != null && m_Client.IsCreated) m_Client.Dispose(); + RaceLogger.Network("Creating server and client worlds"); m_Server = ClientServerBootstrap.CreateServerWorld("ServerWorld"); m_Client = ClientServerBootstrap.CreateClientWorld("ClientWorld"); - //Destroy the local simulation world to avoid the game scene to be loaded into it - //This prevent rendering (rendering from multiple world with presentation is not greatly supported) - //and other issues. - DestroyLocalSimulationWorld(); + // Destroy the local simulation world safely + SafeDestroyLocalSimulationWorld(); World.DefaultGameObjectInjectionWorld ??= m_Server; + RaceLogger.Network("Loading main scene"); SceneLoader.Instance.LoadScene(SceneType.Main); - var networkEndpoint = NetworkEndpoint.AnyIpv4.WithPort(ParsePortOrDefault(port)); + try { - using var drvQuery = m_Server.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); - drvQuery.GetSingletonRW().ValueRW.Listen(networkEndpoint); - } + var parsedPort = ParsePortOrDefault(port); + RaceLogger.Network($"Starting server on port {parsedPort}"); + var networkEndpoint = NetworkEndpoint.AnyIpv4.WithPort(parsedPort); + { + using var drvQuery = m_Server.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); + drvQuery.GetSingletonRW().ValueRW.Listen(networkEndpoint); + } - networkEndpoint = NetworkEndpoint.LoopbackIpv4.WithPort(ParsePortOrDefault(port)); + RaceLogger.Network($"Connecting client to loopback:{parsedPort}"); + networkEndpoint = NetworkEndpoint.LoopbackIpv4.WithPort(parsedPort); + { + using var drvQuery = m_Client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); + drvQuery.GetSingletonRW().ValueRW.Connect(m_Client.EntityManager, networkEndpoint); + } + + RaceLogger.Success("Client/Server setup completed successfully"); + } + catch (System.Exception ex) { - using var drvQuery = m_Client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); - drvQuery.GetSingletonRW().ValueRW.Connect(m_Client.EntityManager, networkEndpoint); + LastConnectionError = $"Failed to start client/server: {ex.Message}"; + Debug.LogError(LastConnectionError); + RaceLogger.Error(LastConnectionError); + IsTryingToConnect = false; } } @@ -65,18 +89,49 @@ public static void StartClientServer(string port) public static void ConnectToServer(string ip, string port) { IsTryingToConnect = true; + LastConnectionError = string.Empty; + + RaceLogger.LogSection("NETWORK CONNECTION"); + RaceLogger.Network($"Connecting to server at {ip}:{port}"); + + if (!ValidateIPv4(ip)) + { + LastConnectionError = $"Invalid IP address format: {ip}"; + Debug.LogError(LastConnectionError); + RaceLogger.Error(LastConnectionError); + IsTryingToConnect = false; + return; + } + + RaceLogger.Network("Creating client world"); var client = ClientServerBootstrap.CreateClientWorld("ClientWorld"); - DestroyLocalSimulationWorld(); + // Destroy the local simulation world safely + SafeDestroyLocalSimulationWorld(); World.DefaultGameObjectInjectionWorld ??= client; + RaceLogger.Network("Loading main scene"); SceneLoader.Instance.LoadScene(SceneType.Main); - var networkEndpoint = NetworkEndpoint.Parse(ip, ParsePortOrDefault(port)); + try { - using var drvQuery = client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); - drvQuery.GetSingletonRW().ValueRW.Connect(client.EntityManager, networkEndpoint); + var parsedPort = ParsePortOrDefault(port); + RaceLogger.Network($"Connecting to {ip}:{parsedPort}"); + var networkEndpoint = NetworkEndpoint.Parse(ip, parsedPort); + { + using var drvQuery = client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); + drvQuery.GetSingletonRW().ValueRW.Connect(client.EntityManager, networkEndpoint); + } + + RaceLogger.Success("Client connection initiated successfully"); + } + catch (System.Exception ex) + { + LastConnectionError = $"Failed to connect to server: {ex.Message}"; + Debug.LogError(LastConnectionError); + RaceLogger.Error(LastConnectionError); + IsTryingToConnect = false; } } @@ -118,16 +173,26 @@ private static ushort ParsePortOrDefault(string s) return port; } - private static void DestroyLocalSimulationWorld() + /// + /// Safely destroys the local simulation world if it exists + /// + private static void SafeDestroyLocalSimulationWorld() { - foreach (var world in World.All) + try { - if (world.Flags == WorldFlags.Game) + foreach (var world in World.All) { - world.Dispose(); - break; + if (world.Flags == WorldFlags.Game && world.IsCreated) + { + world.Dispose(); + break; + } } } + catch (System.Exception ex) + { + Debug.LogWarning($"Error disposing local simulation world: {ex.Message}"); + } } } } \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug.meta b/Assets/Scripts/Gameplay/Debug.meta new file mode 100644 index 000000000..7529812f2 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46b7b94f96787ff498d674791fb9e106 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Gameplay/Debug/README.md b/Assets/Scripts/Gameplay/Debug/README.md new file mode 100644 index 000000000..7e70da0d7 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/README.md @@ -0,0 +1,110 @@ +# Race Debug Logging System + +A comprehensive debug and logging system for the ECS Network Racing Sample that provides: + +- Color-coded console logs for different types of events +- Tracking of game state transitions +- Logging of player actions +- Network event logging +- Easy integration with existing systems + +## 🚀 Getting Started + +1. Add the `RaceLoggerSetup` component to a GameObject in your main scene +2. Create a `RaceLoggerSettings` asset by right-clicking in the Project view and selecting `Create > Racing > Debug > Logger Settings` +3. Assign the settings asset to the `RaceLoggerSetup` component + +## 📝 Log Types + +The logging system supports different types of logs with distinct colors: + +- **Info (Cyan)**: General information messages +- **Success (Green)**: Successful operations and achievements +- **Warning (Yellow)**: Potential issues or noteworthy events +- **Error (Red)**: Error conditions +- **Gameplay (Magenta)**: Game state and gameplay events +- **Network (Blue)**: Networking and connection events +- **Physics (Teal)**: Physics-related events + +## 🧰 Usage Examples + +### Basic Logging + +```csharp +// Log an informational message (cyan) +RaceLogger.Info("Game initialized"); + +// Log a success message (green) +RaceLogger.Success("Player connected successfully"); + +// Log a warning (yellow) +RaceLogger.Warning("Performance may be affected"); + +// Log an error (red) +RaceLogger.Error("Failed to load asset"); + +// Log a gameplay event (magenta) +RaceLogger.Gameplay("Player entered finish line"); + +// Log a network event (blue) +RaceLogger.Network("Connected to server: 192.168.1.1"); + +// Log a physics event (teal) +RaceLogger.Physics("Collision detected"); +``` + +### Log Sections + +To group related logs, you can create labeled sections: + +```csharp +RaceLogger.LogSection("RACE STARTED"); +``` + +### Custom Colored Logs + +You can use custom colors by specifying a hex color: + +```csharp +RaceLogger.CustomColor("Special event occurred", "#FF00FF"); +``` + +## 🔧 Configuration + +The `RaceLoggerSettings` asset allows you to configure: + +- Enable/disable all logging +- Control which categories of logs are shown +- Configure advanced features like stack traces for errors + +## 📚 Architecture + +### Core Components + +1. **RaceLogger**: Static utility class with methods for different types of logs +2. **RaceLoggerManager**: Singleton manager that initializes the logging system +3. **RaceLoggerSettings**: ScriptableObject for configuration +4. **RaceStateLoggerSystem**: ECS system that tracks race state changes +5. **RaceLoggerSetup**: MonoBehaviour for easy setup in scenes + +### Integration Points + +The logging system integrates with: + +- **Race State System**: Logs state transitions during races +- **Car Selection UI**: Logs player car selections +- **ServerConnectionUtils**: Logs network connections and errors + +## 📊 Additional Features + +- Automatic initialization logging with system info +- Detailed state transition tracking +- Network error logging + +## ⚙️ Customization + +You can extend the system by: + +1. Adding new log types to `RaceLogger` +2. Creating new integration points with other systems +3. Modifying the `RaceLoggerSettings` to include additional options \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/README.md.meta b/Assets/Scripts/Gameplay/Debug/README.md.meta new file mode 100644 index 000000000..44821a2d8 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a904faf19fef83e498ec60806640494b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Gameplay/Debug/RaceLogger.cs b/Assets/Scripts/Gameplay/Debug/RaceLogger.cs new file mode 100644 index 000000000..425e930a7 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLogger.cs @@ -0,0 +1,114 @@ +using System; +using UnityEngine; + +namespace Unity.Entities.Racing.Gameplay +{ + /// + /// Provides debug logging with color-coded outputs for game events + /// + public static class RaceLogger + { + private const string PREFIX = "[RACE]"; + + // Log colors (using Unity's rich text format) + private const string COLOR_INFO = "cyan"; + private const string COLOR_SUCCESS = "green"; + private const string COLOR_WARNING = "yellow"; + private const string COLOR_ERROR = "red"; + private const string COLOR_GAMEPLAY = "magenta"; + private const string COLOR_NETWORK = "blue"; + private const string COLOR_PHYSICS = "teal"; + + private static bool s_LoggingEnabled = true; + + /// + /// Enable or disable all logging + /// + public static bool LoggingEnabled + { + get => s_LoggingEnabled; + set => s_LoggingEnabled = value; + } + + /// + /// Logs a regular informational message (cyan) + /// + public static void Info(string message) + { + if (!s_LoggingEnabled) return; + Debug.Log($"{PREFIX} {message}"); + } + + /// + /// Logs a success message (green) + /// + public static void Success(string message) + { + if (!s_LoggingEnabled) return; + Debug.Log($"{PREFIX} {message}"); + } + + /// + /// Logs a warning message (yellow) + /// + public static void Warning(string message) + { + if (!s_LoggingEnabled) return; + Debug.LogWarning($"{PREFIX} {message}"); + } + + /// + /// Logs an error message (red) + /// + public static void Error(string message) + { + if (!s_LoggingEnabled) return; + Debug.LogError($"{PREFIX} {message}"); + } + + /// + /// Logs a gameplay event (magenta) + /// + public static void Gameplay(string message) + { + if (!s_LoggingEnabled) return; + Debug.Log($"{PREFIX} {message}"); + } + + /// + /// Logs a network event (blue) + /// + public static void Network(string message) + { + if (!s_LoggingEnabled) return; + Debug.Log($"{PREFIX} {message}"); + } + + /// + /// Logs a physics event (teal) + /// + public static void Physics(string message) + { + if (!s_LoggingEnabled) return; + Debug.Log($"{PREFIX} {message}"); + } + + /// + /// Logs a formatted message with the specified color + /// + public static void CustomColor(string message, string hexColor) + { + if (!s_LoggingEnabled) return; + Debug.Log($"{PREFIX} {message}"); + } + + /// + /// Creates a labeled section in the log to group related messages + /// + public static void LogSection(string sectionName) + { + if (!s_LoggingEnabled) return; + Debug.Log($"{PREFIX} ========== {sectionName} =========="); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceLogger.cs.meta b/Assets/Scripts/Gameplay/Debug/RaceLogger.cs.meta new file mode 100644 index 000000000..d615c4dd7 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLogger.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ad53532f15be35f46b732d015e2664ce \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceLoggerManager.cs b/Assets/Scripts/Gameplay/Debug/RaceLoggerManager.cs new file mode 100644 index 000000000..2398eb4cd --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLoggerManager.cs @@ -0,0 +1,126 @@ +using UnityEngine; +using Unity.Entities.Racing.Common; + +namespace Unity.Entities.Racing.Gameplay +{ + /// + /// Manages the debug logging system + /// + public class RaceLoggerManager : MonoBehaviour + { + public static RaceLoggerManager Instance { get; private set; } + + [SerializeField] private RaceLoggerSettings settings; + + [Tooltip("Print startup log when the game starts")] + [SerializeField] private bool printStartupLog = true; + + private void Awake() + { + if (Instance != null && Instance != this) + { + Destroy(gameObject); + return; + } + + Instance = this; + DontDestroyOnLoad(gameObject); + + InitializeLogger(); + } + + private void InitializeLogger() + { + if (settings != null) + { + settings.ApplySettings(); + } + + if (printStartupLog) + { + LogStartupInfo(); + } + } + + private void LogStartupInfo() + { + RaceLogger.LogSection("RACE GAME STARTUP"); + RaceLogger.Info($"Game Version: {Application.version}"); + RaceLogger.Info($"Unity Version: {Application.unityVersion}"); + RaceLogger.Info($"Platform: {Application.platform}"); + RaceLogger.Info($"System Language: {Application.systemLanguage}"); + RaceLogger.LogSection("INITIALIZATION"); + } + + /// + /// Log a state change in the race + /// + public void LogRaceStateChange(RaceState previousState, RaceState newState) + { + if (settings == null || !settings.logRaceState) return; + + switch (newState) + { + case RaceState.None: + RaceLogger.Info($"Race State: {previousState} → None"); + break; + case RaceState.NotStarted: + RaceLogger.Info($"Race State: {previousState} → Not Started"); + break; + case RaceState.ReadyToRace: + RaceLogger.Gameplay($"Race State: {previousState} → Ready To Race"); + break; + case RaceState.StartingRace: + RaceLogger.Gameplay($"Race State: {previousState} → Starting Race"); + break; + case RaceState.CountDown: + RaceLogger.LogSection("RACE COUNTDOWN"); + RaceLogger.Gameplay($"Race State: {previousState} → Count Down"); + break; + case RaceState.InProgress: + RaceLogger.LogSection("RACE STARTED"); + RaceLogger.Success($"Race State: {previousState} → In Progress"); + break; + case RaceState.Finishing: + RaceLogger.Gameplay($"Race State: {previousState} → Finishing"); + break; + case RaceState.Finished: + RaceLogger.Success($"Race State: {previousState} → Finished"); + break; + case RaceState.Leaderboard: + RaceLogger.LogSection("RACE FINISHED"); + RaceLogger.Success($"Race State: {previousState} → Leaderboard"); + break; + case RaceState.StartingRaceAutomatically: + RaceLogger.Gameplay($"Race State: {previousState} → Starting Race Automatically"); + break; + default: + RaceLogger.Info($"Race State: {previousState} → {newState}"); + break; + } + } + + /// + /// Log a player action + /// + public void LogPlayerAction(string playerName, string action) + { + if (settings == null || !settings.logPlayerActions) return; + RaceLogger.Gameplay($"Player '{playerName}': {action}"); + } + + /// + /// Log a network event + /// + public void LogNetworkEvent(string eventName, string details = null) + { + if (settings == null || !settings.logNetwork) return; + + string message = string.IsNullOrEmpty(details) + ? $"Network: {eventName}" + : $"Network: {eventName} - {details}"; + + RaceLogger.Network(message); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceLoggerManager.cs.meta b/Assets/Scripts/Gameplay/Debug/RaceLoggerManager.cs.meta new file mode 100644 index 000000000..839136703 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLoggerManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 15d8b28e640b7594e95bab6d387b49ce \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceLoggerSettings.cs b/Assets/Scripts/Gameplay/Debug/RaceLoggerSettings.cs new file mode 100644 index 000000000..d855d2b35 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLoggerSettings.cs @@ -0,0 +1,57 @@ +using UnityEngine; + +namespace Unity.Entities.Racing.Gameplay +{ + /// + /// Scriptable object to configure the RaceLogger from the Unity Editor + /// + [CreateAssetMenu(fileName = "RaceLoggerSettings", menuName = "Racing/Debug/Logger Settings")] + public class RaceLoggerSettings : ScriptableObject + { + [Header("Logging Settings")] + [Tooltip("Enable or disable all logging")] + public bool loggingEnabled = true; + + [Header("Log Categories")] + [Tooltip("Log game initialization and startup events")] + public bool logInitialization = true; + + [Tooltip("Log network and connection events")] + public bool logNetwork = true; + + [Tooltip("Log player actions and inputs")] + public bool logPlayerActions = true; + + [Tooltip("Log race state changes")] + public bool logRaceState = true; + + [Tooltip("Log vehicle physics events")] + public bool logPhysics = false; + + [Tooltip("Log details about code performance")] + public bool logPerformance = false; + + [Header("Advanced Settings")] + [Tooltip("Log call stack for errors")] + public bool logStackTraceForErrors = true; + + [Tooltip("Automatically save logs to file")] + public bool saveLogsToFile = false; + + [Tooltip("Path for log files (relative to persistent data path)")] + public string logFilePath = "Logs"; + + private void OnEnable() + { + ApplySettings(); + } + + /// + /// Apply the settings to the RaceLogger + /// + public void ApplySettings() + { + RaceLogger.LoggingEnabled = loggingEnabled; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceLoggerSettings.cs.meta b/Assets/Scripts/Gameplay/Debug/RaceLoggerSettings.cs.meta new file mode 100644 index 000000000..55e899570 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLoggerSettings.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9216a4a88efffec4aab7be699104b60a \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceLoggerSetup.cs b/Assets/Scripts/Gameplay/Debug/RaceLoggerSetup.cs new file mode 100644 index 000000000..bbe58a20c --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLoggerSetup.cs @@ -0,0 +1,59 @@ +using UnityEngine; + +namespace Unity.Entities.Racing.Gameplay +{ + /// + /// Helper MonoBehaviour to create the RaceLogger prefab and setup in the scene. + /// Add this to a GameObject in your scene to enable logging. + /// + [DefaultExecutionOrder(-1000)] // Ensure this runs very early in initialization + public class RaceLoggerSetup : MonoBehaviour + { + [Header("Logger Settings Asset")] + [Tooltip("The settings for the race logger. Create one using right-click > Create > Racing > Debug > Logger Settings")] + [SerializeField] private RaceLoggerSettings loggerSettings; + + [Header("UI Settings")] + [Tooltip("Show a UI panel with the most recent log messages")] + [SerializeField] private bool showLogUI = false; + + [Tooltip("Maximum number of log entries to display in the UI")] + [SerializeField] private int maxLogEntries = 10; + + private RaceLoggerManager m_LoggerManager; + + private void Awake() + { + // Create the logger manager object + GameObject loggerManagerObj = new GameObject("RaceLoggerManager"); + m_LoggerManager = loggerManagerObj.AddComponent(); + + // Set the settings if available + if (loggerSettings != null) + { + var managerField = m_LoggerManager.GetType().GetField("settings", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (managerField != null) + { + managerField.SetValue(m_LoggerManager, loggerSettings); + } + } + + // Make sure it persists across scenes + DontDestroyOnLoad(loggerManagerObj); + + // Create the logger system in the default world + if (World.DefaultGameObjectInjectionWorld != null) + { + World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged(); + + // Log successful initialization + RaceLogger.LogSection("LOGGER INITIALIZED"); + RaceLogger.Success("Race Logger initialized successfully"); + RaceLogger.Info($"Game Version: {Application.version}"); + RaceLogger.Info($"Unity Version: {Application.unityVersion}"); + RaceLogger.Info($"Platform: {Application.platform}"); + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceLoggerSetup.cs.meta b/Assets/Scripts/Gameplay/Debug/RaceLoggerSetup.cs.meta new file mode 100644 index 000000000..4c67e7e7e --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceLoggerSetup.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6c3694e315c6ec249aeae02c950ec149 \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceStateLoggerSystem.cs b/Assets/Scripts/Gameplay/Debug/RaceStateLoggerSystem.cs new file mode 100644 index 000000000..97c2b3739 --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceStateLoggerSystem.cs @@ -0,0 +1,80 @@ +using Unity.Entities.Racing.Common; +using static Unity.Entities.SystemAPI; + +namespace Unity.Entities.Racing.Gameplay +{ + /// + /// System that monitors race state changes and logs them using the RaceLogger + /// + [UpdateInGroup(typeof(SimulationSystemGroup))] + public partial class RaceStateLoggerSystem : SystemBase + { + private RaceState m_PreviousState; + + protected override void OnCreate() + { + RequireForUpdate(); + m_PreviousState = RaceState.None; + } + + protected override void OnUpdate() + { + var race = GetSingleton(); + + // Only log when the state changes + if (race.State != m_PreviousState) + { + LogRaceStateChange(m_PreviousState, race.State); + m_PreviousState = race.State; + } + } + + private void LogRaceStateChange(RaceState previousState, RaceState newState) + { + // If the RaceLoggerManager is available, use it to log the state change + if (RaceLoggerManager.Instance != null) + { + RaceLoggerManager.Instance.LogRaceStateChange(previousState, newState); + return; + } + + // Otherwise, log directly + switch (newState) + { + case RaceState.None: + RaceLogger.Info($"Race State: {previousState} → None"); + break; + case RaceState.NotStarted: + RaceLogger.Info($"Race State: {previousState} → Not Started"); + break; + case RaceState.ReadyToRace: + RaceLogger.Gameplay($"Race State: {previousState} → Ready To Race"); + break; + case RaceState.StartingRace: + RaceLogger.Gameplay($"Race State: {previousState} → Starting Race"); + break; + case RaceState.CountDown: + RaceLogger.LogSection("RACE COUNTDOWN"); + RaceLogger.Gameplay($"Race State: {previousState} → Count Down"); + break; + case RaceState.InProgress: + RaceLogger.LogSection("RACE STARTED"); + RaceLogger.Success($"Race State: {previousState} → In Progress"); + break; + case RaceState.Finishing: + RaceLogger.Gameplay($"Race State: {previousState} → Finishing"); + break; + case RaceState.Finished: + RaceLogger.Success($"Race State: {previousState} → Finished"); + break; + case RaceState.Leaderboard: + RaceLogger.LogSection("RACE FINISHED"); + RaceLogger.Success($"Race State: {previousState} → Leaderboard"); + break; + case RaceState.StartingRaceAutomatically: + RaceLogger.Gameplay($"Race State: {previousState} → Starting Race Automatically"); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Debug/RaceStateLoggerSystem.cs.meta b/Assets/Scripts/Gameplay/Debug/RaceStateLoggerSystem.cs.meta new file mode 100644 index 000000000..d84379e9a --- /dev/null +++ b/Assets/Scripts/Gameplay/Debug/RaceStateLoggerSystem.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9672940f1e918864a9b738153ed2d004 \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/UI/CarSelection/CarSelectionUI.cs b/Assets/Scripts/Gameplay/UI/CarSelection/CarSelectionUI.cs index b4d7b076b..245f46093 100644 --- a/Assets/Scripts/Gameplay/UI/CarSelection/CarSelectionUI.cs +++ b/Assets/Scripts/Gameplay/UI/CarSelection/CarSelectionUI.cs @@ -23,6 +23,7 @@ public class CarSelectionUI : MonoBehaviour private bool m_InCarSelection; private VisualElement m_CurrentButton; private float m_SubmitWidth; + private int m_SelectedSkinIndex = -1; private void Awake() { @@ -45,6 +46,9 @@ private void OnEnable() m_StartButton = root.Q("car-selection-start-button"); m_StartButton.RegisterCallback(OnStartButtonClicked); SetSkinButtons(root); + + // Log that car selection UI is ready + RaceLogger.Info("Car Selection UI initialized"); } private void OnDisable() @@ -58,6 +62,13 @@ public void ShowCarSelection(bool value) { // Focus on the first Car Skin Button SkinButtons[0].Focus(); + RaceLogger.LogSection("CAR SELECTION"); + RaceLogger.Gameplay("Car selection screen opened"); + } + else if (m_InCarSelection) + { + // Only log when we're actually closing the screen, not when initially setting it to false + RaceLogger.Gameplay("Car selection screen closed"); } m_InCarSelection = value; @@ -69,22 +80,54 @@ private void SetSkinButtons(VisualElement root) SkinButtons = new List