| name | cocos-level-designer |
| description | Use when working on level architecture, difficulty balancing, progression mechanics, and procedural content generation. |
Cocos Level Designer
Overview
This Codex skill adapts the upstream Cocos Creator Claude agent guidance for use in Codex.
Expert in level design, progression systems, and content creation for Cocos Creator games. Use this skill for level architecture, difficulty balancing, progression mechanics, and procedural content generation.
Expertise
- Level design principles and architecture
- Difficulty curve balancing and progression
- Procedural level generation algorithms
- Content management and level data structures
- Player guidance and flow design
- Replayability and variety systems
- Level editor tools and workflows
- Performance optimization for large levels
Usage Examples
Example 1: Level Progression System
Context: Need structured level progression
User: "Design a level progression system for puzzle game"
Assistant: "I will use $cocos-level-designer"
Commentary: Implements balanced difficulty curve with variety mechanics
Example 2: Procedural Generation
Context: Need endless content generation
User: "Create procedural level generation for endless runner"
Assistant: "I will use $cocos-level-designer"
Commentary: Designs algorithm that creates varied, balanced content
Example 3: Level Editor
Context: Need tool for level creation
User: "Build level editor for designers to create content"
Assistant: "I will use $cocos-level-designer"
Commentary: Creates intuitive editor with validation and testing features
Level Architecture
Level Data Structure
export interface LevelData {
id: string;
name: string;
difficulty: number;
category: 'tutorial' | 'normal' | 'challenge' | 'boss';
objectives: LevelObjective[];
winConditions: WinCondition[];
layout: LevelLayout;
entities: EntityData[];
collectibles: CollectibleData[];
requiredLevel: number;
unlocks: string[];
rewards: RewardData[];
maxEntities: number;
preloadAssets: string[];
targetCompletionTime: number;
difficultyRating: number;
}
@ccclass('LevelManager')
export class LevelManager extends Component {
@property
levels: LevelData[] = [];
@property
currentLevelIndex: number = 0;
private _currentLevel: LevelData = null;
private _levelProgress: LevelProgress = null;
async loadLevel(levelId: string): Promise<boolean> {
const levelData = this.levels.find(l => l.id === levelId);
if (!levelData) {
console.error(`Level ${levelId} not found`);
return false;
}
await this.preloadLevelAssets(levelData);
this._currentLevel = levelData;
this._levelProgress = this.initializeLevelProgress(levelData);
await this.setupLevelLayout(levelData.layout);
this.spawnLevelEntities(levelData.entities);
this.node.emit('level-loaded', levelData);
return true;
}
checkWinConditions(): boolean {
if (!this._currentLevel || !this._levelProgress) return false;
return this._currentLevel.winConditions.every(condition => {
return this.evaluateWinCondition(condition);
});
}
}
Difficulty Balancing
@ccclass('DifficultyManager')
export class DifficultyManager extends Component {
@property
baseDifficulty: number = 1.0;
@property
difficultyMultiplier: number = 0.1;
@property
playerSkillWeight: number = 0.3;
calculateLevelDifficulty(levelIndex: number, playerSkill: number): DifficultySettings {
const baseDiff = this.baseDifficulty + (levelIndex * this.difficultyMultiplier);
const skillAdjustment = (playerSkill - 0.5) * this.playerSkillWeight;
const adjustedDifficulty = Math.max(0.1, baseDiff - skillAdjustment);
return {
enemyHealth: adjustedDifficulty,
enemySpeed: 0.8 + (adjustedDifficulty * 0.4),
enemyCount: Math.floor(3 + adjustedDifficulty * 2),
timeLimit: Math.max(30, 120 - (adjustedDifficulty * 10)),
powerupFrequency: Math.max(0.1, 0.8 - (adjustedDifficulty * 0.2))
};
}
adaptDifficultyToPerformance(performance: PlayerPerformance): void {
if (performance.failureRate > 0.7) {
this.reduceDifficulty();
} else if (performance.successRate > 0.9 && performance.averageTime < 0.5) {
this.increaseDifficulty();
}
}
}
Procedural Generation
Algorithm Implementation
@ccclass('ProceduralLevelGenerator')
export class ProceduralLevelGenerator extends Component {
@property
templates: LevelTemplate[] = [];
@property
constraints: GenerationConstraints = {
minDifficulty: 1,
maxDifficulty: 10,
minSize: { width: 20, height: 20 },
maxSize: { width: 100, height: 100 }
};
generateLevel(seed: number, difficulty: number): LevelData {
const rng = new SeededRandom(seed);
const template = this.selectTemplate(difficulty, rng);
const layout = this.generateLayout(template, difficulty, rng);
const entities = this.placeEntities(layout, template, difficulty, rng);
const collectibles = this.placeCollectibles(layout, difficulty, rng);
const objectives = this.generateObjectives(template, entities, collectibles);
return {
id: `generated_${seed}`,
name: `Level ${seed}`,
difficulty,
category: 'normal',
layout,
entities,
collectibles,
objectives,
winConditions: template.winConditions,
rewards: this.calculateRewards(difficulty),
requiredLevel: Math.floor(difficulty),
unlocks: [],
maxEntities: entities.length * 2,
preloadAssets: template.requiredAssets,
targetCompletionTime: this.estimateCompletionTime(difficulty),
difficultyRating: difficulty
};
}
private generateLayout(
template: LevelTemplate,
difficulty: number,
rng: SeededRandom
): LevelLayout {
const size = this.calculateLevelSize(difficulty);
const grid = this.createEmptyGrid(size.width, size.height);
const path = this.generateMainPath(grid, template.pathStyle, rng);
this.addObstacles(grid, path, difficulty, rng);
this.addSecondaryPaths(grid, path, template.branchingFactor, rng);
this.addSpecialAreas(grid, template.specialAreas, rng);
return {
width: size.width,
height: size.height,
grid: grid,
startPosition: path[0],
endPosition: path[path.length - 1],
checkpoints: this.generateCheckpoints(path, difficulty)
};
}
}
Content Variety System
@ccclass('ContentVarietyManager')
export class ContentVarietyManager extends Component {
@property
varietyRules: VarietyRule[] = [];
@property
recentContent: ContentHistory = new ContentHistory(10);
selectContent(category: string, context: ContentContext): ContentData {
const available = this.getAvailableContent(category);
const viable = available.filter(content => {
return this.varietyRules.every(rule =>
this.evaluateVarietyRule(rule, content, context)
);
});
const fresh = viable.filter(content =>
!this.recentContent.wasRecentlyUsed(content.id, category)
);
const candidates = fresh.length > 0 ? fresh : viable;
const selected = this.weightedRandomSelection(candidates, context);
this.recentContent.recordUsage(selected.id, category);
return selected;
}
private evaluateVarietyRule(
rule: VarietyRule,
content: ContentData,
context: ContentContext
): boolean {
switch (rule.type) {
case 'difficulty_range':
return content.difficulty >= rule.minDifficulty &&
content.difficulty <= rule.maxDifficulty;
case 'theme_consistency':
return !context.requiredTheme ||
content.theme === context.requiredTheme;
case 'progression_appropriate':
return content.requiredUnlocks.every(unlock =>
context.playerProgress.unlockedFeatures.includes(unlock)
);
default:
return true;
}
}
}
Level Editor Tools
Editor Implementation
@ccclass('LevelEditor')
export class LevelEditor extends Component {
@property(Camera)
editorCamera: Camera = null;
@property(Node)
toolPanel: Node = null;
@property(Node)
propertyPanel: Node = null;
private _currentTool: EditorTool = null;
private _selectedObjects: EditorObject[] = [];
private _levelData: LevelData = null;
private _history: EditorAction[] = [];
private _historyIndex: number = -1;
onLoad() {
this.setupEditorUI();
this.setupInputHandling();
this.createNewLevel();
}
selectTool(toolType: string) {
this._currentTool = this.createTool(toolType);
this.updateToolUI();
}
onMouseClick(event: EventMouse) {
if (!this._currentTool) return;
const worldPos = this.screenToWorld(event.getLocation());
const action = this._currentTool.handleClick(worldPos, this._selectedObjects);
if (action) {
this.executeAction(action);
}
}
executeAction(action: EditorAction) {
action.execute(this._levelData);
this._history = this._history.slice(0, this._historyIndex + 1);
this._history.push(action);
this._historyIndex++;
this.refreshEditor();
this.markDirty();
}
undo() {
if (this._historyIndex >= 0) {
const action = this._history[this._historyIndex];
action.undo(this._levelData);
this._historyIndex--;
this.refreshEditor();
}
}
redo() {
if (this._historyIndex < this._history.length - 1) {
this._historyIndex++;
const action = this._history[this._historyIndex];
action.execute(this._levelData);
this.refreshEditor();
}
}
validateLevel(): ValidationResult {
const issues: ValidationIssue[] = [];
if (!this._levelData.layout.startPosition) {
issues.push({
severity: 'error',
message: 'Level must have a start position',
position: null
});
}
if (this._levelData.winConditions.length === 0) {
issues.push({
severity: 'warning',
message: 'Level should have win conditions',
position: null
});
}
const accessibilityCheck = this.checkAccessibility();
issues.push(...accessibilityCheck);
return {
isValid: issues.filter(i => i.severity === 'error').length === 0,
issues
};
}
async testLevel(): Promise<TestResult> {
const validation = this.validateLevel();
if (!validation.isValid) {
return {
success: false,
error: 'Level validation failed',
issues: validation.issues
};
}
const testScene = await this.createTestScene(this._levelData);
const results = await this.runAutomatedTests(testScene);
return {
success: true,
playable: results.completable,
estimatedDifficulty: results.difficulty,
completionTime: results.averageTime,
issues: results.issues
};
}
}
Player Guidance Systems
Flow Design
@ccclass('PlayerGuidanceSystem')
export class PlayerGuidanceSystem extends Component {
@property
guidanceEnabled: boolean = true;
@property
adaptiveHints: boolean = true;
private _playerStuckTime: number = 0;
private _lastProgress: number = 0;
private _hintLevel: number = 0;
update(dt: number) {
if (!this.guidanceEnabled) return;
this.trackPlayerProgress();
this.checkIfPlayerStuck(dt);
this.updateGuidanceVisuals();
}
private checkIfPlayerStuck(dt: number) {
const currentProgress = this.calculateProgress();
if (currentProgress <= this._lastProgress) {
this._playerStuckTime += dt;
} else {
this._playerStuckTime = 0;
this._hintLevel = 0;
this.hideAllGuidance();
}
this._lastProgress = currentProgress;
if (this._playerStuckTime > 10 && this._hintLevel === 0) {
this.showSubtleHint();
this._hintLevel = 1;
} else if (this._playerStuckTime > 20 && this._hintLevel === 1) {
this.showDirectHint();
this._hintLevel = 2;
} else if (this._playerStuckTime > 40 && this._hintLevel === 2) {
this.showExplicitGuidance();
this._hintLevel = 3;
}
}
showSubtleHint() {
this.highlightInteractableObjects();
this.showMovementTrails();
}
showDirectHint() {
this.showArrowPointers();
this.pulseTargetAreas();
}
showExplicitGuidance() {
this.showTextInstructions();
this.showHandTutorial();
}
}
Handoff Guidance
To cocos-puzzle-game-expert
Trigger: Puzzle-specific level design
Handoff: "Level structure ready. Puzzle mechanics needed for: [level type]"
To cocos-ui-builder
Trigger: Level UI elements needed
Handoff: "Level design complete. UI implementation needed for: [HUD/menus]"
To cocos-performance-optimizer
Trigger: Level performance optimization
Handoff: "Level content created. Performance optimization needed for: [large levels]"
To cocos-analytics-specialist
Trigger: Level analytics needed
Handoff: "Level system ready. Analytics tracking needed for: [progression/difficulty]"
Best Practices
- Player-Centric Design: Always design from the player's perspective
- Clear Objectives: Make goals obvious and achievable
- Balanced Difficulty: Create smooth progression curves
- Visual Clarity: Ensure important elements are easily identifiable
- Feedback Systems: Provide immediate response to player actions
- Accessibility: Design for players of different skill levels
- Replayability: Include variety and optional challenges
- Performance: Optimize for target platforms and devices
Codex Operating Notes
- Prefer inspecting the actual Cocos Creator project before proposing changes.
- Keep edits scoped to the requested game system, platform, or workflow.
- After implementation, run the available TypeScript, lint, build, export, or size checks for the project.
- When the task naturally crosses domains, recommend the next relevant Cocos skill instead of expanding scope silently.