Skip to main content

performance-optimization

Enforces 60fps rendering for Three.js scenes, bundle size optimization (<500KB initial, <2MB total), Lighthouse performance score >90, memory leak prevention, efficient Three.js patterns (instancing, LOD, culling), and performance budget monitoring for Black Trigram.

소스 정보

저장소
Hack23/blacktrigram
최근 소스 활동
2026년 1월 31일 15:47
감지된 SKILL.md 언어
영어
스타
8
포크
7

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
performance-optimization
description
Enforces 60fps rendering for Three.js scenes, bundle size optimization (<500KB initial, <2MB total), Lighthouse performance score >90, memory leak prevention, efficient Three.js patterns (instancing, LOD, culling), and performance budget monitoring for Black Trigram.
license
MIT
# Performance Optimization Skill ## Purpose This skill ensures that Black Trigram maintains exceptional performance across all platforms, achieving consistent 60fps rendering, optimal bundle sizes, and efficient resource utilization while providing an immersive Korean martial arts experience. ## When to Apply **Automatically trigger this skill when:** - Creating or modifying Three.js scenes and components - Adding new 3D models, textures, or visual effects - Implementing animation systems or game loops - Working with WebGL shaders or materials - Adding third-party dependencies - Optimizing rendering performance - Implementing lazy loading or code splitting - Adding performance-critical game logic - Reviewing pull requests with performance impact ## Core Principles ### 1. 60fps Rendering Target (Essential for Combat) **ALWAYS maintain 60fps (16.67ms per frame):** ✅ **Frame Budget Breakdown** ```typescript // Frame time budget for 60fps const FRAME_BUDGET_MS = { TOTAL: 16.67, // Total frame time GAME_LOGIC: 3.0, // Combat calculations, AI PHYSICS: 2.0, // Collision detection, animations RENDERING: 8.0, // Three.js draw calls INPUT: 1.0, // Input processing AUDIO: 1.0, // Audio updates OVERHEAD: 1.67, // Browser overhead, GC } as const; // ALWAYS measure frame time import { useFrame } from '@react-three/fiber'; export const PerformanceMonitor: React.FC = () => { const frameTimesRef = useRef<number[]>([]); const lastTimeRef = useRef(performance.now()); useFrame(() => { const now = performance.now(); const deltaTime = now - lastTimeRef.current; lastTimeRef.current = now; // Track frame times (rolling window) frameTimesRef.current.push(deltaTime); if (frameTimesRef.current.length > 120) { frameTimesRef.current.shift(); } // Alert if frame time exceeds budget if (deltaTime > FRAME_BUDGET_MS.TOTAL) { console.warn(`Frame time exceeded: ${deltaTime.toFixed(2)}ms`); } // Calculate average FPS const avgFrameTime = frameTimesRef.current.reduce((a, b) => a + b, 0) / frameTimesRef.current.length; const fps = 1000 / avgFrameTime; if (fps < 55) { console.error(`Low FPS detected: ${fps.toFixed(1)}`); } }); return null; }; ``` ✅ **Performance Profiling in Development** ```typescript // ALWAYS profile performance-critical sections import { useFrame } from '@react-three/fiber'; export const ProfiledCombatSystem: React.FC = () => { useFrame(() => { // Profile game logic performance.mark('combat-logic-start'); updateCombatLogic(); performance.mark('combat-logic-end'); performance.measure( 'Combat Logic', 'combat-logic-start', 'combat-logic-end' ); // Check measurement const measure = performance.getEntriesByName('Combat Logic')[0]; if (measure.duration > FRAME_BUDGET_MS.GAME_LOGIC) { console.warn(`Combat logic too slow: ${measure.duration.toFixed(2)}ms`); } performance.clearMarks(); performance.clearMeasures(); }); }; ``` ### 2. Bundle Size Optimization **ALWAYS enforce bundle size limits:** ✅ **Bundle Size Targets** ```typescript // vite.config.ts - Bundle size configuration export default defineConfig({ build: { rollupOptions: { output: { manualChunks: { // Vendor chunk for React + Three.js vendor: ['react', 'react-dom'], three: ['three', '@react-three/fiber', '@react-three/drei'], // Separate chunk for Korean assets korean: ['./src/types/constants/korean'], // Audio system separate chunk audio: ['./src/audio/AudioProvider'], }, }, }, // Minification settings minify: 'terser', terserOptions: { compress: { drop_console: true, // Remove console.log in production drop_debugger: true, pure_funcs: ['console.info', 'console.debug'], }, }, // Bundle size warning thresholds chunkSizeWarningLimit: 500, // 500KB warning }, }); ``` ✅ **Bundle Size Monitoring** ```json // package.json - Add bundle size analysis { "scripts": { "build": "tsc && vite build", "build:analyze": "vite-bundle-visualizer", "build:check-size": "npm run build && bundlesize" }, "bundlesize": [ { "path": "./dist/assets/index-*.js", "maxSize": "500 KB" }, { "path": "./dist/assets/vendor-*.js", "maxSize": "300 KB" }, { "path": "./dist/assets/three-*.js", "maxSize": "400 KB" } ] } ``` ✅ **Lazy Loading for Non-Critical Code** ```typescript // ALWAYS lazy load non-critical screens import { lazy, Suspense } from 'react'; import { LoadingScreen } from '@/components/ui/LoadingScreen'; // Lazy load heavy screens const CombatScreen = lazy(() => import('@/screens/CombatScreen')); const SettingsScreen = lazy(() => import('@/screens/SettingsScreen')); const TrainingScreen = lazy(() => import('@/screens/TrainingScreen')); export const App: React.FC = () => { return ( <Suspense fallback={<LoadingScreen korean="로딩 중" english="Loading" />}> <Router> <Routes> <Route path="/" element={<IntroScreen />} /> <Route path="/combat" element={<CombatScreen />} /> <Route path="/settings" element={<SettingsScreen />} /> <Route path="/training" element={<TrainingScreen />} /> </Routes> </Router> </Suspense> ); }; ``` ### 3. Three.js Performance Optimization **ALWAYS use efficient Three.js patterns:** ✅ **Instancing for Repeated Geometry** ```typescript // GOOD: Use instancing for particles, bullets, effects import { Instances, Instance } from '@react-three/drei'; import { useMemo } from 'react'; import { KOREAN_COLORS } from '@/types/constants'; export const OptimizedParticles: React.FC<{ count: number }> = ({ count }) => { // Pre-calculate positions once const positions = useMemo( () => Array.from({ length: count }, () => [ Math.random() * 20 - 10, Math.random() * 10, Math.random() * 20 - 10, ] as [number, number, number]), [count] ); return ( <Instances limit={count}> <sphereGeometry args={[0.1, 8, 8]} /> <meshBasicMaterial color={KOREAN_COLORS.PRIMARY_CYAN} /> {positions.map((pos, i) => ( <Instance key={i} position={pos} /> ))} </Instances> ); }; // BAD: Creating individual meshes (100x slower!) export const UnoptimizedParticles: React.FC = () => { return ( <> {Array.from({ length: 1000 }).map((_, i) => ( <mesh key={i} position={[Math.random() * 20 - 10, 0, 0]}> <sphereGeometry args={[0.1, 8, 8]} /> <meshBasicMaterial color={0x00ffff} /> </mesh> ))} </> ); }; ``` ✅ **Level of Detail (LOD) for Distance-Based Quality** ```typescript // GOOD: Use LOD for characters at varying distances import { Detailed } from '@react-three/drei'; export const OptimizedCharacter: React.FC<{ position: Vector3 }> = ({ position }) => { return ( <Detailed distances={[0, 10, 20, 40]} position={position}> {/* High detail (0-10 units) - 5000 triangles */} <HighDetailCharacterMesh /> {/* Medium detail (10-20 units) - 1500 triangles */} <MediumDetailCharacterMesh /> {/* Low detail (20-40 units) - 500 triangles */} <LowDetailCharacterMesh /> {/* Billboard (40+ units) - 2 triangles */} <CharacterBillboard /> </Detailed> ); }; ``` ✅ **Frustum Culling and Object Culling** ```typescript // GOOD: Enable frustum culling (default in Three.js) // Manually cull objects outside view when needed import { useFrame, useThree } from '@react-three/fiber'; import { useRef, useState } from 'react'; import * as THREE from 'three'; export const CulledObject: React.FC<{ position: Vector3 }> = ({ position }) => { const meshRef = useRef<THREE.Mesh>(null); const { camera } = useThree(); const [isVisible, setIsVisible] = useState(true); useFrame(() => { if (!meshRef.current) return; // Check if object is in camera frustum const frustum = new THREE.Frustum(); const projectionMatrix = new THREE.Matrix4().multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); frustum.setFromProjectionMatrix(projectionMatrix); const visible = frustum.intersectsObject(meshRef.current); if (visible !== isVisible) { setIsVisible(visible); } }); // Don't render if not visible if (!isVisible) return null; return ( <mesh ref={meshRef} position={position}> <boxGeometry args={[1, 1, 1]} /> <meshStandardMaterial /> </mesh> ); }; ``` ✅ **Memoize Geometries and Materials** ```typescript // GOOD: Reuse geometries and materials import { useMemo } from 'react'; import * as THREE from 'three'; import { KOREAN_COLORS } from '@/types/constants'; export const OptimizedMeshes: React.FC = () => { // Create geometry once, reuse for all instances const sharedGeometry = useMemo( () => new THREE.BoxGeometry(1, 1, 1), [] ); // Create material once, reuse for all instances const sharedMaterial = useMemo( () => new THREE.MeshStandardMaterial({ color: KOREAN_COLORS.PRIMARY_CYAN, metalness: 0.5, roughness: 0.5, }), [] ); // Cleanup on unmount useEffect(() => { return () => { sharedGeometry.dispose(); sharedMaterial.dispose(); }; }, [sharedGeometry, sharedMaterial]); return ( <> <mesh geometry={sharedGeometry} material={sharedMaterial} position={[-2, 0, 0]} /> <mesh geometry={sharedGeometry} material={sharedMaterial} position={[0, 0, 0]} /> <mesh geometry={sharedGeometry} material={sharedMaterial} position={[2, 0, 0]} /> </> ); }; // BAD: Creating new geometry/material for each mesh export const UnoptimizedMeshes: React.FC = () => { return ( <> {Array.from({ length: 100 }).map((_, i) => ( <mesh key={i} position={[i, 0, 0]}> <boxGeometry args={[1, 1, 1]} /> {/* New geometry each time! */} <meshStandardMaterial color={0x00ffff} /> {/* New material each time! */} </mesh> ))} </> ); }; ``` ✅ **Efficient useFrame Usage** ```typescript // GOOD: Batch operations in useFrame import { useFrame } from '@react-three/fiber'; export const OptimizedAnimations: React.FC = () => { const objectsRef = useRef<THREE.Group>(null); useFrame((state, delta) => { if (!objectsRef.current) return;
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기