
Godot Genre Idle Clicker
- 161 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-genre-idle-clicker for development tasks
About
godot-genre-idle-clicker: A skill for development. This provides functionality for development workflows.
- godot-genre-idle-clicker
Godot Genre Idle Clicker by the numbers
- 161 all-time installs (skills.sh)
- +8 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,332 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-genre-idle-clickerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 161 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-genre-idle-clicker for development tasks
Files
Genre: Idle / Clicker
Expert blueprint for idle/clicker games with exponential progression and prestige mechanics.
NEVER Do (Expert Anti-Patterns)
Economics & Math
- NEVER use standard floats for currency; strictly implement a BigNumber (Mantissa/Exponent) system (e.g.,
1.5e300) to preventINFcrashes at 1e308. - NEVER use
Timernodes for revenue generation; strictly use a manual accumulator in_process(delta)to prevent drift during frame fluctuations. - NEVER hardcode generator costs or growth; strictly use an exponential formula:
Cost = BasePrice * pow(GrowthFactor, OwnedCount)(industry standard 1.15x). - NEVER evaluate exact float equality (
==); strictly useis_equal_approx()or>=to prevent "stuck" progress due to precision loss. - NEVER parse scientific notation strings with
to_int(); strictly useto_float()or a dedicated BigNumber parser.
Performance & Optimization
- NEVER update all UI labels every frame; strictly use Signals to update labels ONLY when values change, or throttle updates to 10 FPS.
- NEVER ignore Low Processor Usage Mode for mobile; strictly enable
OS.low_processor_usage_mode = trueto preserve battery life. - NEVER instantiate/delete hundreds of text nodes per second; strictly use Object Pooling or
MultiMeshInstancefor click-feedback. - NEVER update massive logs by modifying the
textproperty; strictly useappend_text()to prevent main thread blocking.
Player Experience & Persistence
- NEVER ignore Offline Progress; strictly calculate
seconds_offline * total_revenueusing system UNIX timestamps (Time.get_unix_time_from_system()). - NEVER make the "Prestige" reset feel like a loss; strictly provide a global multiplier that makes the next run significantly faster (2-5x).
- NEVER calculate offline time using
Time.get_ticks_msec(); strictly use Persistent UNIX timestamps as ticks reset on app restart. - NEVER use Node hierarchies for raw data; strictly use
RefCountedorResourceobjects for lightweight, serializable logic.
---
🛠 Expert Components (scripts/)
Original Expert Patterns
- big_number.gd - The foundation for handling e308+ scales using Mantissa + Exponent math.
- generator.gd - Generic template for exponential cost units and rate calculation.
- scientific_notation_formatter.gd - readable formatting for K, M, B, T suffixes and scientific notation.
Modular Components
- offline_progress_calculator.gd - Real-world delta tracking using UNIX timestamps.
- functional_income_reducer.gd - C++ optimized array reduction for fast income summation.
- threaded_catchup_simulator.gd - WorkerThreadPool background simulation patterns.
---
Core Loop
1. Click: Player performs manual action to gain currency. 2. Buy: Player purchases "generators" (auto-clickers). 3. Wait: Game plays itself, numbers go up. 4. Upgrade: Player buys multipliers to increase efficiency. 5. Prestige: Player resets progress for a permanent global multiplier.
Skill Chain
| Phase | Skills | Purpose |
|---|---|---|
| 1. Math | godot-gdscript-mastery | Handling numbers larger than 64-bit float |
| 2. UI | godot-ui-containers, labels | Displaying "1.5e12" or "1.5T" cleanly |
| 3. Data | godot-save-load-systems | Saving progress, offline time calculation |
| 4. Logic | signals | Decoupling UI from the economic simulation |
| 5. Meta | json-serialization | Balancing hundreds of upgrades via data |
Architecture Overview
1. Big Number System
Standard float goes to INF around 1.8e308. Idle games often go beyond. You need a custom BigNumber class (Mantissa + Exponent).
# big_number.gd
class_name BigNumber
var mantissa: float = 0.0 # 1.0 to 10.0
var exponent: int = 0 # Power of 10
func _init(m: float, e: int) -> void:
mantissa = m
exponent = e
normalize()
func normalize() -> void:
if mantissa >= 10.0:
mantissa /= 10.0
exponent += 1
elif mantissa < 1.0 and mantissa != 0.0:
mantissa *= 10.0
exponent -= 12. Generator System
The core entities that produce currency.
# generator.gd
class_name Generator extends Resource
@export var id: String
@export var base_cost: BigNumber
@export var base_revenue: BigNumber
@export var cost_growth_factor: float = 1.15
var count: int = 0
func get_cost() -> BigNumber:
# Cost = Base * (Growth ^ Count)
return base_cost.multiply(pow(cost_growth_factor, count))3. Simulation Manager (Offline Progress)
Calculating gains while the game was closed.
# game_manager.gd
func _ready() -> void:
var last_save_time = save_data.timestamp
var current_time = Time.get_unix_time_from_system()
var seconds_offline = current_time - last_save_time
if seconds_offline > 60:
var revenue = calculate_revenue_per_second().multiply(seconds_offline)
add_currency(revenue)
show_welcome_back_popup(revenue)Key Mechanics Implementation
Prestige System (Reset)
Resetting generators but keeping prestige_currency.
func prestige() -> void:
if current_money.less_than(prestige_threshold):
return
# Formula: Cube root of money / 1 million
# (Just an example, depends on balance)
var gained_keys = calculate_prestige_gain()
save_data.prestige_currency += gained_keys
save_data.global_multiplier = 1.0 + (save_data.prestige_currency * 0.10)
# Reset
save_data.money = BigNumber.new(0, 0)
save_data.generators = ResetGenerators()
save_game()
reload_scene()Formatting Numbers
Displaying 1234567 as 1.23M.
static func format(bn: BigNumber) -> String:
if bn.exponent < 3:
return str(int(bn.mantissa * pow(10, bn.exponent)))
var suffixes = ["", "K", "M", "B", "T", "Qa", "Qi"]
var suffix_idx = bn.exponent / 3
if suffix_idx < suffixes.size():
return "%.2f%s" % [bn.mantissa * pow(10, bn.exponent % 3), suffixes[suffix_idx]]
else:
return "%.2fe%d" % [bn.mantissa, bn.exponent]Godot-Specific Tips
- Timers: Do NOT use
Timernodes for revenue generation (drifting). Use_process(delta)and accumulate time. - GridContainer: Perfect for the "Generators" list.
- Resources: Use
.tresfiles to define every generator (Farm, Mine, Factory) so you can tweak balance without touching code.
Common Pitfalls
1. Floating Point Errors: Using standard float for money. Fix: Use BigNumber implementation immediately. 2. Boring Prestige: Resetting feels like a punishment. Fix: Ensure the post-prestige run is significantly faster (2x-5x speed). 3. UI Lag: Updating 50 text labels every frame. Fix: Only update labels when values actually change (Signal-based), or throttling updates to 10fps.
---
🚀 Elite Technical Implementations (Batch 09)
1. BigReal-Math-Structure (Handling > 1e308)
Idle games often exceed the limits of 64-bit floats (~1.8e308). Use a custom RefCounted class to store numbers in scientific notation (mantissa + exponent), allowing for virtually infinite growth.
class_name BigReal extends RefCounted
@export var mantissa: float = 0.0
@export var exponent: int = 0
func _init(m: float = 0.0, e: int = 0) -> void:
mantissa = m
exponent = e
_normalize()
func _normalize() -> void:
if mantissa == 0.0:
exponent = 0
return
while abs(mantissa) >= 10.0:
mantissa /= 10.0
exponent += 1
while abs(mantissa) < 1.0 and mantissa != 0.0:
mantissa *= 10.0
exponent -= 1
func multiply(other: BigReal) -> BigReal:
return BigReal.new(mantissa * other.mantissa, exponent + other.exponent)2. Multi-Offline-Progression Pattern
Calculate retroactively what the player earned while the game was closed using Time.get_unix_time_from_system(). Save the timestamp to user:// and compare it upon relaunch.
class_name OfflineProgressionManager extends Node
signal offline_earnings_calculated(seconds_offline: float)
const SAVE_PATH: String = "user://offline_save.json"
func _ready() -> void:
_process_offline_time()
func _process_offline_time() -> void:
var current_time = Time.get_unix_time_from_system()
var last_time = load_timestamp() # From FileAccess
var delta = current_time - last_time
if delta > 60.0:
offline_earnings_calculated.emit(delta)
func save_timestamp() -> void:
var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
file.store_string(JSON.stringify({"last_time": Time.get_unix_time_from_system()}))3. Particle-Batch-Juice (Manual Emission)
Spawning new nodes for click-juice is expensive. Use a single GPUParticles2D and call emit_particle() manually on every click to batch spawn particles directly at the mouse coordinates.
class_name ClickJuiceManager extends Node2D
@export var click_particles: GPUParticles2D
func _input(event: InputEvent) -> void:
if event.is_action_pressed(&"click"):
var click_pos = get_global_mouse_position()
_burst_particles(click_pos)
func _burst_particles(pos: Vector2) -> void:
for i in range(15):
var xform = Transform2D(0.0, pos)
var velocity = Vector2(randf_range(-200.0, 200.0), randf_range(-200.0, 200.0))
# Direct GPU emission bypasses SceneTree overhead
click_particles.emit_particle(xform, velocity, Color.WHITE, Color.WHITE, 0)- Master Skill: godot-master
# big_int_save_parser.gd
extends Node
# 64-bit Integer JSON Save Parser
# Safely handles quintillions of currency units during serialization into/from JSON.
func load_economic_state(json_string: String) -> void:
var data: Dictionary = JSON.parse_string(json_string)
# USE to_float() then int() for scientific notation strings like "1e20".
# to_int() is unsafe for exponential strings as it stops at the 'e'.
var raw_value = data.get("balance", "0")
if raw_value is String:
var balance: int = int(raw_value.to_float())
_apply_balance(balance)
func _apply_balance(_val: int) -> void: pass
# godot-master/scripts/idle_clicker_big_number.gd
class_name BigNumber
extends RefCounted
## Big Number System (Expert Pattern)
## Handles numbers larger than double-precision floats using (Mantissa, Exponent).
## e.g., 1.23e50
var mantissa: float = 0.0
var exponent: int = 0
func _init(m: float = 0.0, e: int = 0) -> void:
mantissa = m
exponent = e
normalize()
func normalize() -> void:
if mantissa == 0.0:
exponent = 0
return
while abs(mantissa) >= 10.0:
mantissa /= 10.0
exponent += 1
while abs(mantissa) < 1.0 and mantissa != 0.0:
mantissa *= 10.0
exponent -= 1
# Operations
func add(other: BigNumber) -> BigNumber:
var diff = exponent - other.exponent
if abs(diff) > 15:
# If difference requires >15 digits precision shift, smaller number is negligible
if diff > 0: return BigNumber.new(mantissa, exponent)
else: return BigNumber.new(other.mantissa, other.exponent)
var new_mantissa = mantissa + other.mantissa * pow(10, -diff)
return BigNumber.new(new_mantissa, exponent) # Normalize happens in init
func multiply(scalar: float) -> BigNumber:
return BigNumber.new(mantissa * scalar, exponent)
func multiply_bn(other: BigNumber) -> BigNumber:
return BigNumber.new(mantissa * other.mantissa, exponent + other.exponent)
func to_string_formatted() -> String:
if exponent < 3:
return str(int(mantissa * pow(10, exponent)))
elif exponent < 6:
return "%.2fK" % (mantissa * pow(10, exponent - 3))
elif exponent < 9:
return "%.2fM" % (mantissa * pow(10, exponent - 6))
else:
return "%.2fe%d" % [mantissa, exponent]
## EXPERT USAGE:
## var cost = BigNumber.new(1.5, 12) # 1.5 Trillion
## var new_cost = cost.multiply(1.15)
# decoupled_economy_signal_bus.gd
extends Node
# Decoupled Economy Signal Bus
# Ensures the internal simulation is completely independent from the visual UI layer.
signal currency_updated(total: float, delta: float)
var _total_currency: float = 0.0
func add_currency(amount: float) -> void:
_total_currency += amount
# Emit signal for any UI listeners to update themselves only when necessary.
currency_updated.emit(_total_currency, amount)
# functional_income_reducer.gd
extends Node
# Functional Array Reduction (High-Performance Math)
# Uses fast internal C++ loops to sum income from thousands of generators.
func get_total_income(generator_counts: Array[int], income_per_unit: Array[float]) -> float:
# Pattern: Map counts to income, then reduce to a single sum.
# reduce(func(accumulator, value), initial_value)
return generator_counts.reduce(func(total, current_count):
return total + (current_count * income_per_unit[generator_counts.find(current_count)]), 0.0)
# godot-master/scripts/idle_clicker_generator.gd
extends Resource
## Generator Resource (Expert Pattern)
## Represents a purchasable producer (e.g., Grandma, Mine).
## Calculates cost scaling exponentially.
class_name Generator
@export var id: String
@export var name: String
@export var base_cost_mantissa: float = 10.0
@export var base_cost_exponent: int = 0
@export var base_revenue_mantissa: float = 1.0
@export var base_revenue_exponent: int = 0
@export var cost_growth_factor: float = 1.15
var count: int = 0
func get_cost() -> BigNumber:
var growth_mult = pow(cost_growth_factor, count)
var base = BigNumber.new(base_cost_mantissa, base_cost_exponent)
return base.multiply(growth_mult)
func get_revenue_per_second() -> BigNumber:
var base = BigNumber.new(base_revenue_mantissa, base_revenue_exponent)
# Revenue = Base * Count (* Modifiers eventually)
return base.multiply(float(count))
func purchase() -> void:
count += 1
## EXPERT USAGE:
## Create .tres files for each generator.
## Use get_cost() to check affordability, purchase() to increment.
# godot-master/scripts/idle_clicker_big_number.gd
class_name BigNumber
extends RefCounted
## Big Number System (Expert Pattern)
## Handles numbers larger than double-precision floats using (Mantissa, Exponent).
## e.g., 1.23e50
var mantissa: float = 0.0
var exponent: int = 0
func _init(m: float = 0.0, e: int = 0) -> void:
mantissa = m
exponent = e
normalize()
func normalize() -> void:
if mantissa == 0.0:
exponent = 0
return
while abs(mantissa) >= 10.0:
mantissa /= 10.0
exponent += 1
while abs(mantissa) < 1.0 and mantissa != 0.0:
mantissa *= 10.0
exponent -= 1
# Operations
func add(other: BigNumber) -> BigNumber:
var diff = exponent - other.exponent
if abs(diff) > 15:
# If difference requires >15 digits precision shift, smaller number is negligible
if diff > 0: return BigNumber.new(mantissa, exponent)
else: return BigNumber.new(other.mantissa, other.exponent)
var new_mantissa = mantissa + other.mantissa * pow(10, -diff)
return BigNumber.new(new_mantissa, exponent) # Normalize happens in init
func multiply(scalar: float) -> BigNumber:
return BigNumber.new(mantissa * scalar, exponent)
func multiply_bn(other: BigNumber) -> BigNumber:
return BigNumber.new(mantissa * other.mantissa, exponent + other.exponent)
func to_string_formatted() -> String:
if exponent < 3:
return str(int(mantissa * pow(10, exponent)))
elif exponent < 6:
return "%.2fK" % (mantissa * pow(10, exponent - 3))
elif exponent < 9:
return "%.2fM" % (mantissa * pow(10, exponent - 6))
else:
return "%.2fe%d" % [mantissa, exponent]
## EXPERT USAGE:
## var cost = BigNumber.new(1.5, 12) # 1.5 Trillion
## var new_cost = cost.multiply(1.15)
# godot-master/scripts/idle_clicker_generator.gd
extends Resource
## Generator Resource (Expert Pattern)
## Represents a purchasable producer (e.g., Grandma, Mine).
## Calculates cost scaling exponentially.
class_name Generator
@export var id: String
@export var name: String
@export var base_cost_mantissa: float = 10.0
@export var base_cost_exponent: int = 0
@export var base_revenue_mantissa: float = 1.0
@export var base_revenue_exponent: int = 0
@export var cost_growth_factor: float = 1.15
var count: int = 0
func get_cost() -> BigNumber:
var growth_mult = pow(cost_growth_factor, count)
var base = BigNumber.new(base_cost_mantissa, base_cost_exponent)
return base.multiply(growth_mult)
func get_revenue_per_second() -> BigNumber:
var base = BigNumber.new(base_revenue_mantissa, base_revenue_exponent)
# Revenue = Base * Count (* Modifiers eventually)
return base.multiply(float(count))
func purchase() -> void:
count += 1
## EXPERT USAGE:
## Create .tres files for each generator.
## Use get_cost() to check affordability, purchase() to increment.
# godot-master/scripts/idle_clicker_scientific_notation_formatter.gd
extends Node
## Scientific Notation Formatter Expert Pattern
## Handles numbers up to 1e303 (Centillion) with human-readable suffixes.
const SUFFIXES = [
"", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc",
"UDc", "DDc", "TDc", "QaDc", "QiDc", "SxDc", "SpDc", "OcDc", "NoDc", "Vg"
]
func format_value(value: float) -> String:
# 1. Standard Case
if value < 1000:
return str(floor(value))
# 2. BigNum Logic
var exponent = floor(log(value) / log(1000))
var suffix_index = int(exponent)
if suffix_index < SUFFIXES.size():
var short_val = value / pow(1000, exponent)
# Professional formatting: 3 significant digits (e.g. 1.23M)
return "%.2f%s" % [short_val, SUFFIXES[suffix_index]]
else:
# 3. Scientific Fallback
# Beyond Vigintillion, use standard scientific notation.
var e_val = log(value) / log(10)
return "%.2fe%d" % [value / pow(10, floor(e_val)), int(e_val)]
## EXPERT NOTE:
## For performance in idle games with 100+ labels, CACHE the result
## and only re-calculate if the value has changed by > 1%.
## Use 'ColorRect' or 'Shader' for 'Damage Number' popups to avoid
## Label-instance overhead during "Click Storms".
# idle_performance_setup.gd
extends Node
# Low Processor Mode Setup
# Essential for mobile idle games to prevent excessive battery drain during background/menu time.
func setup_performance() -> void:
# Optimizes for low CPU usage by only refreshing the screen if needed.
OS.low_processor_usage_mode = true
# Increases the sleep time between frames (in microseconds) to further lower CPU overhead.
# 6900 corresponds to a slight delay that substantially reduces draw calls.
OS.low_processor_usage_mode_sleep_usec = 6900
# lightweight_upgrade_resource.gd
extends RefCounted
class_name ClickerUpgradeResource
# Lightweight RefCounted Data Structures
# Avoids the Node-tree overhead for non-visual upgrade logic.
var level: int = 0
var multiplier: float = 1.15
var base_cost: float = 10.0
func get_next_cost() -> float:
# Standard idle scaling formula: cost * (growth ^ level)
return base_cost * pow(multiplier, level)
func purchase() -> void:
level += 1
# offline_progress_calculator.gd
extends Node
# Calculating Offline Ticks
# Measures real-world time passed while the application was closed.
func calculate_offline_progress(last_save_unix_time: float) -> float:
# get_unix_time_from_system() returns persistent time since the Epoch.
# NEVER use get_ticks_msec() for offline progress as it resets on boot.
var current_time := Time.get_unix_time_from_system()
var delta_seconds := current_time - last_save_unix_time
# Returns raw seconds; usually passed to a simulation loop.
return delta_seconds
# precision_cost_validator.gd
extends Node
# Precision-Safe Cost Validation
# Accounts for IEEE 754 precision loss when comparing floating-point economy values.
func can_afford(currency: float, cost: float) -> bool:
# is_equal_approx prevents scenarios where 100.0000001 vs 100.0 causes a failed purchase.
# Pattern: Exact equality is unreliable; use approx or greater-than threshold.
return currency > cost or is_equal_approx(currency, cost)
# godot-master/scripts/idle_clicker_scientific_notation_formatter.gd
extends Node
## Scientific Notation Formatter Expert Pattern
## Handles numbers up to 1e303 (Centillion) with human-readable suffixes.
const SUFFIXES = [
"", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc",
"UDc", "DDc", "TDc", "QaDc", "QiDc", "SxDc", "SpDc", "OcDc", "NoDc", "Vg"
]
func format_value(value: float) -> String:
# 1. Standard Case
if value < 1000:
return str(floor(value))
# 2. BigNum Logic
var exponent = floor(log(value) / log(1000))
var suffix_index = int(exponent)
if suffix_index < SUFFIXES.size():
var short_val = value / pow(1000, exponent)
# Professional formatting: 3 significant digits (e.g. 1.23M)
return "%.2f%s" % [short_val, SUFFIXES[suffix_index]]
else:
# 3. Scientific Fallback
# Beyond Vigintillion, use standard scientific notation.
var e_val = log(value) / log(10)
return "%.2fe%d" % [value / pow(10, floor(e_val)), int(e_val)]
## EXPERT NOTE:
## For performance in idle games with 100+ labels, CACHE the result
## and only re-calculate if the value has changed by > 1%.
## Use 'ColorRect' or 'Shader' for 'Damage Number' popups to avoid
## Label-instance overhead during "Click Storms".
# scientific_notation_math.gd
extends Node
# Scientific Notation Formatter (Scale Management)
# Handles display for astronomical clicker currencies exceeding standard float precision limits.
func format_large_value(value: float) -> String:
# Switches to scientific notation after 1 Million.
if value > 1_000_000.0:
# String.num_scientific produces optimized "1.23e12" format natively.
return String.num_scientific(value)
# Standard string formatting for lower values.
return str(snappedf(value, 0.01))
# thread_safe_ui_updater.gd
extends Node
# Thread-Safe UI Update Pattern
# Safely propagates data from background simulations to main-thread UI nodes.
@onready var currency_label: Label = Label.new()
func update_display_deferred(new_value_string: String) -> void:
# NEVER set .text directly from a thread.
# call_deferred ensures the update happens on the next frame in the main thread.
currency_label.call_deferred("set_text", "Balance: " + new_value_string)
# threaded_catchup_simulator.gd
extends Node
# Threaded Offline Catch-Up (Scalability Optimization)
# Offloads heavy incremental math to a background thread to prevent UI freezing.
func process_offline_sim(total_ticks: int) -> void:
# WorkerThreadPool allows chunked processing across all CPU cores.
var task_id := WorkerThreadPool.add_group_task(_simulate_single_tick, total_ticks)
# Non-blocking wait if needed, or simply allow background completion.
WorkerThreadPool.wait_for_group_task_completion(task_id)
func _simulate_single_tick(_tick_index: int) -> void:
# Logic for individual tick simulation (e.g., compounding interest).
pass