# Godot C# Library Management System ## Overview This document describes a comprehensive system for managing reusable C# libraries across multiple Godot game projects using Git submodules. This approach enables clean separation of concerns, version control for individual libraries, and easy code reuse across multiple game projects. ## System Architecture ### Core Principles 1. **Separation of Concerns**: Libraries and game projects exist in separate Git repositories 2. **Modular Design**: Each library serves a specific purpose and can be developed independently 3. **Version Control**: Libraries can be versioned and updated independently of game projects 4. **Code Reuse**: Multiple projects can share the same libraries without code duplication 5. **Clean Dependencies**: C# project references ensure proper compilation and IntelliSense support ### Directory Structure ``` ~/GameDev/ ├── Godot/ # Godot engine installation ├── libraries/ # Local development copies of libraries │ ├── utilities/ # Example: utilities library repository │ ├── audio-manager/ # Example: audio management library │ └── ui-framework/ # Example: UI framework library └── projects/ # Game project repositories ├── test-game/ # Example: test game project │ └── Libraries/ # Git submodules directory │ ├── utilities/ # Submodule → library/utilities │ └── audio-manager/# Submodule → library/audio-manager └── puzzle-game/ # Example: another game project └── Libraries/ # Git submodules directory └── utilities/ # Submodule → library/utilities ``` ### Git Repository Organization **Gitea Organizations:** - `library/` - Contains all reusable library repositories - `project/` - Contains all game project repositories **Repository Examples:** - `https://git.ein-softworks.com/library/utilities.git` - `https://git.ein-softworks.com/library/audio-manager.git` - `https://git.ein-softworks.com/project/test-game.git` - `https://git.ein-softworks.com/project/puzzle-game.git` ## How Git Submodules Work Git submodules allow you to include one Git repository inside another as a subdirectory. In our case: 1. **Library repositories** contain reusable C# code and Godot scenes 2. **Game project repositories** include libraries as submodules in their `Libraries/` directory 3. **Submodules point to specific commits** in the library repositories, ensuring reproducible builds 4. **Updates are explicit** - you control when to pull in library changes ### Benefits of This Approach - **Independent Development**: Work on libraries without affecting game projects - **Version Stability**: Games use specific library versions until explicitly updated - **Selective Updates**: Choose which libraries to update in each project - **Clean History**: Each repository maintains its own commit history - **Collaborative Friendly**: Multiple developers can work on different libraries simultaneously ## Initial Setup Process ### Prerequisites - macOS with Godot v4.5.1 stable installed at `~/GameDev/Godot/` - .NET 8.0.121 installed - Git configured with access to your Gitea instance - Gitea instance with `library` and `project` organizations created ### Step 1: Create Directory Structure ```bash cd ~/GameDev mkdir -p libraries projects ``` ### Step 2: Create Your First Library #### Initialize the Library Repository ```bash cd ~/GameDev/libraries git init utilities cd utilities mkdir -p Scripts ``` #### Create Library Project Configuration Create `project.godot`: ```ini ; Engine configuration file. [application] config/name="Utilities Library" config/features=PackedStringArray("4.3", "C#", "Forward Plus") [dotnet] project/assembly_name="EinSoftworks.Utilities" ``` Create `Utilities.csproj`: ```xml net6.0 net7.0 net8.0 true EinSoftworks.Utilities EinSoftworks.Utilities ``` #### Create Example Library Code Create `Scripts/MathUtils.cs`: ```csharp using Godot; namespace EinSoftworks.Utilities { public static class MathUtils { /// /// Clamps a value between 0 and 1. /// public static float Clamp01(float value) { return Mathf.Clamp(value, 0f, 1f); } /// /// Checks if a number is approximately equal to another number. /// public static bool Approximately(float a, float b, float threshold = 0.001f) { return Mathf.Abs(a - b) < threshold; } /// /// Converts degrees to radians. /// public static float DegreesToRadians(float degrees) { return degrees * Mathf.Pi / 180f; } /// /// Converts radians to degrees. /// public static float RadiansToDegrees(float radians) { return radians * 180f / Mathf.Pi; } } } ``` #### Create .gitignore ```gitignore # Godot 4+ specific ignores .godot/ # Godot-specific ignores .import/ export.cfg export_presets.cfg # Imported translations (automatically generated from CSV files) *.translation # Mono-specific ignores .mono/ data_*/ mono_crash.*.json # .NET specific ignores bin/ obj/ *.tmp ``` #### Commit and Push Library ```bash git add . git commit -m "Initial utilities library setup with MathUtils" git remote add origin https://git.ein-softworks.com/library/utilities.git git branch -M main git push -u origin main ``` ### Step 3: Create Your First Game Project #### Initialize the Game Repository ```bash cd ~/GameDev/projects git init test-game cd test-game mkdir -p Scripts Scenes Libraries ``` #### Create Game Project Configuration Create `project.godot`: ```ini ; Engine configuration file. [application] config/name="Test Game" config/features=PackedStringArray("4.3", "C#", "Forward Plus") run/main_scene="res://Scenes/Main.tscn" [dotnet] project/assembly_name="TestGame" ``` Create `TestGame.csproj`: ```xml net6.0 net7.0 net8.0 true TestGame ``` #### Add Library as Submodule ```bash git submodule add https://git.ein-softworks.com/library/utilities.git Libraries/utilities git submodule update --init --recursive ``` #### Create Game Code Create `Scripts/Main.cs`: ```csharp using Godot; using EinSoftworks.Utilities; namespace TestGame { public partial class Main : Node2D { public override void _Ready() { GD.Print("Test Game Started!"); // Test the utilities library TestMathUtils(); } private void TestMathUtils() { // Test degree/radian conversion float testAngle = 90f; float radians = MathUtils.DegreesToRadians(testAngle); float backToDegrees = MathUtils.RadiansToDegrees(radians); GD.Print($"90° → {radians} rad → {backToDegrees}°"); // Test clamping float clampedValue = MathUtils.Clamp01(1.5f); GD.Print($"Clamped 1.5 to 0-1 range: {clampedValue}"); // Test approximation bool isApprox = MathUtils.Approximately(0.1f, 0.100001f); GD.Print($"0.1 ≈ 0.100001: {isApprox}"); } } } ``` Create `Scenes/Main.tscn`: ``` [gd_scene load_steps=2 format=3 uid="uid://b8y1qxqxqxqxq"] [ext_resource type="Script" path="res://Scripts/Main.cs" id="1"] [node name="Main" type="Node2D"] script = ExtResource("1") [node name="Label" type="Label" parent="."] offset_right = 400.0 offset_bottom = 100.0 text = "Test Game - Check console for utilities library output" horizontal_alignment = 1 vertical_alignment = 1 ``` #### Create .gitignore ```gitignore # Godot 4+ specific ignores .godot/ # Godot-specific ignores .import/ export.cfg export_presets.cfg # Imported translations (automatically generated from CSV files) *.translation # Mono-specific ignores .mono/ data_*/ mono_crash.*.json # .NET specific ignores bin/ obj/ *.tmp ``` #### Commit and Push Game Project ```bash git add . git commit -m "Initial test game setup with utilities library submodule" git remote add origin https://git.ein-softworks.com/project/test-game.git git branch -M main git push -u origin main ``` ### Step 4: Open and Build in Godot 1. Launch Godot: `~/GameDev/Godot/Godot.app/Contents/MacOS/Godot` 2. In Project Manager, click **"Import"** 3. Navigate to `~/GameDev/projects/test-game/` and select `project.godot` 4. Click **"Import & Edit"** 5. In the Godot editor, go to **Project → Tools → C# → Create C# solution** 6. Click the **"Build"** button in the toolbar (or **Project → Tools → C# → Build Solution**) 7. Run the game with the **Play** button Expected console output: ``` Test Game Started! 90° → 1.5708 rad → 90° Clamped 1.5 to 0-1 range: 1 0.1 ≈ 0.100001: True ``` ## Usage Workflows ### Working on Libraries #### Developing New Features When you want to add functionality to an existing library: ```bash # Navigate to library cd ~/GameDev/libraries/utilities # Create a new branch for your feature git checkout -b feature/string-utilities # Add your new code (example) cat >> Scripts/StringUtils.cs << 'EOF' using Godot; namespace EinSoftworks.Utilities { public static class StringUtils { public static string ToPascalCase(string input) { if (string.IsNullOrEmpty(input)) return input; return char.ToUpper(input[0]) + input.Substring(1).ToLower(); } public static bool IsValidEmail(string email) { return email.Contains("@") && email.Contains("."); } } } EOF # Test your changes in Godot # Open ~/GameDev/libraries/utilities in Godot # Build and test the library # Commit your changes git add . git commit -m "Add StringUtils with PascalCase and email validation" # Push the feature branch git push origin feature/string-utilities # Create a pull request in Gitea # After review and merge, switch back to main git checkout main git pull origin main # Clean up feature branch git branch -d feature/string-utilities ``` #### Creating a New Library ```bash # Create new library repository cd ~/GameDev/libraries git init audio-manager cd audio-manager # Set up library structure mkdir -p Scripts Resources cat > project.godot << 'EOF' ; Engine configuration file. [application] config/name="Audio Manager Library" config/features=PackedStringArray("4.3", "C#", "Forward Plus") [dotnet] project/assembly_name="EinSoftworks.AudioManager" EOF cat > AudioManager.csproj << 'EOF' net6.0 net7.0 net8.0 true EinSoftworks.AudioManager EinSoftworks.AudioManager EOF # Create basic audio manager cat > Scripts/AudioManager.cs << 'EOF' using Godot; namespace EinSoftworks.AudioManager { public partial class AudioManager : Node { private AudioStreamPlayer _musicPlayer; private AudioStreamPlayer _sfxPlayer; public override void _Ready() { _musicPlayer = new AudioStreamPlayer(); _sfxPlayer = new AudioStreamPlayer(); AddChild(_musicPlayer); AddChild(_sfxPlayer); } public void PlayMusic(AudioStream music, float volume = 1.0f) { _musicPlayer.Stream = music; _musicPlayer.VolumeDb = Mathf.LinearToDb(volume); _musicPlayer.Play(); } public void PlaySFX(AudioStream sfx, float volume = 1.0f) { _sfxPlayer.Stream = sfx; _sfxPlayer.VolumeDb = Mathf.LinearToDb(volume); _sfxPlayer.Play(); } } } EOF # Copy standard .gitignore cp ../utilities/.gitignore . # Commit and push git add . git commit -m "Initial audio manager library" git remote add origin https://git.ein-softworks.com/library/audio-manager.git git branch -M main git push -u origin main ``` ### Working with Game Projects #### Adding Libraries to Existing Projects To add a new library to an existing game project: ```bash # Navigate to your game project cd ~/GameDev/projects/test-game # Add the new library as a submodule git submodule add https://git.ein-softworks.com/library/audio-manager.git Libraries/audio-manager # Update the .csproj file to reference the new library # Edit TestGame.csproj and add to ItemGroup: # # Example of the updated .csproj: cat > TestGame.csproj << 'EOF' net6.0 net7.0 net8.0 true TestGame EOF # Commit the changes git add . git commit -m "Add audio-manager library dependency" git push origin main # In Godot: Project → Reload Current Project # Then build the solution ``` #### Updating Libraries in Projects To update a library to the latest version: ```bash # Navigate to your game project cd ~/GameDev/projects/test-game # Update specific library to latest git submodule update --remote Libraries/utilities # Or update all submodules git submodule update --remote # Commit the library updates git add . git commit -m "Update utilities library to latest version" git push origin main # In Godot: Project → Reload Current Project # Then rebuild the solution ``` To update to a specific version/commit: ```bash # Navigate to the submodule cd ~/GameDev/projects/test-game/Libraries/utilities # Check available tags/versions git tag -l # Checkout specific version git checkout v1.2.0 # Go back to project root cd ~/GameDev/projects/test-game # Commit the specific version git add Libraries/utilities git commit -m "Update utilities library to v1.2.0" git push origin main ``` #### Creating New Game Projects To create a new game project with existing libraries: ```bash # Create new project cd ~/GameDev/projects git init puzzle-game cd puzzle-game # Set up basic structure mkdir -p Scripts Scenes Libraries # Create project.godot cat > project.godot << 'EOF' ; Engine configuration file. [application] config/name="Puzzle Game" config/features=PackedStringArray("4.3", "C#", "Forward Plus") run/main_scene="res://Scenes/Main.tscn" [dotnet] project/assembly_name="PuzzleGame" EOF # Add required libraries git submodule add https://git.ein-softworks.com/library/utilities.git Libraries/utilities git submodule add https://git.ein-softworks.com/library/audio-manager.git Libraries/audio-manager # Create .csproj with all dependencies cat > PuzzleGame.csproj << 'EOF' net6.0 net7.0 net8.0 true PuzzleGame EOF # Copy .gitignore from another project cp ../test-game/.gitignore . # Initialize submodules git submodule update --init --recursive # Create basic game code cat > Scripts/Main.cs << 'EOF' using Godot; using EinSoftworks.Utilities; using EinSoftworks.AudioManager; namespace PuzzleGame { public partial class Main : Node2D { private AudioManager _audioManager; public override void _Ready() { GD.Print("Puzzle Game Started!"); // Set up audio manager _audioManager = new AudioManager(); AddChild(_audioManager); // Test utilities float angle = MathUtils.DegreesToRadians(45f); GD.Print($"45 degrees = {angle} radians"); } } } EOF # Commit and push git add . git commit -m "Initial puzzle game setup with utilities and audio-manager libraries" git remote add origin https://git.ein-softworks.com/project/puzzle-game.git git branch -M main git push -u origin main ``` ### Advanced Workflows #### Working with Library Dependencies If one library depends on another: ```csharp // In Libraries/ui-framework/UIFramework.csproj net6.0 true EinSoftworks.UIFramework EinSoftworks.UIFramework ``` #### Creating Library Releases For important library milestones: ```bash # In your library repository cd ~/GameDev/libraries/utilities # Create and push a tag git tag -a v1.0.0 -m "Release version 1.0.0 - Initial stable release" git push origin v1.0.0 # Create release notes in Gitea # Navigate to your Gitea repository → Releases → New Release ``` #### Cloning Projects on New Machines When setting up on a new development machine: ```bash # Clone project with submodules git clone --recursive https://git.ein-softworks.com/project/test-game.git # Or if already cloned without --recursive cd test-game git submodule update --init --recursive ``` ## Best Practices ### Library Design 1. **Keep libraries focused**: Each library should have a single, well-defined purpose 2. **Use proper namespacing**: Follow the pattern `CompanyName.LibraryName` 3. **Document your code**: Use XML documentation comments for public APIs 4. **Version your releases**: Use semantic versioning (major.minor.patch) 5. **Test thoroughly**: Create test projects for each library ### Project Organization 1. **Consistent naming**: Use kebab-case for repository names, PascalCase for namespaces 2. **Clear dependencies**: Only include libraries you actually use 3. **Regular updates**: Keep libraries updated, but test thoroughly after updates 4. **Backup strategy**: Ensure all repositories are backed up on your Gitea instance ### Git Workflow 1. **Feature branches**: Use feature branches for library development 2. **Descriptive commits**: Write clear commit messages 3. **Regular pushes**: Push changes regularly to avoid data loss 4. **Clean history**: Squash commits when merging features ### Development Environment 1. **Consistent paths**: Always use the `~/GameDev/` structure 2. **IDE setup**: Configure your IDE to recognize the library references 3. **Build automation**: Consider setting up CI/CD for library testing 4. **Documentation**: Keep this document updated as your system evolves ## Troubleshooting ### Common Issues #### Submodule Not Found ```bash # If Libraries/utilities is empty cd ~/GameDev/projects/test-game git submodule update --init --recursive ``` #### Build Errors ```bash # In Godot editor # Project → Reload Current Project # Project → Tools → C# → Create C# solution # Build ``` #### Missing References Check that your `.csproj` file includes all required `` entries for your submodules. #### Submodule Update Conflicts ```bash # If you have uncommitted changes in a submodule cd Libraries/utilities git stash git pull origin main git stash pop ``` ### Getting Help 1. Check Godot's C# documentation 2. Review Git submodule documentation 3. Examine this document for reference patterns 4. Test changes in isolation before applying to important projects --- *This documentation should be updated as your library system evolves and new patterns emerge.*