Documentation

Idle Factory

A framework for idle, clicker and tycoon games in Unity: describe your game with assets and get production chains, offline progress, saving, prestige, managers, rewarded-ad boosts, a drop-in UI and editor tools for balancing.

  • Version 1.0.0
  • Unity 6000.0+
  • Platforms Android, iOS, WebGL, Desktop
  • Updated Oct 10, 2026
Idle Factory

Overview

Idle Factory is a framework for idle, clicker and tycoon games in Unity. You describe your game with assets (resources, production nodes, upgrades, boosts), drop one component into a scene, and get production chains, offline progress, saving, prestige, managers, rewarded-ad boosts, a drop-in UI and editor tools for balancing. Everything lives under the namespace HardArtcore.IdleFactory.

Requirements

  • Unity 6000.0 LTS or newer. Tested on 6000.0.84f1 (editor, play mode, WebGL and Android IL2CPP ARM64 builds) and 6000.6.2f1 (clean import, package tests, WebGL build).
  • Any render pipeline. Nothing in the package depends on Built-in, URP or HDRP.
  • Plain C# with no platform-specific code except the optional notifications module (Android/iOS). On WebGL saves go to PlayerPrefs automatically.

The core needs no packages. Optional parts switch themselves on when their package is present and are silently left out otherwise (the project still compiles without errors or warnings):

PartNeeds
Drop-in UI components, HUD prefab, Tycoon democom.unity.ugui 2.0+ (includes TextMeshPro in Unity 6) Optional
Factory demoUI Toolkit module (com.unity.modules.uielements, on by default) Optional
OfflineNotificationModuleMobile Notifications com.unity.mobile.notifications 2.3+ Optional
Included testsTest Framework com.unity.test-framework Optional
TextMeshPro

The first time you open a scene with the HUD, Unity offers to import TMP Essential Resources. Accept it (or use Window › TextMeshPro › Import TMP Essential Resources). The fonts are Unity's and are not shipped with this package.

Quick start

The 30-second way

  1. Open the wizard Window › Idle Factory › New Idle Game...
  2. Create Type a name and press Create.
  3. Press Play Tap, buy, unlock, hire managers, watch a (simulated) ad for a boost.

The wizard creates a folder with a GameConfig, three producers with milestones, upgrades, manager hires, a boost, clicking, and a scene containing the IdleGameRunner, PrestigeModule, AutomationModule, SimulatedRewardedAdProvider and the HUD prefab. Edit those assets to make the game yours.

New Idle Game wizard
The New Idle Game wizard.

By hand

  1. Resources Create › Idle Factory › Resource for each currency or material (e.g. Coins, Ore).
  2. Producers Create › Idle Factory › Production Node for each producer. Set its Output, and Inputs if it converts one resource into another.
  3. Upgrades and boosts Optionally Create › Idle Factory › Upgrade and Boost.
  4. Game config Create › Idle Factory › Game Config and add everything to its lists. Set a Click Resource for tapping.
  5. Runner Create an empty GameObject, add IdleGameRunner and assign the config.
  6. UI Drag Runtime/UI/Prefabs/IdleFactoryHUD into the scene (or build your own UI, see Drop-in UI).
  7. Press Play
Tip

Open the config's inspector and press Open in Idle Factory window to check it for mistakes.

What is in the package

HardArtcore/IdleFactory/
├── Runtime/
│   ├── Core/            Plain C# engine (no UnityEngine): IdleGame, GameDefinition, GameState, EconomySimulator
│   ├── Config/          ScriptableObjects: GameConfig, ResourceDefinition, ProductionNodeConfig, UpgradeDefinition, BoostDefinition
│   ├── UI/              Drop-in uGUI components and prefabs (needs uGUI/TextMeshPro)
│   ├── Notifications/   OfflineNotificationModule (needs Mobile Notifications)
│   └── ...              IdleGameRunner, save storages, PrestigeModule, AutomationModule, rewarded-ad bridge
├── Editor/              Config Inspector window, New Idle Game wizard
├── Samples/
│   ├── FactoryDemo/     Production-chain demo with validation tools (UI Toolkit)
│   └── TycoonDemo/      Business-tycoon demo built from the drop-in UI (uGUI)
├── Tests/               Edit-mode and play-mode tests
└── Documentation/       Documentation and changelog

Each folder is its own assembly definition, so you can delete any optional part (UI, notifications, samples, tests) without touching the rest.

Core concepts

Resources

A ResourceDefinition is anything that can be counted: coins, ore, gems. It has an Id (used in save files, so keep it stable), a display name, icon, color, a starting amount and an optional cap (MaxAmount, 0 = unlimited). Production that would exceed the cap is lost.

Production nodes

A ProductionNodeConfig produces BaseOutputAmount of its Output every BaseProductionTime seconds, consuming its Inputs per cycle. A node without inputs is a generator.

Order is automatic
Producers always run before the nodes that consume their output, whatever order you list them in.
Waiting for inputs
A node that finished a cycle but lacks inputs waits with one cycle ready ("blocked") and does not store up extra cycles, so there is no burst when inputs return.
Unlocking
StartsUnlocked, or pay UnlockCost.
Levels
Pay LevelCost (price = BaseCost × Growth^level). Each level above 1 adds OutputPerLevel of the base output (1 = output grows linearly with level, like most tycoon games). MaxLevel 0 = unlimited.
Milestones
Permanent bonuses at level thresholds, e.g. level 25 doubles speed. An effect with an empty target applies to the node itself.

Formulas, per node:

cycle time      = BaseProductionTime / Speed
output / cycle  = BaseOutputAmount x (1 + OutputPerLevel x (level - 1)) x Output
inputs / cycle  = input amount x InputCost
unlock & level prices x PurchaseCost (of that node)
Production chains
Production chains: ore is mined, smelted into ingots and pressed into gears.

Upgrades and effects

An UpgradeDefinition is bought in levels like a node (price curve, max level) and carries a list of effects. Every bonus in the system (upgrades, milestones, boosts, prestige) is an effect:

FieldMeaning
StatSpeed, Output, InputCost, PurchaseCost or ClickPower
OpAdd: +PerLevel per level (0.1 = +10%). Multiply: × PerLevel per level (2 = doubles each level)
TargetA node, or empty for every node (upgrades, boosts). Milestones: empty = their own node

All effects on a stat combine as (1 + sum of Add) × product of Multiply, floored at 0. Milestones, boosts and code-added modifiers count as level 1. To make something cheaper use a negative Add (-0.1 = 10% cheaper per level) or a Multiply below 1. Upgrade prices use the game-wide PurchaseCost.

Boosts

A BoostDefinition applies its effects for DurationMinutes. Activating it again adds time, capped at MaxDurationMinutes (0 = no cap). Boosts keep running during offline catch-up and survive prestige. They are usually granted by rewarded ads.

Clicking

Set Click Resource and Click Amount on the GameConfig. Each IdleGame.Click() adds ClickAmount × ClickPower; with CritChance (0..1) it is multiplied by CritMultiplier. Leave Click Resource empty for a pure idle game; the HUD hides its tap button.

Offline progress and time

The game remembers when the simulation was last up to date (GameState.LastSeenUtc). On startup, and whenever the real clock jumps more than 2 seconds ahead of it (app resumed on mobile, laptop woke up, editor paused, long freeze), the runner simulates the missed time:

  • capped at the config's Max Offline Hours;
  • using the same simulation as live play, in 1-second steps, so offline earnings always match what playing would have produced, including resource caps, input shortages, boosts running out and managers buying;
  • the result is an OfflineReport (seconds simulated, gain per resource) passed to IdleGameRunner.OfflineProgressApplied and kept in IdleGameRunner.LastOfflineReport.

IdleGame.Advance(seconds) runs the same catch-up on demand, e.g. for "time warp" rewards. IdleGame.ClaimBonus(report, 2) pays a report's gains again once ("watch an ad to double").

Clock cheating

Offline time comes from the device clock by default. Set IdleGame.NowUtc to a function returning server time (Unix seconds) to prevent players from moving their clock forward. Moving it backwards earns nothing.

Saving

  • The runner autosaves every AutoSaveInterval seconds, when the app is paused (mobile) and on quit.
  • The whole save is one GameState object as JSON. Content is matched by Id when loading, so you can add, remove and reorder resources, nodes, upgrades and boosts in updates without breaking saves.
  • Storage: files in Application.persistentDataPath/<SaveKey>.json (written to a temp file, then swapped in; the previous save is kept as <SaveKey>.bak.json). On WebGL, PlayerPrefs.
  • Corrupt saves are never overwritten silently: the unreadable file is copied to <SaveKey>.corrupt and the backup is loaded instead.
  • Tamper detection: set Signing Secret on the runner to sign saves (HMAC-SHA256). Edited saves are rejected and the backup is used.
  • Cloud saves or encryption: subclass IdleGameRunner and override CreateStorage() to return your own ISaveStorage (Load, Save, Delete of a string by key).
  • Renaming Ids: override MigrateSave(GameState); it runs for every save before it loads. Rename entries in ResourceIds, Nodes[i].Id, UpgradeIds or BoostIds there (renaming an Id that is not present is harmless). To migrate other data, keep your own version marker in a module's save data.
  • Multiple slots: set IdleGameRunner.SaveKey.
Important

The signing secret ships in your build, so it deters casual editing; it is not real security. Turning it on later invalidates existing unsigned saves unless you construct SignedSaveStorage yourself with acceptUnsigned: true.

Modules

Modules are components placed next to the IdleGameRunner. They are ticked by the simulation (so they also work during offline catch-up) and save their own data.

PrestigeModule
Resets progress for permanent points. Points = floor(sqrt(earned this run / Threshold)) of the measured resource; each point adds OutputBonusPerPoint to all output. Call Prestige() from UI (the HUD's Prestige tab does).
AutomationModule
Managers. List Node + HiredBy (an upgrade; empty = hired from the start). Hired, enabled managers unlock and level their node whenever it is affordable, spending at most Budget Share of the funds per round so the player keeps a reserve. Players can toggle each manager (the node card's manager button).
OfflineNotificationModule
Needs Mobile Notifications. When the app goes to the background on Android or iOS it schedules "production stopped" for the moment offline earnings hit the cap, plus an optional reminder. Everything is cancelled when the player returns. On other platforms and in the editor it does nothing, so the same scene builds everywhere.

Your own module

Implement IIdleModule:

DailyGiftModule.cs
using HardArtcore.IdleFactory;
using UnityEngine;

public class DailyGiftModule : MonoBehaviour, IIdleModule
{
    [SerializeField] ResourceDefinition _currency;
    IdleGame _game;
    long _lastClaimDay;

    public string Id => "daily_gift";
    public void Initialize(IdleGame game) => _game = game;
    public void Tick(double deltaTime) { }
    public string Save() => _lastClaimDay.ToString();
    public void Load(string data) => _lastClaimDay = data == null ? 0 : long.Parse(data);

    public bool CanClaim => _game.NowUtc() / 86400 > _lastClaimDay;

    public void Claim()
    {
        if (!CanClaim) return;
        _lastClaimDay = _game.NowUtc() / 86400;
        _game.AddResource(_game.Definition.IndexOfResource(_currency.Id), 500);
    }
}

Modules can also add temporary or permanent bonuses with game.SetModifiers("my_source", effects) and remove them with RemoveModifiers.

Rewarded ads

The package does not bundle an ad SDK. Boost buttons and the offline popup's "x2" button talk to a RewardedAdProvider on the runner's GameObject:

MyAdProvider.cs
using System;
using HardArtcore.IdleFactory;

public class MyAdProvider : RewardedAdProvider
{
    public override bool IsReady => /* your SDK: is a rewarded ad loaded? */ true;

    public override void Show(Action<bool> onFinished)
    {
        // Show the ad with your SDK (LevelPlay, Unity Ads, AdMob...) and call
        // onFinished(true) only when the player earned the reward, onFinished(false) otherwise.
    }
}
Before release

SimulatedRewardedAdProvider waits one second and grants the reward (untick Grant Reward to test skips). Replace it before release. Without any provider, boosts and the bonus are free.

Drop-in UI (uGUI + TextMeshPro)

Runtime/UI/Prefabs/IdleFactoryHUD is a complete HUD for phones and browsers: resource bar, tap button with floating numbers, x1/x10/x100/Max toggle, and Production / Upgrades / Boosts / Prestige tabs, plus the welcome-back popup. Its lists fill themselves from the runner's GameConfig. Restyle the prefabs freely; every reference on the components is optional except the asset itself.

The HUD adapts to the screen: AdaptiveLayout keeps it inside Screen.safeArea (notches, gesture bars) and stacks one compact header row (resources, tap button, buy mode) above the tabs on every screen (phones, desktop, WebGL). Cards and buttons use Runtime/UI/Sprites/RoundedRect.png, a 9-sliced sprite whose corner radius you set per Image with Pixels Per Unit Multiplier (radius = 48 / multiplier).

The HUD in portrait and landscape
The same HUD on a phone and in a wide browser window.
ComponentShows / does
ResourceViewAmount, cap, per-second rate, name, icon
NodeViewName, level, recipe, progress, status, unlock/level button, next milestone, manager toggle
UpgradeViewName, description, level, effect, buy button (optionally hidden when maxed)
BoostViewEffect, time left, "watch ad" button
PrestigeViewPoints, bonus, progress to next point, reset button
ClickButton + FloatingNumbersTapping with rising "+12" / "CRIT" numbers
OfflinePopupWelcome-back earnings and the "watch ad: x2" bonus
BuyModeButtonCycles the shared purchase amount (BuyMode.Count)
IdleListSpawnerSpawns one view prefab per resource, node, upgrade or boost
IdleTabsMinimal tab bar
AdaptiveLayoutSafe area + header row stacked above the content
EnsureEventSystemAdds an EventSystem with the right input module (legacy or Input System)

Your own widgets

Subclass IdleView, override OnBound() (once, when the game exists) and Refresh() (every frame), and use Game, Config and Runner. Views find the runner through IdleGameRunner.Instance, so they work in any scene as long as a runner exists.

Prefer UI Toolkit? The Factory demo builds its whole UI in code with UI Toolkit; use FactoryDemoUI.cs as a reference.

Editor tools

Window › Idle Factory › Config Inspector (or the button on any GameConfig):

Validate
Errors that stop the game from starting (missing outputs, duplicate Ids, resources not listed in the config, invalid prices) and design warnings: resources nothing produces but something costs, resources never spent, production loops, effects that do nothing.
Production Graph
Every node laid out by chain depth with resource flows; click a node to select it.
Economy Simulator
Plays your config for up to 48 hours in a fraction of a second with a simple bot (unlock when affordable, otherwise buy the cheapest level or upgrade; optional clicks per second). Shows when each node unlocks, a log-scale chart per resource, final amounts and every purchase. Use it to spot where progress stalls before playtesting. Prestige, managers and boosts are not simulated.

Window › Idle Factory › New Idle Game... opens the starter wizard from the Quick start.

Config Inspector validation
Validation and design lint.
Production graph
The production graph.
Economy simulator
The economy simulator: unlock times, a log-scale chart and every purchase.

Scripting

Get the game from the runner: var game = IdleGameRunner.Instance.Game;. Everything is addressed by index, matching the GameConfig lists (config.IndexOf(asset), or game.Definition.IndexOfResource("coins")).

AreaAPI
ResourcesGetAmount(r), AddResource(r, amount) (respects caps)
NodesCanUnlock, TryUnlock, LevelUpCost(n, count), MaxAffordableLevels, TryLevelUp(n, count), CycleTime, OutputPerCycle, IsBlocked
UpgradesGetUpgradeLevel, UpgradeCost(u, count), MaxAffordableUpgrades, TryBuyUpgrade(u, count)
BoostsActivateBoost, IsBoostActive, BoostTimeLeft
ClickingClick() returns amount and whether it was critical
TimeAdvance(seconds), ApplyOfflineTime(), ClaimBonus(report, multiplier), NowUtc
BonusesGetStat(stat, node) (node -1 = game-wide), SetModifiers(source, effects), RemoveModifiers(source)
ModulesAddModule, GetModule<T>()
StateSave(), Load(state), ResetProgress() (prestige), State
EventsChanged (every frame and after any change), NodeUnlocked, NodeLevelChanged, UpgradeBought, MilestoneReached, BoostStarted, BoostEnded, Clicked, Produced (live play only), ProgressReset

Runner: Instance, Game, Config, SaveKey, Save(), Reload(), DeleteSaveAndRestart(), LastOfflineReport, OfflineProgressApplied, and the overridable CreateStorage() and MigrateSave().

The engine itself (Runtime/Core) has no Unity dependency. You can build a GameDefinition in code, run new IdleGame(definition) on a server to validate progress, or unit-test your economy outside Unity.

Demos

Both demos save to their own slot, so they never touch your game's save.

Tycoon
Samples/TycoonDemo/TycoonDemo.unity (uGUI). Six businesses with milestones, managers hired via upgrades, tapping with crits, two ad boosts, prestige ("investors") and the welcome-back popup. It is built entirely from the drop-in prefabs and shows what you get without writing UI code.
Factory
Samples/FactoryDemo/FactoryDemo.unity (UI Toolkit). A production chain where nodes compete for ore, with every upgrade type, milestones, a boost and prestige. Its Validation tools panel lets you check the engine by hand: time warp (+1 min, +1 h), simulated absence (2 h, and 24 h to see the cap), +1K coins, boost activation, and save / reload / wipe with the live save path and autosave timer.
Tycoon demo
The Tycoon demo, built only from the drop-in prefabs.
Factory demo with validation tools
The Factory demo and its validation tools.

Tests

With the Test Framework installed, Window › General › Test Runner lists the package tests: the engine (simulation, offline matching live play, saves and remapping, purchases, effects, milestones, boosts, clicks, the simulator, the linter), the Unity layer (config conversion, file storage, signed saves, managers) and a play-mode test of the runner. Run them after changing the engine; they finish in seconds.

Limits and FAQ

How big can numbers get?
Amounts are double: exact enough for idle games up to about 1e308, with suffixes up to 1e90 and scientific notation beyond.
Is offline progress exact?
It runs the live simulation in 1-second steps. Results match live play to within a cycle (an included test compares an hour of frame-by-frame play with one hour of catch-up). The cost grows with the number of nodes and steps; for very large configs, lower Max Offline Hours.
Can I use the Input System only?
Yes. EnsureEventSystem adds the Input System UI module when the legacy input manager is disabled.
Enter Play Mode without domain reload?
Supported: the package's static state resets when play starts.
Does it work on WebGL?
Yes; saves use PlayerPrefs there. Gzip builds should enable Decompression Fallback if your host does not serve compressed files.

Release notes

1.0.0

  • Engine: resources, production chains with automatic ordering, levels, unlocks, upgrades with stackable effects, milestones, boosts, clicking with crits, prestige reset.
  • Offline progress that runs the live simulation, with a configurable cap and catch-up after any pause.
  • Saving: id-based loading that survives content updates, atomic files with backup, corrupt-save recovery, optional tamper signing, PlayerPrefs on WebGL, pluggable storage and migrations.
  • Modules: prestige, managers (automation), offline notifications.
  • Rewarded-ad bridge for boosts and doubled offline earnings.
  • Drop-in uGUI + TextMeshPro components and a complete HUD prefab.
  • Editor: Config Inspector (validation, design lint, production graph, economy simulator) and New Idle Game wizard.
  • Demos: Tycoon (uGUI) and Factory (UI Toolkit, with validation tools). Both adapt to portrait and landscape, so they run as Android apps and in the browser (WebGL).
  • Edit-mode and play-mode tests.

Need help?

Questions, bug reports or feature requests: I usually reply within a day.

support@hardartcore.com

Last updated October 10, 2026.