一键导入
cocos-level-designer
Use when working on level architecture, difficulty balancing, progression mechanics, and procedural content generation.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when working on level architecture, difficulty balancing, progression mechanics, and procedural content generation.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use this skill when the user asks to release the iOS app, upload a build, submit to App Store Connect review, or "在 App Store 上架/提交审核" for WordMagicGame.
Seed a lesson-import draft on a deployed Happyword API (Preview or prod) and optionally run the extract-pending cron so it reaches `pending` (待复核). Uses tools starting with `hw_`. Invoke when the user wants to upload a textbook photo for review against a specific Vercel URL, mirror E2E `test_lesson_import_cron_e2e`, or smoke-test lesson import + cron on a branch deployment.
Investigate why a Vercel preview / production deployment of the FastAPI server is broken (404 on every path, 500 on every path, e2e CI red, etc.). Pulls the right log surface for the symptom and maps HTTP error headers to root causes. Use whenever the deployed server returns wrong responses, the server-ci e2e job fails, or autofix loops keep retrying without converging.
Health-check every active Vercel Preview deployment listed in the live manifest at `https://happyword.cool/api/v1/public/preview-urls.json`. Probes `GET /api/v1/public/health` through Vercel Deployment Protection (`x-vercel-protection-bypass` header), prints a one-line status per preview, and exits non-zero if any are sick. Use when the user asks to "test preview health", "check previews", "are PR previews alive?", before merging anything that touches `server/`, after a fleet-wide redeploy or env-var rotation, or after rotating `VERCEL_AUTOMATION_BYPASS_SECRET` — do NOT loop ad-hoc `curl` calls or use `tools/vercel/smoke-prod.sh` (production-only) for this.
Drives a per-feature visual + spec-anchored gap scout across HarmonyOS / iOS / Android using tools/parity_scout/. Use when asked to "find iOS / Android gaps vs HarmonyOS main", "check parity for <feature>", or "screenshot the three platforms and tell me what's different".
Drives a per-feature visual + spec-anchored gap scout across HarmonyOS / iOS / Android using tools/parity_scout/. Use when asked to "find iOS / Android gaps vs HarmonyOS main", "check parity for <feature>", or "screenshot the three platforms and tell me what's different".
| name | cocos-level-designer |
| description | Use when working on level architecture, difficulty balancing, progression mechanics, and procedural content generation. |
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.
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
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
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
export interface LevelData {
id: string;
name: string;
difficulty: number;
category: 'tutorial' | 'normal' | 'challenge' | 'boss';
// Objectives
objectives: LevelObjective[];
winConditions: WinCondition[];
// Content
layout: LevelLayout;
entities: EntityData[];
collectibles: CollectibleData[];
// Progression
requiredLevel: number;
unlocks: string[];
rewards: RewardData[];
// Performance
maxEntities: number;
preloadAssets: string[];
// Analytics
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;
}
// Preload assets
await this.preloadLevelAssets(levelData);
// Initialize level
this._currentLevel = levelData;
this._levelProgress = this.initializeLevelProgress(levelData);
// Setup level layout
await this.setupLevelLayout(levelData.layout);
// Spawn entities
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);
});
}
}
@ccclass('DifficultyManager')
export class DifficultyManager extends Component {
@property
baseDifficulty: number = 1.0;
@property
difficultyMultiplier: number = 0.1; // Per level
@property
playerSkillWeight: number = 0.3;
calculateLevelDifficulty(levelIndex: number, playerSkill: number): DifficultySettings {
// Base difficulty progression
const baseDiff = this.baseDifficulty + (levelIndex * this.difficultyMultiplier);
// Adjust for player skill
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 {
// Dynamic difficulty adjustment
if (performance.failureRate > 0.7) {
this.reduceDifficulty();
} else if (performance.successRate > 0.9 && performance.averageTime < 0.5) {
this.increaseDifficulty();
}
}
}
@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 {
// Initialize random with seed for reproducible levels
const rng = new SeededRandom(seed);
// Select appropriate template
const template = this.selectTemplate(difficulty, rng);
// Generate layout
const layout = this.generateLayout(template, difficulty, rng);
// Place entities
const entities = this.placeEntities(layout, template, difficulty, rng);
// Add collectibles
const collectibles = this.placeCollectibles(layout, difficulty, rng);
// Calculate objectives
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);
// Generate main path
const path = this.generateMainPath(grid, template.pathStyle, rng);
// Add obstacles
this.addObstacles(grid, path, difficulty, rng);
// Add branching paths
this.addSecondaryPaths(grid, path, template.branchingFactor, rng);
// Add special areas
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)
};
}
}
@ccclass('ContentVarietyManager')
export class ContentVarietyManager extends Component {
@property
varietyRules: VarietyRule[] = [];
@property
recentContent: ContentHistory = new ContentHistory(10);
selectContent(category: string, context: ContentContext): ContentData {
// Get available content for category
const available = this.getAvailableContent(category);
// Filter based on variety rules
const viable = available.filter(content => {
return this.varietyRules.every(rule =>
this.evaluateVarietyRule(rule, content, context)
);
});
// Avoid recently used content
const fresh = viable.filter(content =>
!this.recentContent.wasRecentlyUsed(content.id, category)
);
// Select from fresh content, fallback to viable
const candidates = fresh.length > 0 ? fresh : viable;
const selected = this.weightedRandomSelection(candidates, context);
// Record usage
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;
}
}
}
@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) {
// Execute the action
action.execute(this._levelData);
// Add to history for undo/redo
this._history = this._history.slice(0, this._historyIndex + 1);
this._history.push(action);
this._historyIndex++;
// Update UI
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[] = [];
// Check for required elements
if (!this._levelData.layout.startPosition) {
issues.push({
severity: 'error',
message: 'Level must have a start position',
position: null
});
}
// Check win conditions
if (this._levelData.winConditions.length === 0) {
issues.push({
severity: 'warning',
message: 'Level should have win conditions',
position: null
});
}
// Check accessibility
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
};
}
// Create test instance
const testScene = await this.createTestScene(this._levelData);
// Run automated tests
const results = await this.runAutomatedTests(testScene);
return {
success: true,
playable: results.completable,
estimatedDifficulty: results.difficulty,
completionTime: results.averageTime,
issues: results.issues
};
}
}
@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;
// Progressive hint system
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() {
// Subtle visual cues
this.highlightInteractableObjects();
this.showMovementTrails();
}
showDirectHint() {
// More obvious guidance
this.showArrowPointers();
this.pulseTargetAreas();
}
showExplicitGuidance() {
// Clear instructions
this.showTextInstructions();
this.showHandTutorial();
}
}
Trigger: Puzzle-specific level design Handoff: "Level structure ready. Puzzle mechanics needed for: [level type]"
Trigger: Level UI elements needed Handoff: "Level design complete. UI implementation needed for: [HUD/menus]"
Trigger: Level performance optimization Handoff: "Level content created. Performance optimization needed for: [large levels]"
Trigger: Level analytics needed Handoff: "Level system ready. Analytics tracking needed for: [progression/difficulty]"