Popcorning Guinea Pig
Search Results for

    Main Menu Example

    This example demonstrates creating a traditional main menu using C# code and the PopcorningGuineaPig Engine's UI system. Unlike the PGPSCN approach, this example shows manual UI creation with programmatic positioning, styling, and event handling.

    Overview

    The Main Menu example is located in examples/main-menu/ and showcases:

    • Traditional C# UI Creation: Manual instantiation and configuration of UI elements
    • Responsive Design: Percentage-based positioning that adapts to screen resolution
    • Multi-Language Support: Unicode font support for Chinese, Japanese, and Korean
    • Settings Integration: Persistent settings with resolution and language options
    • Scene Management: Navigation between splash screens and main menu
    • Input System: Command pattern for keyboard shortcuts (ESC to exit)

    Key Features

    1. Scene Progression

    The game starts with splash screens before showing the main menu:

    public MyGame() : base()
    {
        // Create scenes
        _engineSplashScreenScene = new EngineSplashScreenScene();
        _splashScreenScene = new SplashScreenScene();
        _mainMenu = new MainMenuScene();
    
        // Queue scenes with timing
        this.SceneManager.QueueScene(_engineSplashScreenScene, 0f); // Start immediately
        this.SceneManager.QueueScene(_splashScreenScene, 3.0f);     // Show after 3 seconds
        this.SceneManager.QueueScene(_mainMenu, 3.0f);             // Show after another 3 seconds
    }
    

    2. Responsive UI Positioning

    UI elements use percentage-based positioning for responsive design:

    private const float COPYRIGHT_X_PERCENT = 0.02f;   // 2% from left edge
    private const float COPYRIGHT_Y_PERCENT = 0.02f;   // 2% from top edge
    private const float BUTTON_WIDTH_PERCENT = 0.125f; // 12.5% of screen width
    private const float BUTTON_HEIGHT_PERCENT = 0.067f; // 6.7% of screen height
    
    private void PositionUIElements()
    {
        int screenWidth = Raylib.GetScreenWidth();
        int screenHeight = Raylib.GetScreenHeight();
        
        Vector2 copyrightPos = new Vector2(
            screenWidth * COPYRIGHT_X_PERCENT,
            screenHeight * COPYRIGHT_Y_PERCENT
        );
        
        Vector2 buttonSize = new Vector2(
            screenWidth * BUTTON_WIDTH_PERCENT,
            screenHeight * BUTTON_HEIGHT_PERCENT
        );
    }
    

    3. Multi-Language Font Management

    Centralized font management supports multiple languages:

    private void ConfigureCentralizedFonts()
    {
        var fontPaths = new Dictionary<string, string>
        {
            {"en", "assets/Roboto-Regular.ttf"},
            {"ja", "assets/NotoSansJP-VariableFont_wght.ttf"},
            {"ko", "assets/NotoSansKR-VariableFont_wght.ttf"},
            {"zh", "assets/NotoSansSC-VariableFont_wght.ttf"},
            {"emoji", "assets/NotoColorEmoji-Regular.ttf"}
        };
        Game.Instance.Localization.SetFontPaths(fontPaths);
    }
    

    4. Unicode Font Loading

    Advanced font loading with Unicode support for CJK characters:

    private Font LoadFontWithUnicodeSupport(string path)
    {
        // Define character ranges: ASCII + Chinese + Japanese + Korean
        int[] codepoints = new int[/* calculated size */];
        int index = 0;
    
        // ASCII range (0x20-0x7E)
        for (int i = 0x20; i <= 0x7E; i++)
            codepoints[index++] = i;
    
        // Chinese range (0x4E00-0x9FFF) - Common CJK
        for (int i = 0x4E00; i <= 0x9FFF; i++)
            codepoints[index++] = i;
    
        // Japanese Hiragana (0x3040-0x309F)
        for (int i = 0x3040; i <= 0x309F; i++)
            codepoints[index++] = i;
    
        // Korean Hangul (0xAC00-0xD7AF)
        for (int i = 0xAC00; i <= 0xD7AF; i++)
            codepoints[index++] = i;
    
        // Load font with Unicode ranges
        var font = Raylib.LoadFontEx(pathPtr, fontSize, codepointsPtr, codepoints.Length);
        return font;
    }
    

    5. Dynamic Language Switching

    Runtime language changes with immediate UI updates:

    private void OnLanguageChanged(string selectedLanguage)
    {
        // Set the language in the localization system
        Game.Instance.Localization.SetLanguage(selectedLanguage);
    
        // Force refresh of all localized content with proper fonts
        RefreshAllLocalizedContent();
    }
    
    private void RefreshAllLocalizedContent()
    {
        // Get translated strings for the new language
        string titleText = Game.Instance.Localization.GetString("main_menu_title");
        string settingsText = Game.Instance.Localization.GetString("settings_button");
        string exitText = Game.Instance.Localization.GetString("exit_button");
        
        // Update UI elements with new text and appropriate fonts
        copyright.SetText(titleText);
        settingsButton.SetText(settingsText);
        exitButton.SetText(exitText);
        
        // Update fonts based on language
        string fontPath = GetFontPathForLanguage(currentLanguage);
        UpdateElementFont("copyright", fontPath);
        UpdateElementFont("settings_button", fontPath);
        UpdateElementFont("exit_button", fontPath);
    }
    

    6. Command Pattern Input System

    Keyboard shortcuts using the command pattern:

    protected override void ConfigureInputBindings()
    {
        // Add ESC key binding to exit the game
        SceneInputContext.AddBinding(new InputBinding(
            "exit_game",
            InputKey.Keyboard(KeyboardKey.Escape),
            new LambdaCommand(() => ExitGame(), "exit_game"),
            InputTriggerType.Pressed
        ));
    }
    

    7. Settings Management

    Persistent settings with resolution and display mode configuration:

    private void LoadSettings()
    {
        _settingsService = new SettingsService();
        string settingsPath = Path.Combine(Directory.GetCurrentDirectory(), "settings.json");
        
        if (File.Exists(settingsPath))
        {
            _settings = _settingsService.LoadSettings(settingsPath);
        }
        else
        {
            _settings = new Settings(); // Use defaults
        }
    }
    

    UI Elements

    Copyright Label

    Displays the game title with localization support:

    copyright = new Label(
        Game.Instance.Localization.GetString("main_menu_title"), 
        copyrightPos, 
        20, 
        Color.White
    );
    

    Settings Button

    Opens the settings scene:

    settingsButton = new Button(
        Game.Instance.Localization.GetString("settings_button"), 
        settingsButtonPos, 
        buttonSize, 
        Color.Blue
    );
    settingsButton.SetOnClick(OpenSettings);
    

    Exit Button

    Exits the application:

    exitButton = new Button(
        Game.Instance.Localization.GetString("exit_button"), 
        exitButtonPos, 
        buttonSize, 
        Color.Red
    );
    exitButton.SetOnClick(() => ExitGame());
    

    Language Dropdown

    Runtime language selection:

    var languages = Game.Instance.Localization.AvailableLanguages.ToList();
    languageDropdown = new Dropdown<string>(
        languageDropdownPos, 
        languageDropdownSize, 
        languages, 
        0
    );
    languageDropdown.SetOnSelectionChanged((selectedLanguage) => 
        OnLanguageChanged(selectedLanguage)
    );
    

    Running the Example

    Prerequisites

    • .NET 10.0 or later
    • PopcorningGuineaPig.Engine reference
    • Raylib-cs package

    Build and Run

    cd examples/main-menu/MainMenu
    dotnet build
    dotnet run
    

    Project Structure

    MainMenu/
    ├── MainMenu.csproj           # Project file
    ├── MyGame.cs                 # Game initialization and scene management
    ├── Program.cs                # Entry point
    ├── settings.json             # Persistent game settings
    ├── assets/                   # Assets directory
    │   ├── Roboto-Regular.ttf    # Western languages font
    │   ├── NotoSansSC-VariableFont_wght.ttf    # Chinese font
    │   ├── NotoSansJP-VariableFont_wght.ttf    # Japanese font
    │   ├── NotoSansKR-VariableFont_wght.ttf    # Korean font
    │   ├── NotoColorEmoji-Regular.ttf          # Emoji font
    │   └── localization.csv      # Translation data
    └── scenes/
        ├── EngineSplashScreenScene.cs  # Engine splash screen
        ├── SplashScreenScene.cs        # Game splash screen
        ├── MainMenuScene.cs            # Main menu implementation
        └── SettingsScene.cs            # Settings menu
    

    What You'll Learn

    1. Manual UI Creation: Creating and positioning UI elements programmatically
    2. Responsive Design Patterns: Percentage-based positioning for multiple resolutions
    3. Unicode Font Handling: Loading and managing fonts for international text
    4. Localization Integration: Dynamic language switching with immediate UI updates
    5. Command Pattern Input: Keyboard shortcuts using the engine's input system
    6. Scene Management: Transitioning between splash screens and main menu
    7. Settings Persistence: Loading and saving game configuration
    8. Resolution Adaptation: Handling window resize events

    Advanced Techniques

    Resolution Change Handling

    The scene automatically adjusts UI elements when resolution changes:

    public override void Render(int width, int height)
    {
        // Check if resolution has changed
        if (width != Raylib.GetScreenWidth() || height != Raylib.GetScreenHeight())
        {
            PositionUIElements();
        }
        
        copyright.Draw();
        settingsButton.Draw();
        exitButton.Draw();
        languageDropdown.Draw();
    }
    

    Font Path Selection

    Dynamic font selection based on language:

    private string GetFontPathForLanguage(string languageCode)
    {
        return languageCode switch
        {
            "zh" => "assets/NotoSansSC-VariableFont_wght.ttf",
            "ko" => "assets/NotoSansKR-VariableFont_wght.ttf",
            "ja" => "assets/NotoSansJP-VariableFont_wght.ttf",
            _ => "assets/Roboto-Regular.ttf" // Default to English font
        };
    }
    

    Error Handling

    Robust error handling for font loading and UI updates:

    public override void Update(double deltaTime)
    {
        try
        {
            settingsButton.Update();
            exitButton.Update();
            languageDropdown.Update();
        }
        catch (Exception ex)
        {
            // Handle exceptions gracefully
            Console.WriteLine($"Error in MainMenuScene.Update: {ex.Message}");
        }
    }
    

    Comparison with PGPSCN Approach

    Traditional C# Approach (This Example)

    • More Control: Direct access to all UI element properties
    • Type Safety: Compile-time checking of UI element interactions
    • Debugging: Full debugging capabilities with breakpoints
    • Complexity: More code required for basic UI layouts
    • Compilation: Changes require recompilation

    PGPSCN Approach

    • Declarative: UI defined in JSON format
    • Rapid Iteration: Changes without recompilation
    • Designer Friendly: Non-programmers can modify UI
    • Less Control: Limited to supported properties and actions
    • Runtime Errors: JSON parsing errors only caught at runtime

    Related Documentation

    • PGPSCN Example - Declarative alternative to this approach
    • UI Components - Available UI element types
    • Input System - Command pattern input handling
    • Localization - Multi-language support
    • Settings Management - Configuration persistence
    • Scene Management - Scene lifecycle

    Next Steps

    After exploring this example, consider:

    1. Add New UI Elements: Buttons, sliders, or custom components
    2. Implement Custom Fonts: Create your own font loading system
    3. Enhance Localization: Add more languages or dynamic text
    4. Create Custom Scenes: Build additional menu screens
    5. Compare Approaches: Try implementing the same UI using PGPSCN format
    6. Performance Optimization: Profile and optimize UI rendering

    This example demonstrates the traditional, code-first approach to UI development with the PopcorningGuineaPig Engine, providing maximum flexibility and control over the user interface.

    • Edit this page
    In this article
    Back to top © 2024 Popcorning Guinea Pig Game Engine. A custom C# game engine for indie developers.