Skip to content

Instantly share code, notes, and snippets.

@SteffenBlake
Created October 18, 2025 17:32
Show Gist options
  • Select an option

  • Save SteffenBlake/75c6bebeca0136229ae6300b9f2eb70f to your computer and use it in GitHub Desktop.

Select an option

Save SteffenBlake/75c6bebeca0136229ae6300b9f2eb70f to your computer and use it in GitHub Desktop.
// MIT License
// Copyright (c) 2025 Steffen Blake
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using R3;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Atomic.Net.MonoGame;
public class StackedSubject<T> : Observable<T>, ISubject<T>
{
private readonly List<Observer<T>> _subscribers = [];
public void OnNext(T value)
{
if (_subscribers.Count > 0)
{
_subscribers.Last().OnNext(value);
}
}
public void OnErrorResume(Exception error)
{
foreach (var subscriber in _subscribers)
{
subscriber.OnErrorResume(error);
}
}
public void OnCompleted(Result complete)
{
foreach (var subscriber in _subscribers)
{
subscriber.OnCompleted(complete);
}
}
protected override IDisposable SubscribeCore(Observer<T> observer)
{
_subscribers.Add(observer);
return Disposable.Create(() =>
{
_subscribers.Remove(observer);
observer.Dispose();
});
}
}
@SteffenBlake
Copy link
Author

Example code showing how I consume from a StackedSubject:

PauseModel.Instance!.ShowPauseMenu is an Observable bool that controls, well, its self documenting lol

InputModel.Instance!.CancelPressed is the StackedSubject that fires off whenever the cancel button has been pressed.

using Godot;
using R3;
using System;
using BurgerGame.Input;

namespace BurgerGame.PauseMenu;
public partial class PauseMenuContainer : CenterContainer
{
    private IDisposable? _subscription;
    public override void _Ready()
    {
        base._Ready();
        _subscription = PauseModel.Instance!.ShowPauseMenu.Subscribe(OnShowPauseMenu);
    }

    public override void _ExitTree()
    {
        base._ExitTree();
        _subscription?.Dispose();
    }

    IDisposable? _cancelSubscription;
    private void OnShowPauseMenu(bool showPauseMenu)
    {
        if (showPauseMenu)
        {
            _cancelSubscription = InputModel.Instance!.CancelPressed.Subscribe(
                (_) => PauseModel.Instance!.Paused.Value = false
            );
            Visible = true;
        }
        else
        {
            _cancelSubscription?.Dispose();
            _cancelSubscription = null;
            Visible = false;
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment