Popcorning Guinea Pig
Search Results for

    UI Components

    The Popcorning Guinea Pig engine provides a comprehensive set of UI components for building interactive user interfaces, all built on a hierarchical system with flexible styling and positioning.

    Overview

    The UI system features:

    • Hierarchical component tree with parent-child relationships
    • Flexible positioning and sizing options
    • Event-driven interaction model
    • Consistent styling and theming support
    • Built-in accessibility features
    • Custom component creation capabilities

    Base UI Component

    All UI components inherit from the base UIElement class:

    public abstract class UIElement
    {
        public Vector2 Position { get; set; }
        public Vector2 Size { get; set; }
        public AnchorPoint Anchor { get; set; }
        public bool Visible { get; set; }
        public bool Enabled { get; set; }
        public float Alpha { get; set; }
        public Color Tint { get; set; }
        public string Name { get; set; }
        
        // Hierarchy
        public UIElement Parent { get; protected set; }
        public List<UIElement> Children { get; protected set; }
        
        // Events
        public event Action<UIElement> OnClick;
        public event Action<UIElement> OnHover;
        public event Action<UIElement> OnFocus;
        
        // Methods
        public virtual void AddChild(UIElement child);
        public virtual void RemoveChild(UIElement child);
        public virtual void Update(GameTime gameTime);
        public virtual void Draw(SpriteBatch spriteBatch);
    }
    

    Core UI Components

    Label

    Display text with various formatting options:

    public class Label : UIElement
    {
        public string Text { get; set; }
        public Font Font { get; set; }
        public Color TextColor { get; set; }
        public TextAlignment Alignment { get; set; }
        public bool WordWrap { get; set; }
        public float LineSpacing { get; set; }
    }
    
    // Usage
    var titleLabel = new Label
    {
        Text = "Game Title",
        Position = new Vector2(400, 100),
        Font = assetRegistry.LoadAsset<Font>("fonts/title.ttf"),
        TextColor = Color.White,
        Alignment = TextAlignment.Center
    };
    

    Button

    Interactive button component with multiple states:

    public class Button : UIElement
    {
        public string Text { get; set; }
        public Font Font { get; set; }
        public Texture2D NormalTexture { get; set; }
        public Texture2D HoverTexture { get; set; }
        public Texture2D PressedTexture { get; set; }
        public Texture2D DisabledTexture { get; set; }
        public Color TextColor { get; set; }
        public ButtonState State { get; protected set; }
        
        public event Action OnClick;
        public event Action OnPressed;
        public event Action OnReleased;
    }
    
    // Usage
    var playButton = new Button
    {
        Text = "Play Game",
        Position = new Vector2(400, 200),
        Size = new Vector2(200, 60),
        NormalTexture = assetRegistry.LoadAsset<Texture2D>("ui/button_normal.png"),
        HoverTexture = assetRegistry.LoadAsset<Texture2D>("ui/button_hover.png"),
        PressedTexture = assetRegistry.LoadAsset<Texture2D>("ui/button_pressed.png"),
        OnClick = () => StartGame()
    };
    

    Panel

    Container component for organizing other UI elements:

    public class Panel : UIElement
    {
        public Texture2D BackgroundTexture { get; set; }
        public Color BackgroundColor { get; set; }
        public Texture2D BorderTexture { get; set; }
        public float BorderThickness { get; set; }
        public bool ClipChildren { get; set; }
        public Padding Padding { get; set; }
    }
    
    // Usage
    var settingsPanel = new Panel
    {
        Position = new Vector2(50, 50),
        Size = new Vector2(400, 500),
        BackgroundColor = new Color(0, 0, 0, 180), // Semi-transparent black
        BorderThickness = 2,
        Padding = new Padding(20)
    };
    

    Slider

    Interactive slider for numeric value selection:

    public class Slider : UIElement
    {
        public float MinValue { get; set; }
        public float MaxValue { get; set; }
        public float Value { get; set; }
        public float StepSize { get; set; }
        public Texture2D TrackTexture { get; set; }
        public Texture2D HandleTexture { get; set; }
        public bool ShowValue { get; set; }
        
        public event Action<float> OnValueChanged;
    }
    
    // Usage
    var volumeSlider = new Slider
    {
        Position = new Vector2(200, 300),
        Size = new Vector2(200, 20),
        MinValue = 0f,
        MaxValue = 1f,
        Value = 0.8f,
        StepSize = 0.01f,
        OnValueChanged = (value) => SetMasterVolume(value)
    };
    

    Toggle

    Checkbox or toggle switch component:

    public class Toggle : UIElement
    {
        public bool IsChecked { get; set; }
        public string Text { get; set; }
        public Texture2D CheckedTexture { get; set; }
        public Texture2D UncheckedTexture { get; set; }
        public Texture2D HoverTexture { get; set; }
        public Font Font { get; set; }
        
        public event Action<bool> OnToggled;
    }
    
    // Usage
    var fullscreenToggle = new Toggle
    {
        Text = "Fullscreen",
        Position = new Vector2(200, 350),
        IsChecked = true,
        OnToggled = (isChecked) => SetFullscreen(isChecked)
    };
    

    Dropdown

    Dropdown menu for selecting from multiple options:

    public class Dropdown : UIElement
    {
        public string[] Items { get; set; }
        public int SelectedIndex { get; set; }
        public string SelectedItem => SelectedIndex >= 0 && SelectedIndex < Items.Length ? Items[SelectedIndex] : null;
        public bool IsOpen { get; protected set; }
        public Font Font { get; set; }
        public Texture2D BackgroundTexture { get; set; }
        public Texture2D ArrowTexture { get; set; }
        
        public event Action<int> OnSelectionChanged;
    }
    
    // Usage
    var resolutionDropdown = new Dropdown
    {
        Items = new[] { "1920x1080", "1600x900", "1280x720" },
        SelectedIndex = 0,
        Position = new Vector2(200, 400),
        Size = new Vector2(150, 30),
        OnSelectionChanged = (index) => SetResolution(index)
    };
    

    Advanced Components

    GridContainer

    Layout container that arranges children in a grid:

    public class GridContainer : UIElement
    {
        public int Rows { get; set; }
        public int Columns { get; set; }
        public Vector2 CellSize { get; set; }
        public Vector2 Spacing { get; set; }
        public GridAlignment Alignment { get; set; }
        
        public void AddChild(UIElement child, int row, int column);
        public void RemoveChild(int row, int column);
        public UIElement GetChild(int row, int column);
    }
    
    // Usage
    var inventoryGrid = new GridContainer
    {
        Rows = 5,
        Columns = 8,
        CellSize = new Vector2(50, 50),
        Spacing = new Vector2(5, 5),
        Position = new Vector2(100, 100)
    };
    
    // Add items to specific grid positions
    for (int row = 0; row < 5; row++)
    {
        for (int col = 0; col < 8; col++)
        {
            var slot = new ItemSlot();
            inventoryGrid.AddChild(slot, row, col);
        }
    }
    

    ScrollView

    Scrollable container for content larger than the display area:

    public class ScrollView : UIElement
    {
        public Vector2 ContentSize { get; set; }
        public Vector2 ScrollOffset { get; set; }
        public bool ShowScrollbars { get; set; }
        public ScrollbarStyle ScrollbarStyle { get; set; }
        public UIElement Content { get; set; }
        
        public void ScrollTo(Vector2 position);
        public void ScrollToChild(UIElement child);
    }
    
    // Usage
    var chatScrollView = new ScrollView
    {
        Position = new Vector2(50, 50),
        Size = new Vector2(300, 400),
        ShowScrollbars = true,
        ContentSize = new Vector2(300, 800) // Content is taller than view
    };
    
    var chatContent = new Panel
    {
        Size = new Vector2(300, 800)
    };
    chatScrollView.Content = chatContent;
    

    ProgressBar

    Visual progress indicator:

    public class ProgressBar : UIElement
    {
        public float MinValue { get; set; }
        public float MaxValue { get; set; }
        public float Value { get; set; }
        public ProgressBarStyle Style { get; set; }
        public Color FillColor { get; set; }
        public Color BackgroundColor { get; set; }
        public bool ShowPercentage { get; set; }
        public Font Font { get; set; }
    }
    
    // Usage
    var healthBar = new ProgressBar
    {
        Position = new Vector2(50, 50),
        Size = new Vector2(200, 20),
        MinValue = 0,
        MaxValue = 100,
        Value = 75,
        FillColor = Color.Red,
        BackgroundColor = Color.DarkRed,
        ShowPercentage = true
    };
    

    InputField

    Text input component with validation:

    public class InputField : UIElement
    {
        public string Text { get; set; }
        public string PlaceholderText { get; set; }
        public Font Font { get; set; }
        public int MaxLength { get; set; }
        public bool IsPassword { get; set; }
        public bool IsReadOnly { get; set; }
        public TextValidation Validation { get; set; }
        public bool HasFocus { get; protected set; }
        
        public event Action<string> OnTextChanged;
        public event Action OnEnterPressed;
    }
    
    // Usage
    var playerNameInput = new InputField
    {
        Position = new Vector2(200, 250),
        Size = new Vector2(200, 30),
        PlaceholderText = "Enter your name",
        MaxLength = 20,
        Validation = TextValidation.AlphaNumeric,
        OnTextChanged = (text) => ValidatePlayerName(text)
    };
    

    Component Styling

    Styling System

    Apply consistent styling across components:

    public class UIStyle
    {
        public Color BackgroundColor { get; set; }
        public Color TextColor { get; set; }
        public Color BorderColor { get; set; }
        public float BorderThickness { get; set; }
        public Font Font { get; set; }
        public Padding Padding { get; set; }
        public Padding Margin { get; set; }
    }
    
    public class UITheme
    {
        public UIStyle DefaultStyle { get; set; }
        public UIStyle ButtonStyle { get; set; }
        public UIStyle PanelStyle { get; set; }
        public UIStyle LabelStyle { get; set; }
        
        public void ApplyTheme(UIElement element);
    }
    
    // Usage
    var darkTheme = new UITheme
    {
        DefaultStyle = new UIStyle
        {
            BackgroundColor = new Color(32, 32, 32),
            TextColor = Color.White,
            BorderColor = Color.Gray,
            Font = defaultFont
        },
        ButtonStyle = new UIStyle
        {
            BackgroundColor = new Color(64, 64, 64),
            TextColor = Color.White,
            BorderThickness = 2
        }
    };
    
    // Apply theme to components
    darkTheme.ApplyTheme(myButton);
    darkTheme.ApplyTheme(myPanel);
    

    Custom Styling

    Create custom styled components:

    public class StyledButton : Button
    {
        public UIStyle CustomStyle { get; set; }
        
        public override void Draw(SpriteBatch spriteBatch)
        {
            if (CustomStyle != null)
            {
                ApplyStyle(CustomStyle);
            }
            base.Draw(spriteBatch);
        }
        
        private void ApplyStyle(UIStyle style)
        {
            TextColor = style.TextColor;
            if (style.Font != null)
                Font = style.Font;
            // Apply other style properties...
        }
    }
    

    Event Handling

    Component Events

    Handle user interactions with events:

    public class MenuScene : Scene
    {
        private Button _playButton;
        private Button _settingsButton;
        private Slider _volumeSlider;
        
        public MenuScene()
        {
            SetupUI();
        }
        
        private void SetupUI()
        {
            _playButton = new Button
            {
                Text = "Play",
                Position = new Vector2(400, 200),
                OnClick = OnPlayClicked,
                OnHover = OnButtonHovered
            };
            
            _settingsButton = new Button
            {
                Text = "Settings",
                Position = new Vector2(400, 250),
                OnClick = OnSettingsClicked
            };
            
            _volumeSlider = new Slider
            {
                Position = new Vector2(300, 300),
                MinValue = 0f,
                MaxValue = 1f,
                OnValueChanged = OnVolumeChanged
            };
            
            AddElement(_playButton);
            AddElement(_settingsButton);
            AddElement(_volumeSlider);
        }
        
        private void OnPlayClicked()
        {
            SceneManager.LoadScene("Gameplay");
        }
        
        private void OnSettingsClicked()
        {
            SceneManager.PushScene("Settings");
        }
        
        private void OnVolumeChanged(float volume)
        {
            AudioManager.SetMasterVolume(volume);
        }
        
        private void OnButtonHovered()
        {
            AudioManager.PlaySound("ui/hover.wav");
        }
    }
    

    Event Propagation

    Control how events bubble through the UI hierarchy:

    public class UIEventSystem
    {
        public void ProcessEvents(UIElement root, InputState input)
        {
            var hitElement = FindHitElement(root, input.MousePosition);
            
            if (input.IsMouseButtonPressed(MouseButton.Left))
            {
                ProcessClickEvent(hitElement, input);
            }
            
            if (input.MousePosition != _lastMousePosition)
            {
                ProcessHoverEvent(hitElement, input);
            }
        }
        
        private void ProcessClickEvent(UIElement element, InputState input)
        {
            var currentElement = element;
            
            while (currentElement != null)
            {
                if (currentElement.OnClick != null)
                {
                    currentElement.OnClick.Invoke(currentElement);
                    
                    // Stop propagation if event is handled
                    if (currentElement.ConsumesEvents)
                        break;
                }
                
                currentElement = currentElement.Parent;
            }
        }
    }
    

    Animation

    Component Animation

    Animate UI components for better user experience:

    public class UIAnimator
    {
        private readonly List<UIAnimation> _activeAnimations;
        
        public void AnimatePosition(UIElement element, Vector2 targetPosition, float duration)
        {
            var animation = new PositionAnimation
            {
                Element = element,
                StartPosition = element.Position,
                TargetPosition = targetPosition,
                Duration = duration,
                EasingFunction = EasingFunctions.EaseOutQuad
            };
            
            _activeAnimations.Add(animation);
        }
        
        public void AnimateScale(UIElement element, Vector2 targetScale, float duration)
        {
            var animation = new ScaleAnimation
            {
                Element = element,
                StartScale = element.Size,
                TargetScale = targetScale,
                Duration = duration
            };
            
            _activeAnimations.Add(animation);
        }
        
        public void AnimateAlpha(UIElement element, float targetAlpha, float duration)
        {
            var animation = new AlphaAnimation
            {
                Element = element,
                StartAlpha = element.Alpha,
                TargetAlpha = targetAlpha,
                Duration = duration
            };
            
            _activeAnimations.Add(animation);
        }
    }
    
    // Usage
    var animator = new UIAnimator();
    
    // Fade in a panel
    animator.AnimateAlpha(myPanel, 1.0f, 0.5f);
    
    // Slide in a menu
    animator.AnimatePosition(mainMenu, new Vector2(0, 0), 0.3f);
    

    Transition Effects

    Create smooth transitions between UI states:

    public class UITransitions
    {
        public static void SlideIn(UIElement element, Direction direction, float duration = 0.3f)
        {
            var startPos = GetOffscreenPosition(element, direction);
            var endPos = element.Position;
            
            element.Position = startPos;
            
            var tween = new Tween(0f, 1f, duration);
            tween.OnUpdate = (progress) =>
            {
                element.Position = Vector2.Lerp(startPos, endPos, progress);
            };
            
            TweenManager.AddTween(tween);
        }
        
        public static void FadeIn(UIElement element, float duration = 0.3f)
        {
            element.Alpha = 0f;
            
            var tween = new Tween(0f, 1f, duration);
            tween.OnUpdate = (progress) =>
            {
                element.Alpha = progress;
            };
            
            TweenManager.AddTween(tween);
        }
    }
    

    Custom Components

    Creating Custom Components

    Extend the base UIElement to create specialized components:

    public class HealthBar : UIElement
    {
        private float _currentHealth;
        private float _maxHealth;
        private Color _healthColor;
        private Color _backgroundColor;
        
        public float CurrentHealth
        {
            get => _currentHealth;
            set
            {
                _currentHealth = Math.Clamp(value, 0, _maxHealth);
                OnHealthChanged?.Invoke(_currentHealth);
            }
        }
        
        public float MaxHealth
        {
            get => _maxHealth;
            set => _maxHealth = Math.Max(0, value);
        }
        
        public event Action<float> OnHealthChanged;
        
        public override void Draw(SpriteBatch spriteBatch)
        {
            var bounds = GetAbsoluteBounds();
            
            // Draw background
            spriteBatch.FillRectangle(bounds, _backgroundColor);
            
            // Draw health fill
            var healthPercentage = _maxHealth > 0 ? _currentHealth / _maxHealth : 0;
            var fillWidth = bounds.Width * healthPercentage;
            var fillBounds = new Rectangle(bounds.X, bounds.Y, (int)fillWidth, bounds.Height);
            
            spriteBatch.FillRectangle(fillBounds, _healthColor);
            
            base.Draw(spriteBatch);
        }
    }
    
    // Usage
    var playerHealthBar = new HealthBar
    {
        Position = new Vector2(20, 20),
        Size = new Vector2(200, 20),
        MaxHealth = 100,
        CurrentHealth = 80,
        OnHealthChanged = (health) => 
        {
            if (health <= 0)
                ShowGameOverScreen();
        }
    };
    

    Component Composition

    Combine multiple components to create complex UI elements:

    public class PlayerInfoPanel : Panel
    {
        private Label _nameLabel;
        private HealthBar _healthBar;
        private ProgressBar _experienceBar;
        private Label _levelLabel;
        
        public string PlayerName
        {
            get => _nameLabel.Text;
            set => _nameLabel.Text = value;
        }
        
        public float Health
        {
            get => _healthBar.CurrentHealth;
            set => _healthBar.CurrentHealth = value;
        }
        
        public int Level { get; set; }
        
        public PlayerInfoPanel()
        {
            Size = new Vector2(300, 120);
            BackgroundColor = new Color(0, 0, 0, 128);
            
            CreateChildComponents();
            LayoutComponents();
        }
        
        private void CreateChildComponents()
        {
            _nameLabel = new Label
            {
                Text = "Player",
                TextColor = Color.White,
                Font = GameFonts.DefaultFont
            };
            
            _healthBar = new HealthBar
            {
                MaxHealth = 100,
                CurrentHealth = 100
            };
            
            _experienceBar = new ProgressBar
            {
                MaxValue = 1000,
                FillColor = Color.Yellow
            };
            
            _levelLabel = new Label
            {
                Text = "Level 1",
                TextColor = Color.Gold,
                Font = GameFonts.SmallFont
            };
            
            AddChild(_nameLabel);
            AddChild(_healthBar);
            AddChild(_experienceBar);
            AddChild(_levelLabel);
        }
        
        private void LayoutComponents()
        {
            _nameLabel.Position = new Vector2(10, 10);
            _levelLabel.Position = new Vector2(200, 10);
            _healthBar.Position = new Vector2(10, 40);
            _healthBar.Size = new Vector2(280, 20);
            _experienceBar.Position = new Vector2(10, 70);
            _experienceBar.Size = new Vector2(280, 15);
        }
    }
    

    Best Practices

    Performance

    1. Object pooling: Reuse UI components to reduce garbage collection
    2. Visibility culling: Don't update or draw invisible components
    3. Batch rendering: Group similar UI elements for efficient rendering
    4. Minimize hierarchy depth: Keep UI trees reasonably shallow

    Accessibility

    1. Keyboard navigation: Support tab order and keyboard shortcuts
    2. Screen reader support: Provide text alternatives for visual elements
    3. High contrast: Support high contrast themes
    4. Scalable text: Allow text size adjustments

    Design

    1. Consistent spacing: Use consistent margins and padding
    2. Visual hierarchy: Use size, color, and position to show importance
    3. Feedback: Provide visual and audio feedback for interactions
    4. Responsive layout: Support different screen sizes and resolutions

    The UI component system provides a flexible foundation for creating rich, interactive user interfaces in your Popcorning Guinea Pig games.

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