Idle Clicker Demo
Overview
A short arcade run that integrates Event Channels, Variables, Runtime Sets, and Delegate Objects (ActionSO) in one game loop. It is the only sample that exercises ActionSO.
Smash shapes on the board for cash. Bills arrive on a timer, and every unpaid bill raises the collector’s cut, which is taken out of everything you earn. Paying a bill runs the perk attached to it. Stamina never regenerates, so the run ends when it reaches zero.
Features Used
| Feature | Asset / Class | Description |
|---|---|---|
| Runtime Set | ActiveTargets (TargetRuntimeSetSO) | Targets register themselves while alive |
| Variable | Stamina, MaxStamina, Cash, CollectorRate | Global run state |
| Event Channel | OnStaminaChanged, OnCashChanged, OnCollectorRateChanged | Paired change channels owned by the Variables |
| Event Channel | OnSmashed, OnBillIssued, OnBillPaid, OnRunEnded | Run notifications |
| Action | BankPayout (ActionSO<int>) | Applies the collector’s cut to a payout, then banks it |
| Action | DeepBreathPerk, WindfallPerk, NegotiatorPerk, SecondWindPerk | Perks unlocked by paying bills |
| Data | BillSO assets | Each bill owns the ActionSO paying it unlocks |
The two rules this sample demonstrates
A Variable owns its change channel
VariableSO<T> holds a serialized EventChannelSO<T>, and the Value setter raises it. A writer therefore only assigns Value:
// Correct: the Variable raises OnCashChanged by itself
cash.Value += net;
// Wrong: fires the channel twice
cash.Value += net;
onCashChanged.RaiseEvent(cash.Value);
Consumers subscribe to the channel asset and bootstrap the initial value from the Variable in OnEnable.
Instance data stays in C# fields
A target’s kind, remaining hits, and lifetime are unique per object, so they are plain fields on the Target component. Only the set of live targets is shared, so only that is a ScriptableObject:
public class Target : MonoBehaviour
{
[SerializeField] private TargetRuntimeSetSO activeTargets; // global
public int Hits { get; private set; } // per instance
private void OnEnable() => activeTargets?.Add(this);
private void OnDisable() => activeTargets?.Remove(this);
}
Putting Hits in an IntVariableSO would make every target on screen share one value.
Perks are assets, not code
Every run modifier is a Variable, so a perk is an action asset that adds a clamped delta to one:
| Perk | Composition |
|---|---|
| Deep Breath | SequenceActionSO: raise MaxStamina, then restore Stamina |
| Windfall | Add to Cash |
| Negotiator | Subtract from CollectorRate, clamped at 0 |
| Second Wind | Add to Stamina |
Adding a perk means authoring an asset and dropping it on a BillSO. No new code.
How to Use
- Open the
IdleClickerscene - Enter Play Mode
- Click a shape on the left to smash it. Blue is cheap, green pays more, purple takes three hits, red is a Decoy - hitting it issues another bill
- A swing that hits nothing still costs 1 stamina
- Click PAY on a bill once you can afford it, and watch its perk change a Variable
- Leave a bill unpaid and watch
COLLECTORrise and your income shrink
Use Cases
| Use Case | Example |
|---|---|
| Data-driven rewards | Quest or loot rewards authored as ActionSO assets on a data asset |
| Configurable modifiers | Buffs that clamp a shared stat without new code per effect |
| Decoupled HUD | Readouts that must not reference the system producing the value |
| Active object tracking | Spawned objects that register themselves instead of being searched for |