Skip to content

Core.Utilities.EdgeDetector

Dennis Steffen edited this page Jan 4, 2026 · 1 revision

In game development, the Edge Detector pattern is essential for handling input and state changes. While it shares logic with PLC (machine) programming—where it is used to handle continuous scan cycles via R_TRIG and F_TRIG blocks—in your engine, it serves to turn continuous data (like "is this key held down?") into discrete events (like "was this key just tapped?").

1. Explanation of the Logic

The EdgeDetector monitors a boolean value across two consecutive frames:

  • Rising Edge (IsRisingEdge): Becomes true only on the exact frame an input changes from false to true. This is a "Button Down" event.
  • Falling Edge (IsFallingEdge): Becomes true only on the exact frame an input changes from true to false. This is a "Button Up" (release) event.
  • The "Memory" (_previous): The class stores the state of the previous frame. Without this memory, the engine wouldn't know if a true value is a new press or just the user continuing to hold the button.

2. Usage Cases in Game Development

  • Jumping: You want the player to jump once when they press the button. If you didn't use a Rising Edge, the player would jump every single frame the button is held, resulting in "moon gravity" or infinite jumping.
  • Menu Navigation: When navigating a list, you want one "click" to move the selection down one item. Edge detection prevents the selection from flying to the bottom of the list instantly.
  • Weapon Fire:
    • Semi-auto: Use IsRisingEdge to fire one bullet per click.
    • Charging: Start charging on IsRisingEdge and release the projectile on IsFallingEdge.
  • Toggling UI: Opening and closing a pause menu or a map with a single button.
  • Animation Triggers: Detecting the moment a "Ground Sensor" becomes false to trigger a falling animation.

3. C# Example: Player Combat Logic

Here is how you might use the EdgeDetector logic to handle different weapon behaviors in a game:

public class CombatSystem
{
    private EdgeDetector _attackDetector = new();
    
    // Called every frame
    public void Update(bool isAttackButtonHeld)
    {
        // Update the detector with the raw input state
        _attackDetector.Update(isAttackButtonHeld);

        // 1. Rising Edge: Triggered the moment the button is pressed
        if (_attackDetector.IsRisingEdge)
        {
            PlaySwordSwingSound();
            StartChargingHeavyAttack();
        }

        // 2. Continuous State: (Standard boolean logic, not part of EdgeDetector)
        if (isAttackButtonHeld)
        {
            ShowChargingParticleEffects();
        }

        // 3. Falling Edge: Triggered the moment the button is released
        if (_attackDetector.IsFallingEdge)
        {
            ReleaseHeavyAttack();
            StopChargingEffects();
        }
    }

    private void PlaySwordSwingSound() => Console.WriteLine("Swoosh!");
    private void StartChargingHeavyAttack() => Console.WriteLine("Charging...");
    private void ReleaseHeavyAttack() => Console.WriteLine("Boom! Attack Released.");
    private void StopChargingEffects() => Console.WriteLine("Effects Off.");
}

4. Summary for the Engine Context

As seen in your BaseInput.cs, your engine uses this to populate the IsPressed property. This allows your game logic to distinguish between Down (the button is currently at the bottom) and IsPressed (the button was just clicked this frame). This distinction is the difference between a character walking (continuous Down) and a character jumping (discrete IsPressed).

Clone this wiki locally