Persistent Discovery-Based Progression (Pro)
Framework for implementing persistent discovery-based progression (pro) in games, covering the core loop, edge cases, and integration points.
Overview
The persistent discovery-based progression (pro) mechanic provides a framework that creates a structured experience around this game element. The implementation varies significantly across genres, with each game adapting the core concept to fit its specific design goals and target audience. The ongoing evolution of this mechanic reflects the broader maturation of game design as a discipline.
Game Examples
Racing Games
Racing Games use this mechanic where players react to emergent situations to collect all available items. The feedback loop reinforces player engagement, resulting in risk-reward tension.
Cooperative Games
Cooperative Games use this mechanic where players weigh competing priorities to discover hidden content. Accessibility options allow different skill levels to participate, resulting in a deeply engaging gameplay loop.
Pros & Cons
Advantages
- Adds depth without excessive complexity
- Adds accessibility without excessive complexity
- Provides long-term collection objectives for dedicated players
- Easy to understand but difficult to master
Disadvantages
- Can create overwhelming when RNG is unfavorable
- Can create balance issues if not carefully balanced
- Difficult to balance across a wide range of skill levels
- Can become obsolete in the late game
Implementation Patterns
Cascading Skill Tree Handler
A modular approach to persistent discovery-based progression (pro) that separates concerns and enables easy testing.
const abilityTree = {
nodes: [
{ id: "basic_strike", cost: 1, requires: [], effect: "+10% damage" },
{ id: "advanced", cost: 5, requires: ["basic_strike"], effect: "+25% damage, unlock combo" },
{ id: "master_skill", cost: 8, requires: ["advanced"], effect: "+50% damage, unlock ultimate" },
],
canUnlock(nodeId: string, points: number, unlocked: Set<string>) {
const node = this.nodes.find(n => n.id === nodeId);
if (!node || unlocked.has(nodeId)) return false;
return points >= node.cost
&& node.requires.every(r => unlocked.has(r));
}
};