diff --git a/Assets/DebugOverlay/CVar.cs b/Assets/DebugOverlay/CVar.cs
new file mode 100644
index 0000000..170821f
--- /dev/null
+++ b/Assets/DebugOverlay/CVar.cs
@@ -0,0 +1,126 @@
+using System;
+using UnityEngine;
+
+///
+/// A config variable that can be changed from the console.
+/// Reading the value is extremely fast (direct field access performance).
+/// Usage: public static readonly CVar<float> fov = new CVar<float>("fov", 90f, "Field of view");
+/// Access: float currentFov = CVars.fov; // Implicit conversion, zero overhead
+///
+[System.Serializable]
+public partial class CVar where T : IComparable, IConvertible
+{
+ [SerializeField] private T _value;
+ [SerializeField] private readonly T _defaultValue;
+ [SerializeField] private readonly string _name;
+ [SerializeField] private readonly string _description;
+
+ private readonly Func _validator;
+ private readonly Action _onChanged;
+
+ ///
+ /// Current value of the cvar. Ultra-fast access.
+ ///
+ public T Value
+ {
+ get => _value;
+ set => SetValue(value);
+ }
+
+ ///
+ /// Default value of the cvar
+ ///
+ public T DefaultValue => _defaultValue;
+
+ ///
+ /// Name of the cvar as it appears in console
+ ///
+ public string Name => _name;
+
+ ///
+ /// Description shown in help
+ ///
+ public string Description => _description;
+
+ ///
+ /// Implicit conversion for ultra-fast value access
+ ///
+ public static implicit operator T(CVar cvar) => cvar._value;
+
+ ///
+ /// Create a new config variable. Automatically registers with CVarSystem.
+ ///
+ /// Console name (e.g., "fov")
+ /// Default value
+ /// Help description
+ /// Optional validation function
+ /// Optional callback when value changes
+ public CVar(string name, T defaultValue, string description = "",
+ Func validator = null, Action onChanged = null)
+ {
+ _name = name?.ToLower() ?? throw new ArgumentNullException(nameof(name));
+ _defaultValue = defaultValue;
+ _description = description ?? "";
+ _value = defaultValue;
+ _validator = validator;
+ _onChanged = onChanged;
+
+ // Register with the system
+ CVarSystem.Register(this);
+ }
+
+ ///
+ /// Set the value with validation and change notification
+ ///
+ public bool SetValue(T newValue)
+ {
+ // Validate if validator exists
+ if (_validator != null && !_validator(newValue))
+ {
+ return false;
+ }
+
+ var oldValue = _value;
+ _value = newValue;
+
+ // Notify if value actually changed
+ if (!oldValue.Equals(newValue))
+ {
+ _onChanged?.Invoke(newValue);
+ }
+
+ return true;
+ }
+
+ ///
+ /// Reset to default value
+ ///
+ public void Reset()
+ {
+ SetValue(_defaultValue);
+ }
+
+ ///
+ /// Try to set value from string (used by console)
+ ///
+ public bool TrySetFromString(string valueStr)
+ {
+ try
+ {
+ T newValue = (T)Convert.ChangeType(valueStr, typeof(T));
+ return SetValue(newValue);
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Get current value as string
+ ///
+ public override string ToString()
+ {
+ return _value?.ToString() ?? "null";
+ }
+}
\ No newline at end of file
diff --git a/Assets/DebugOverlay/CVar.cs.meta b/Assets/DebugOverlay/CVar.cs.meta
new file mode 100644
index 0000000..1745cef
--- /dev/null
+++ b/Assets/DebugOverlay/CVar.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6a4b8c9d1e2f3a4b5c6d7e8f9a0b1c2d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
\ No newline at end of file
diff --git a/Assets/DebugOverlay/CVarSystem.cs b/Assets/DebugOverlay/CVarSystem.cs
new file mode 100644
index 0000000..f15bf93
--- /dev/null
+++ b/Assets/DebugOverlay/CVarSystem.cs
@@ -0,0 +1,356 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using UnityEngine;
+
+///
+/// Base interface for type-erased cvar access
+///
+public interface ICVar
+{
+ string Name { get; }
+ string Description { get; }
+ object Value { get; }
+ object DefaultValue { get; }
+ bool TrySetFromString(string value);
+ void Reset();
+ string ToString();
+}
+
+///
+/// Make CVar implement ICVar for type-erased access
+///
+public partial class CVar : ICVar where T : IComparable, IConvertible
+{
+ object ICVar.Value => _value;
+ object ICVar.DefaultValue => _defaultValue;
+}
+
+///
+/// System that manages all config variables and integrates with console
+///
+public static class CVarSystem
+{
+ private static readonly Dictionary s_CVars = new Dictionary();
+ private static bool s_Initialized = false;
+
+ ///
+ /// Initialize the cvar system (called by Game.Init)
+ ///
+ public static void Initialize(Console console)
+ {
+ if (s_Initialized)
+ return;
+
+ s_Initialized = true;
+
+ // Register console commands
+ console.AddCommand("cvarlist", CmdCVarList, "List all config variables");
+ console.AddCommand("cvarreset", CmdCVarReset, "Reset a cvar to default value");
+ console.AddCommand("cvarresetall", CmdCVarResetAll, "Reset all cvars to default values");
+ console.AddCommand("set", CmdSet, "Set a cvar value (set )");
+
+ // Add save/load commands
+ console.AddCommand("cvarsave", CmdCVarSave, "Save all cvars to config file");
+ console.AddCommand("cvarload", CmdCVarLoad, "Load cvars from config file");
+ console.AddCommand("cvarexec", CmdCVarExec, "Execute a config file");
+
+ Debug.Log($"CVarSystem initialized with {s_CVars.Count} variables");
+ }
+
+ ///
+ /// Register a cvar (called automatically by CVar constructor)
+ ///
+ public static void Register(ICVar cvar)
+ {
+ if (s_CVars.ContainsKey(cvar.Name))
+ {
+ Debug.LogWarning($"CVar '{cvar.Name}' is already registered!");
+ return;
+ }
+
+ s_CVars[cvar.Name] = cvar;
+
+ if (s_Initialized)
+ Debug.Log($"Registered cvar: {cvar.Name} = {cvar.Value}");
+ }
+
+ ///
+ /// Get a cvar by name
+ ///
+ public static ICVar GetCVar(string name)
+ {
+ s_CVars.TryGetValue(name.ToLower(), out ICVar cvar);
+ return cvar;
+ }
+
+ ///
+ /// Get all registered cvars
+ ///
+ public static IEnumerable GetAllCVars()
+ {
+ return s_CVars.Values;
+ }
+
+ ///
+ /// Try to set a cvar value from string
+ ///
+ public static bool TrySetCVar(string name, string value)
+ {
+ var cvar = GetCVar(name);
+ if (cvar == null)
+ return false;
+
+ return cvar.TrySetFromString(value);
+ }
+
+ ///
+ /// Handle console input that might be a cvar assignment (name = value)
+ /// Returns true if handled as cvar assignment
+ ///
+ public static bool TryHandleConsoleInput(string input, Console console)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ return false;
+
+ input = input.Trim();
+
+ // Handle "name = value" syntax
+ if (input.Contains("="))
+ {
+ var parts = input.Split('=');
+ if (parts.Length == 2)
+ {
+ string name = parts[0].Trim();
+ string value = parts[1].Trim();
+
+ var cvar = GetCVar(name);
+ if (cvar != null)
+ {
+ if (cvar.TrySetFromString(value))
+ {
+ console.Write("^0F0{0}^FFF = {1}\n", name, cvar.ToString());
+ return true;
+ }
+ else
+ {
+ console.Write("^F00Invalid value '{0}' for {1}\n", value, name);
+ return true;
+ }
+ }
+ }
+ }
+
+ // Handle just "name" to show current value
+ var showCvar = GetCVar(input);
+ if (showCvar != null)
+ {
+ console.Write("^0F0{0}^FFF = {1} (default: {2})\n",
+ showCvar.Name, showCvar.ToString(), showCvar.DefaultValue);
+ console.Write(" {0}\n", showCvar.Description);
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Save all cvar values to a config file
+ ///
+ public static void SaveConfig(string filePath = null)
+ {
+ if (string.IsNullOrEmpty(filePath))
+ filePath = Path.Combine(Application.persistentDataPath, "config.cfg");
+
+ try
+ {
+ using (var writer = new StreamWriter(filePath))
+ {
+ writer.WriteLine("// Generated config file");
+ writer.WriteLine("// Format: = ");
+ writer.WriteLine();
+
+ foreach (var cvar in s_CVars.Values)
+ {
+ writer.WriteLine($"{cvar.Name} = {cvar.ToString()}");
+ }
+ }
+
+ Debug.Log($"Saved {s_CVars.Count} cvars to: {filePath}");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"Failed to save config: {e.Message}");
+ }
+ }
+
+ ///
+ /// Load cvar values from a config file
+ ///
+ public static void LoadConfig(string filePath = null)
+ {
+ if (string.IsNullOrEmpty(filePath))
+ filePath = Path.Combine(Application.persistentDataPath, "config.cfg");
+
+ if (!File.Exists(filePath))
+ {
+ Debug.LogWarning($"Config file not found: {filePath}");
+ return;
+ }
+
+ try
+ {
+ int loadedCount = 0;
+ string[] lines = File.ReadAllLines(filePath);
+
+ foreach (string line in lines)
+ {
+ string trimmed = line.Trim();
+
+ // Skip comments and empty lines
+ if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith("//"))
+ continue;
+
+ // Parse cvar assignment (without console output during load)
+ if (trimmed.Contains("="))
+ {
+ var parts = trimmed.Split('=');
+ if (parts.Length == 2)
+ {
+ string name = parts[0].Trim();
+ string value = parts[1].Trim();
+
+ if (TrySetCVar(name, value))
+ loadedCount++;
+ }
+ }
+ }
+
+ Debug.Log($"Loaded {loadedCount} cvars from: {filePath}");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"Failed to load config: {e.Message}");
+ }
+ }
+
+ // Console command implementations
+ private static void CmdCVarList(string[] args)
+ {
+ var console = Game.console;
+ console.Write("^FF0Config Variables:\n");
+
+ foreach (var cvar in s_CVars.Values)
+ {
+ console.Write(" ^0F0{0,-20}^FFF = {1,-15} ^888(default: {2})\n",
+ cvar.Name, cvar.ToString(), cvar.DefaultValue.ToString());
+ if (!string.IsNullOrEmpty(cvar.Description))
+ console.Write(" ^AAA{0}\n", cvar.Description);
+ }
+
+ console.Write("^FF0Total: {0} variables\n", s_CVars.Count);
+ console.Write("^888Usage: = or just to show current value\n");
+ }
+
+ private static void CmdCVarReset(string[] args)
+ {
+ var console = Game.console;
+
+ if (args.Length != 1)
+ {
+ console.Write("Usage: cvarreset \n");
+ return;
+ }
+
+ var cvar = GetCVar(args[0]);
+ if (cvar == null)
+ {
+ console.Write("^F00Unknown cvar: {0}\n", args[0]);
+ return;
+ }
+
+ cvar.Reset();
+ console.Write("^0F0{0}^FFF reset to {1}\n", cvar.Name, cvar.ToString());
+ }
+
+ private static void CmdCVarResetAll(string[] args)
+ {
+ var console = Game.console;
+
+ int count = 0;
+ foreach (var cvar in s_CVars.Values)
+ {
+ cvar.Reset();
+ count++;
+ }
+
+ console.Write("^0F0Reset {0} cvars to default values\n", count);
+ }
+
+ private static void CmdSet(string[] args)
+ {
+ var console = Game.console;
+
+ if (args.Length != 2)
+ {
+ console.Write("Usage: set \n");
+ return;
+ }
+
+ string name = args[0];
+ string value = args[1];
+
+ var cvar = GetCVar(name);
+ if (cvar == null)
+ {
+ console.Write("^F00Unknown cvar: {0}\n", name);
+ return;
+ }
+
+ if (cvar.TrySetFromString(value))
+ {
+ console.Write("^0F0{0}^FFF = {1}\n", cvar.Name, cvar.ToString());
+ }
+ else
+ {
+ console.Write("^F00Invalid value '{0}' for {1}\n", value, name);
+ }
+ }
+
+ private static void CmdCVarSave(string[] args)
+ {
+ var console = Game.console;
+
+ string filePath = null;
+ if (args.Length > 0)
+ filePath = args[0];
+
+ SaveConfig(filePath);
+ console.Write("^0F0Config saved\n");
+ }
+
+ private static void CmdCVarLoad(string[] args)
+ {
+ var console = Game.console;
+
+ string filePath = null;
+ if (args.Length > 0)
+ filePath = args[0];
+
+ LoadConfig(filePath);
+ console.Write("^0F0Config loaded\n");
+ }
+
+ private static void CmdCVarExec(string[] args)
+ {
+ var console = Game.console;
+
+ if (args.Length != 1)
+ {
+ console.Write("Usage: cvarexec \n");
+ return;
+ }
+
+ LoadConfig(args[0]);
+ }
+}
\ No newline at end of file
diff --git a/Assets/DebugOverlay/CVarSystem.cs.meta b/Assets/DebugOverlay/CVarSystem.cs.meta
new file mode 100644
index 0000000..7af2ca4
--- /dev/null
+++ b/Assets/DebugOverlay/CVarSystem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7b5c9d0e2f3a4b5c6d7e8f9a0b1c2d3e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
\ No newline at end of file
diff --git a/Assets/DebugOverlay/CVars.cs b/Assets/DebugOverlay/CVars.cs
new file mode 100644
index 0000000..6199404
--- /dev/null
+++ b/Assets/DebugOverlay/CVars.cs
@@ -0,0 +1,116 @@
+using System;
+using UnityEngine;
+
+///
+/// Example config variables for common game settings.
+/// Add your own cvars here - they'll automatically be available in the console!
+///
+/// Usage examples:
+/// In console: "fov = 110" or "set fov 110"
+/// In code: float currentFov = CVars.fov; // Ultra-fast access
+///
+public static class CVars
+{
+ // === Graphics Settings ===
+ public static readonly CVar fov = new CVar(
+ "fov", 90, "Field of view in degrees",
+ validator: x => x >= 60 && x <= 150,
+ onChanged: x => Debug.Log($"FOV changed to {x}")
+ );
+
+ public static readonly CVar maxFps = new CVar(
+ "maxfps", 60, "Maximum framerate (0 = unlimited)",
+ validator: x => x >= 0 && x <= 300,
+ onChanged: x => Application.targetFrameRate = x == 0 ? -1 : x
+ );
+
+ public static readonly CVar vsync = new CVar(
+ "vsync", true, "Enable vertical sync",
+ onChanged: x => QualitySettings.vSyncCount = x ? 1 : 0
+ );
+
+ public static readonly CVar mouseSensitivity = new CVar(
+ "sensitivity", 1.0f, "Mouse sensitivity multiplier",
+ validator: x => x > 0.0f && x <= 10.0f
+ );
+
+ // === Audio Settings ===
+ public static readonly CVar masterVolume = new CVar(
+ "volume", 1.0f, "Master volume (0-1)",
+ validator: x => x >= 0.0f && x <= 1.0f,
+ onChanged: x => AudioListener.volume = x
+ );
+
+ public static readonly CVar muteAudio = new CVar(
+ "mute", false, "Mute all audio",
+ onChanged: x => AudioListener.pause = x
+ );
+
+ // === Debug Settings ===
+ public static readonly CVar showFps = new CVar(
+ "showfps", true, "Show FPS counter"
+ );
+
+ public static readonly CVar showStats = new CVar(
+ "showstats", false, "Show detailed performance stats"
+ );
+
+ public static readonly CVar drawWireframe = new CVar(
+ "wireframe", false, "Render in wireframe mode",
+ onChanged: x => Camera.main.GetComponent().renderingPath = x ? RenderingPath.Forward : RenderingPath.DeferredShading
+ );
+
+ // === Game Settings ===
+ public static readonly CVar playerName = new CVar(
+ "playername", "Player", "Player display name"
+ );
+
+ public static readonly CVar walkSpeed = new CVar(
+ "walkspeed", 5.0f, "Player walking speed",
+ validator: x => x > 0.0f && x <= 50.0f
+ );
+
+ public static readonly CVar jumpHeight = new CVar(
+ "jumpheight", 2.0f, "Player jump height",
+ validator: x => x >= 0.0f && x <= 10.0f
+ );
+
+ // === Console Settings ===
+ public static readonly CVar consoleSpeed = new CVar(
+ "consolespeed", 5.0f, "Console open/close animation speed",
+ validator: x => x > 0.0f && x <= 20.0f
+ );
+
+ public static readonly CVar consoleHeight = new CVar(
+ "consoleheight", 25, "Console height in characters",
+ validator: x => x >= 10 && x <= 50
+ );
+
+ // === Network Settings (if applicable) ===
+ public static readonly CVar serverAddress = new CVar(
+ "serveraddress", "localhost", "Server IP address"
+ );
+
+ public static readonly CVar serverPort = new CVar(
+ "serverport", 7777, "Server port",
+ validator: x => x > 0 && x <= 65535
+ );
+
+ public static readonly CVar networkRate = new CVar(
+ "rate", 20, "Network update rate (Hz)",
+ validator: x => x >= 1 && x <= 128
+ );
+
+ ///
+ /// Example of how to create a cvar with complex validation
+ ///
+ public static readonly CVar gameMode = new CVar(
+ "gamemode", "normal", "Current game mode",
+ validator: ValidateGameMode
+ );
+
+ private static bool ValidateGameMode(string mode)
+ {
+ return mode == "normal" || mode == "hardcore" || mode == "creative" || mode == "debug";
+ }
+}
\ No newline at end of file
diff --git a/Assets/DebugOverlay/CVars.cs.meta b/Assets/DebugOverlay/CVars.cs.meta
new file mode 100644
index 0000000..87d611e
--- /dev/null
+++ b/Assets/DebugOverlay/CVars.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8c6d0e1f3a4b5c6d7e8f9a0b1c2d3e4f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
\ No newline at end of file
diff --git a/Assets/DebugOverlay/Console.cs b/Assets/DebugOverlay/Console.cs
index 944080d..2d67f19 100644
--- a/Assets/DebugOverlay/Console.cs
+++ b/Assets/DebugOverlay/Console.cs
@@ -101,6 +101,9 @@ public void Init(DebugOverlay debugOverlay)
m_DebugOverlay = debugOverlay != null ? debugOverlay : DebugOverlay.instance;
Resize(m_DebugOverlay.width, m_DebugOverlay.height);
Clear();
+
+ // Initialize CVar system
+ CVarSystem.Initialize(this);
}
public void Shutdown()
@@ -238,11 +241,20 @@ void HistoryStore(string cmd)
void ExecuteCommand(string command)
{
+ if (string.IsNullOrWhiteSpace(command))
+ return;
+
+ Write('>' + command + '\n');
+
+ // Try CVar handling first (supports "name = value" and "name" syntax)
+ if (CVarSystem.TryHandleConsoleInput(command, this))
+ return;
+
+ // Parse as regular command
var splitCommand = command.Split(null as char[], System.StringSplitOptions.RemoveEmptyEntries);
if (splitCommand.Length < 1)
return;
- Write('>' + string.Join(" ", splitCommand) + '\n');
var commandName = splitCommand[0].ToLower();
CommandDelegate commandDelegate;
diff --git a/Assets/Game/Stats.cs b/Assets/Game/Stats.cs
index c43c503..b3cbed9 100644
--- a/Assets/Game/Stats.cs
+++ b/Assets/Game/Stats.cs
@@ -12,8 +12,6 @@ public class Stats : IGameSystem
long m_StopWatchFreq;
long m_LastFrameTicks;
- int m_ShowStats = 1;
-
public void Init()
{
m_StopWatch = new System.Diagnostics.Stopwatch();
@@ -21,12 +19,15 @@ public void Init()
m_StopWatch.Start();
m_LastFrameTicks = m_StopWatch.ElapsedTicks;
Debug.Assert(System.Diagnostics.Stopwatch.IsHighResolution);
- Game.console.AddCommand("showstats", CmdShowstats, "Show or hide stats");
+
+ // Legacy command still works, but now uses CVar
+ Game.console.AddCommand("showstats", CmdShowstats, "Toggle detailed stats (also available as cvar)");
}
private void CmdShowstats(string[] args)
{
- m_ShowStats = (m_ShowStats + 1) % 3;
+ // Toggle through: false -> true -> false
+ CVars.showStats.Value = !CVars.showStats.Value;
}
void CalcStatistics(float[] data, out float mean, out float variance, out float minValue, out float maxValue)
@@ -56,7 +57,7 @@ void CalcStatistics(float[] data, out float mean, out float variance, out float
float[] fpsHistory = new float[50];
public void TickUpdate()
{
- if (m_ShowStats < 1)
+ if (!CVars.showFps)
return;
long ticks = m_StopWatch.ElapsedTicks;
@@ -70,9 +71,9 @@ public void TickUpdate()
fpsHistory[Time.frameCount % fpsHistory.Length] = 1.0f / Time.deltaTime;
DebugOverlay.DrawGraph(1, 1, 9, 1.5f, fpsHistory, Time.frameCount % fpsHistory.Length, Color.green);
- DebugOverlay.Write(30, 0, "Open console (F12) and type: \"showstats\" to toggle graphs");
+ DebugOverlay.Write(30, 0, "Open console (F12) and type: \"showstats = true\" or \"showfps = false\"");
- if (m_ShowStats < 2)
+ if (!CVars.showStats)
return;
DebugOverlay.Write(0, 4, "Hello, {0,-5} world!", Time.frameCount % 100 < 50 ? "Happy" : "Evil");
diff --git a/DEVELOPMENT_GUIDELINES.md b/DEVELOPMENT_GUIDELINES.md
new file mode 100644
index 0000000..b66c3c8
--- /dev/null
+++ b/DEVELOPMENT_GUIDELINES.md
@@ -0,0 +1,56 @@
+# Development Guidelines
+
+This document contains guidelines extracted from analyzing preferred implementations to ensure future development aligns with the project's design philosophy.
+
+## Core Principles
+
+### 1. Prioritize Simplicity Over Abstraction
+- **Prefer concrete classes over generic abstractions**
+- **Avoid complex manager patterns when simple registration works**
+- **Keep related functionality in single files when possible**
+- **Write less code when possible**
+
+### 2. Performance is Paramount
+- **Direct field access over method calls for hot paths**
+- **Minimize allocation and indirection**
+- **Avoid implicit operators or complex conversion layers**
+- **Prefer explicit over implicit behavior**
+
+### 3. Follow Unity's Design Patterns
+- **Use direct field access like Unity components do**
+- **Keep registration simple and automatic**
+- **Prefer concrete implementations over abstract interfaces**
+- **Use clear, descriptive names**
+
+### 4. Console Integration Should Feel Natural
+- **Use intuitive command syntax (`name value` vs `name = value`)**
+- **Query by name for getting values**
+- **Keep parsing logic simple and direct**
+- **Minimize complex assignment detection**
+
+### 5. Code Style Guidelines
+- **Write less code when possible**
+- **Prefer concrete implementations over abstract interfaces**
+- **Use clear, descriptive names**
+- **Avoid over-engineering solutions**
+
+### 6. File Organization
+- **Keep related functionality together**
+- **Prefer single files over multiple small files**
+- **Minimize cross-file dependencies**
+
+## Key Takeaways
+
+1. **Simplicity wins**: The preferred implementation is much shorter and easier to understand
+2. **Performance matters**: Direct field access (`cvar.value`) is faster than method calls
+3. **Unity-like patterns**: Follow Unity's style of direct, explicit access
+4. **Natural integration**: Console commands should feel intuitive and simple
+5. **Less is more**: Avoid unnecessary abstraction layers and complex type systems
+
+## When to Apply These Guidelines
+
+- **New feature development**: Always consider the simpler approach first
+- **Performance-critical code**: Prioritize direct access over abstraction
+- **Console integration**: Keep commands natural and intuitive
+- **Code reviews**: Question complex abstractions and prefer simpler solutions
+- **Refactoring**: Look for opportunities to simplify existing code
\ No newline at end of file