cfsa-antigravity 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +324 -0
- package/package.json +34 -0
- package/template/.agent/instructions/commands.md +48 -0
- package/template/.agent/instructions/patterns.md +61 -0
- package/template/.agent/instructions/structure.md +29 -0
- package/template/.agent/instructions/tech-stack.md +43 -0
- package/template/.agent/instructions/workflow.md +41 -0
- package/template/.agent/kit-sync.md +15 -0
- package/template/.agent/rules/boundary-not-placeholder.md +146 -0
- package/template/.agent/rules/completion-checklist.md +48 -0
- package/template/.agent/rules/decision-classification.md +103 -0
- package/template/.agent/rules/extensibility.md +47 -0
- package/template/.agent/rules/question-vs-command.md +81 -0
- package/template/.agent/rules/security-first.md +43 -0
- package/template/.agent/rules/specificity-standards.md +54 -0
- package/template/.agent/rules/tdd-contract-first.md +57 -0
- package/template/.agent/rules/vertical-slices.md +42 -0
- package/template/.agent/skill-library/MANIFEST.md +480 -0
- package/template/.agent/skill-library/README.md +38 -0
- package/template/.agent/skill-library/meta/brand-guidelines/SKILL.md +73 -0
- package/template/.agent/skill-library/meta/claude-code/README.md +9 -0
- package/template/.agent/skill-library/meta/claude-code/agent-development/SKILL.md +415 -0
- package/template/.agent/skill-library/meta/claude-code/hook-development/SKILL.md +712 -0
- package/template/.agent/skill-library/meta/claude-code/plugin-structure/SKILL.md +476 -0
- package/template/.agent/skill-library/meta/git-advanced/SKILL.md +972 -0
- package/template/.agent/skill-library/meta/mcp-builder/SKILL.md +236 -0
- package/template/.agent/skill-library/meta/product-marketing-context/SKILL.md +241 -0
- package/template/.agent/skill-library/meta/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skill-library/meta/tmux-processes/SKILL.md +210 -0
- package/template/.agent/skill-library/meta/using-tmux-for-interactive-commands/SKILL.md +178 -0
- package/template/.agent/skill-library/stack/3d/threejs-pro/SKILL.md +300 -0
- package/template/.agent/skill-library/stack/ai/ai-sdk/SKILL.md +77 -0
- package/template/.agent/skill-library/stack/ai/langchain/SKILL.md +530 -0
- package/template/.agent/skill-library/stack/ai/ollama/SKILL.md +321 -0
- package/template/.agent/skill-library/stack/ai/openai-sdk/SKILL.md +549 -0
- package/template/.agent/skill-library/stack/analytics/google-analytics/SKILL.md +153 -0
- package/template/.agent/skill-library/stack/api/graphql/SKILL.md +1061 -0
- package/template/.agent/skill-library/stack/api/trpc/SKILL.md +576 -0
- package/template/.agent/skill-library/stack/auth/authjs/SKILL.md +569 -0
- package/template/.agent/skill-library/stack/auth/clerk/SKILL.md +590 -0
- package/template/.agent/skill-library/stack/auth/firebase-auth/SKILL.md +734 -0
- package/template/.agent/skill-library/stack/cms/payload-cms/SKILL.md +573 -0
- package/template/.agent/skill-library/stack/cms/shopify/SKILL.md +1193 -0
- package/template/.agent/skill-library/stack/cms/wordpress/SKILL.md +1104 -0
- package/template/.agent/skill-library/stack/css/sass-scss/SKILL.md +1121 -0
- package/template/.agent/skill-library/stack/css/tailwind-css-patterns/SKILL.md +863 -0
- package/template/.agent/skill-library/stack/css/tailwind-design-system/SKILL.md +490 -0
- package/template/.agent/skill-library/stack/css/vanilla-css/SKILL.md +1078 -0
- package/template/.agent/skill-library/stack/databases/clickhouse/SKILL.md +311 -0
- package/template/.agent/skill-library/stack/databases/influxdb/SKILL.md +280 -0
- package/template/.agent/skill-library/stack/databases/lancedb/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/databases/mongodb/SKILL.md +1169 -0
- package/template/.agent/skill-library/stack/databases/neo4j/SKILL.md +839 -0
- package/template/.agent/skill-library/stack/databases/pgvector/SKILL.md +241 -0
- package/template/.agent/skill-library/stack/databases/pinecone/SKILL.md +212 -0
- package/template/.agent/skill-library/stack/databases/postgresql/SKILL.md +658 -0
- package/template/.agent/skill-library/stack/databases/qdrant/SKILL.md +312 -0
- package/template/.agent/skill-library/stack/databases/redis/SKILL.md +1079 -0
- package/template/.agent/skill-library/stack/databases/spacetimedb/SKILL.md +532 -0
- package/template/.agent/skill-library/stack/databases/sqlite/SKILL.md +1132 -0
- package/template/.agent/skill-library/stack/databases/supabase/SKILL.md +640 -0
- package/template/.agent/skill-library/stack/databases/surrealdb-expert/SKILL.md +945 -0
- package/template/.agent/skill-library/stack/databases/timescaledb/SKILL.md +745 -0
- package/template/.agent/skill-library/stack/databases/weaviate/SKILL.md +218 -0
- package/template/.agent/skill-library/stack/devops/github-actions/SKILL.md +554 -0
- package/template/.agent/skill-library/stack/devops/kubernetes/SKILL.md +950 -0
- package/template/.agent/skill-library/stack/devops/nginx/SKILL.md +841 -0
- package/template/.agent/skill-library/stack/devops/terraform/SKILL.md +860 -0
- package/template/.agent/skill-library/stack/email/resend/SKILL.md +391 -0
- package/template/.agent/skill-library/stack/engines/godot/SKILL.md +488 -0
- package/template/.agent/skill-library/stack/extensions/chrome-extension/SKILL.md +375 -0
- package/template/.agent/skill-library/stack/extensions/vscode-extension/SKILL.md +453 -0
- package/template/.agent/skill-library/stack/frameworks/astro-framework/SKILL.md +162 -0
- package/template/.agent/skill-library/stack/frameworks/electron/SKILL.md +1286 -0
- package/template/.agent/skill-library/stack/frameworks/fastapi/SKILL.md +650 -0
- package/template/.agent/skill-library/stack/frameworks/hono/SKILL.md +90 -0
- package/template/.agent/skill-library/stack/frameworks/nestjs/SKILL.md +878 -0
- package/template/.agent/skill-library/stack/frameworks/nextjs/SKILL.md +635 -0
- package/template/.agent/skill-library/stack/frameworks/nuxt/SKILL.md +564 -0
- package/template/.agent/skill-library/stack/frameworks/sveltekit/SKILL.md +614 -0
- package/template/.agent/skill-library/stack/frameworks/tauri/SKILL.md +920 -0
- package/template/.agent/skill-library/stack/gamedev/godot/SKILL.md +1032 -0
- package/template/.agent/skill-library/stack/gamedev/unity/SKILL.md +1175 -0
- package/template/.agent/skill-library/stack/hosting/aws/SKILL.md +467 -0
- package/template/.agent/skill-library/stack/hosting/cloudflare/SKILL.md +201 -0
- package/template/.agent/skill-library/stack/hosting/docker-expert/SKILL.md +409 -0
- package/template/.agent/skill-library/stack/hosting/vercel/SKILL.md +484 -0
- package/template/.agent/skill-library/stack/languages/bash-scripting/SKILL.md +773 -0
- package/template/.agent/skill-library/stack/languages/c-cpp/SKILL.md +712 -0
- package/template/.agent/skill-library/stack/languages/gdscript/SKILL.md +789 -0
- package/template/.agent/skill-library/stack/languages/go/SKILL.md +664 -0
- package/template/.agent/skill-library/stack/languages/java/SKILL.md +778 -0
- package/template/.agent/skill-library/stack/languages/kotlin/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/languages/python/SKILL.md +678 -0
- package/template/.agent/skill-library/stack/languages/rust/SKILL.md +673 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/SKILL.md +141 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/advanced-generics.md +90 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/branded-types.md +57 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/builder-pattern.md +71 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/common-pitfalls.md +135 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/conditional-types.md +27 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/decorators.md +98 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/discriminated-unions.md +62 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/mapped-types.md +53 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/performance-best-practices.md +104 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/template-literal-types.md +49 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/testing-types.md +112 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-guards.md +70 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-inference.md +101 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/utility-types.md +98 -0
- package/template/.agent/skill-library/stack/languages/vanilla-javascript/SKILL.md +803 -0
- package/template/.agent/skill-library/stack/messaging/kafka/SKILL.md +235 -0
- package/template/.agent/skill-library/stack/mobile/expo-react-native/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/mobile/flutter/SKILL.md +316 -0
- package/template/.agent/skill-library/stack/mobile/react-native/SKILL.md +337 -0
- package/template/.agent/skill-library/stack/monitoring/posthog/SKILL.md +396 -0
- package/template/.agent/skill-library/stack/monitoring/sentry/SKILL.md +509 -0
- package/template/.agent/skill-library/stack/observability/datadog/SKILL.md +179 -0
- package/template/.agent/skill-library/stack/observability/distributed-tracing/SKILL.md +140 -0
- package/template/.agent/skill-library/stack/observability/logging-best-practices/SKILL.md +168 -0
- package/template/.agent/skill-library/stack/observability/opentelemetry/SKILL.md +164 -0
- package/template/.agent/skill-library/stack/observability/prometheus-grafana/SKILL.md +246 -0
- package/template/.agent/skill-library/stack/observability/python-observability/SKILL.md +158 -0
- package/template/.agent/skill-library/stack/orm/drizzle-orm/SKILL.md +613 -0
- package/template/.agent/skill-library/stack/orm/prisma/SKILL.md +744 -0
- package/template/.agent/skill-library/stack/payments/lemonsqueezy/SKILL.md +393 -0
- package/template/.agent/skill-library/stack/payments/stripe-integration/SKILL.md +457 -0
- package/template/.agent/skill-library/stack/queue/bullmq/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/queue/inngest/SKILL.md +438 -0
- package/template/.agent/skill-library/stack/realtime/socketio/SKILL.md +595 -0
- package/template/.agent/skill-library/stack/search/elasticsearch/SKILL.md +248 -0
- package/template/.agent/skill-library/stack/search/meilisearch/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/security/crypto-patterns/SKILL.md +437 -0
- package/template/.agent/skill-library/stack/security/csp-cors-headers/SKILL.md +588 -0
- package/template/.agent/skill-library/stack/security/dependency-auditing/SKILL.md +560 -0
- package/template/.agent/skill-library/stack/security/input-sanitization/SKILL.md +430 -0
- package/template/.agent/skill-library/stack/security/owasp-web-security/SKILL.md +421 -0
- package/template/.agent/skill-library/stack/state/tanstack-query/SKILL.md +637 -0
- package/template/.agent/skill-library/stack/state/zustand/SKILL.md +483 -0
- package/template/.agent/skill-library/stack/storage/aws-s3/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/testing/playwright/SKILL.md +641 -0
- package/template/.agent/skill-library/stack/testing/storybook/SKILL.md +923 -0
- package/template/.agent/skill-library/stack/testing/testing-library/SKILL.md +872 -0
- package/template/.agent/skill-library/stack/testing/vitest/SKILL.md +714 -0
- package/template/.agent/skill-library/stack/ui/react-best-practices/SKILL.md +877 -0
- package/template/.agent/skill-library/stack/ui/react-composition-patterns/SKILL.md +1107 -0
- package/template/.agent/skill-library/stack/ui/react-flow/SKILL.md +425 -0
- package/template/.agent/skill-library/stack/ui/shadcn-ui/SKILL.md +703 -0
- package/template/.agent/skill-library/surface/api/api-caching/SKILL.md +458 -0
- package/template/.agent/skill-library/surface/api/api-documentation-openapi/SKILL.md +697 -0
- package/template/.agent/skill-library/surface/api/api-error-handling/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/api-security-checklist/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/api-versioning/SKILL.md +420 -0
- package/template/.agent/skill-library/surface/api/email-best-practices/SKILL.md +59 -0
- package/template/.agent/skill-library/surface/api/rate-limiting-abuse-protection/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/webhook-design/SKILL.md +752 -0
- package/template/.agent/skill-library/surface/cli/cli-configuration-management/SKILL.md +445 -0
- package/template/.agent/skill-library/surface/cli/cli-error-diagnostics/SKILL.md +515 -0
- package/template/.agent/skill-library/surface/cli/cli-shell-integration/SKILL.md +479 -0
- package/template/.agent/skill-library/surface/cli/cli-ux-design/SKILL.md +477 -0
- package/template/.agent/skill-library/surface/desktop/desktop-app-distribution/SKILL.md +416 -0
- package/template/.agent/skill-library/surface/desktop/desktop-security-sandboxing/SKILL.md +407 -0
- package/template/.agent/skill-library/surface/desktop/desktop-ux-conventions/SKILL.md +361 -0
- package/template/.agent/skill-library/surface/desktop/native-os-integration/SKILL.md +563 -0
- package/template/.agent/skill-library/surface/extension/browser-extension-patterns/SKILL.md +482 -0
- package/template/.agent/skill-library/surface/extension/plugin-architecture-design/SKILL.md +632 -0
- package/template/.agent/skill-library/surface/extension/vscode-extension-development/SKILL.md +728 -0
- package/template/.agent/skill-library/surface/mobile/app-store-submission/SKILL.md +304 -0
- package/template/.agent/skill-library/surface/mobile/mobile-offline-sync/SKILL.md +443 -0
- package/template/.agent/skill-library/surface/mobile/mobile-responsive-patterns/SKILL.md +432 -0
- package/template/.agent/skill-library/surface/mobile/push-notifications/SKILL.md +495 -0
- package/template/.agent/skill-library/surface/web/accessibility-compliance/SKILL.md +827 -0
- package/template/.agent/skill-library/surface/web/ai-seo/SKILL.md +398 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/content-patterns.md +285 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/platform-ranking-factors.md +152 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/SKILL.md +309 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/event-library.md +260 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/ga4-implementation.md +300 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/gtm-implementation.md +390 -0
- package/template/.agent/skill-library/surface/web/authentication-ui-flows/SKILL.md +530 -0
- package/template/.agent/skill-library/surface/web/dark-mode-theming/SKILL.md +516 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/SKILL.md +105 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/charts.csv +26 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/colors.csv +97 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/landing.csv +31 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/styles.csv +59 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/typography.csv +58 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/ux-guidelines.csv +100 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/core.py +258 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/design_system.py +1067 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/search.py +106 -0
- package/template/.agent/skill-library/surface/web/form-handling-validation/SKILL.md +675 -0
- package/template/.agent/skill-library/surface/web/frontend-design/SKILL.md +1393 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/cppn-hero.tsx +299 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/wave-hero.tsx +875 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/SKILL.md +111 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/scripts/ux_audit.py +739 -0
- package/template/.agent/skill-library/surface/web/i18n-localization/SKILL.md +154 -0
- package/template/.agent/skill-library/surface/web/offline-first-pwa/SKILL.md +657 -0
- package/template/.agent/skill-library/surface/web/page-cro/SKILL.md +182 -0
- package/template/.agent/skill-library/surface/web/page-cro/references/experiments.md +248 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/SKILL.md +238 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/references/playbooks.md +308 -0
- package/template/.agent/skill-library/surface/web/schema-markup/SKILL.md +179 -0
- package/template/.agent/skill-library/surface/web/schema-markup/references/schema-examples.md +398 -0
- package/template/.agent/skill-library/surface/web/seo-audit/SKILL.md +394 -0
- package/template/.agent/skill-library/surface/web/seo-audit/references/ai-writing-detection.md +200 -0
- package/template/.agent/skill-library/surface/web/web-performance-optimization/SKILL.md +646 -0
- package/template/.agent/skill-library/surface/web/web-scraping/SKILL.md +58 -0
- package/template/.agent/skills/accessibility/SKILL.md +522 -0
- package/template/.agent/skills/accessibility/references/WCAG.md +162 -0
- package/template/.agent/skills/adversarial-review/SKILL.md +90 -0
- package/template/.agent/skills/antigravity-workflows/SKILL.md +81 -0
- package/template/.agent/skills/antigravity-workflows/resources/implementation-playbook.md +36 -0
- package/template/.agent/skills/api-design-principles/SKILL.md +37 -0
- package/template/.agent/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/template/.agent/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/template/.agent/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/template/.agent/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/template/.agent/skills/api-design-principles/resources/implementation-playbook.md +513 -0
- package/template/.agent/skills/api-versioning/SKILL.md +420 -0
- package/template/.agent/skills/architecture-mapping/SKILL.md +219 -0
- package/template/.agent/skills/bootstrap-agents/SKILL.md +259 -0
- package/template/.agent/skills/brainstorming/SKILL.md +236 -0
- package/template/.agent/skills/brand-guidelines/SKILL.md +44 -0
- package/template/.agent/skills/clean-code/SKILL.md +94 -0
- package/template/.agent/skills/code-review-pro/SKILL.md +152 -0
- package/template/.agent/skills/concise-planning/SKILL.md +68 -0
- package/template/.agent/skills/cross-layer-consistency/SKILL.md +117 -0
- package/template/.agent/skills/database-schema-design/SKILL.md +429 -0
- package/template/.agent/skills/deployment-procedures/SKILL.md +241 -0
- package/template/.agent/skills/design-anti-cliche/SKILL.md +159 -0
- package/template/.agent/skills/design-direction/SKILL.md +45 -0
- package/template/.agent/skills/error-handling-patterns/SKILL.md +721 -0
- package/template/.agent/skills/find-skills/SKILL.md +145 -0
- package/template/.agent/skills/git-advanced/SKILL.md +972 -0
- package/template/.agent/skills/git-workflow/SKILL.md +420 -0
- package/template/.agent/skills/idea-extraction/SKILL.md +271 -0
- package/template/.agent/skills/logging-best-practices/SKILL.md +851 -0
- package/template/.agent/skills/migration-management/SKILL.md +384 -0
- package/template/.agent/skills/minimalist-surgical-development/SKILL.md +69 -0
- package/template/.agent/skills/parallel-agents/SKILL.md +165 -0
- package/template/.agent/skills/parallel-debugging/SKILL.md +135 -0
- package/template/.agent/skills/parallel-feature-development/SKILL.md +166 -0
- package/template/.agent/skills/performance-budgeting/SKILL.md +144 -0
- package/template/.agent/skills/pipeline-rubrics/SKILL.md +51 -0
- package/template/.agent/skills/pipeline-rubrics/references/architecture-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +21 -0
- package/template/.agent/skills/pipeline-rubrics/references/fe-rubric.md +20 -0
- package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/scoring.md +28 -0
- package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +11 -0
- package/template/.agent/skills/prd-templates/SKILL.md +88 -0
- package/template/.agent/skills/prd-templates/references/architecture-design-template.md +88 -0
- package/template/.agent/skills/prd-templates/references/be-spec-template.md +101 -0
- package/template/.agent/skills/prd-templates/references/data-placement-template.md +74 -0
- package/template/.agent/skills/prd-templates/references/decomposition-templates.md +211 -0
- package/template/.agent/skills/prd-templates/references/design-system-decisions.md +198 -0
- package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +124 -0
- package/template/.agent/skills/prd-templates/references/fe-classification-procedures.md +47 -0
- package/template/.agent/skills/prd-templates/references/fe-spec-template.md +84 -0
- package/template/.agent/skills/prd-templates/references/infrastructure-report-template.md +71 -0
- package/template/.agent/skills/prd-templates/references/operational-templates.md +116 -0
- package/template/.agent/skills/prd-templates/references/placeholder-guard-template.md +21 -0
- package/template/.agent/skills/prd-templates/references/surface-model.md +61 -0
- package/template/.agent/skills/prd-templates/references/vision-template.md +66 -0
- package/template/.agent/skills/prompt-engineer/README.md +659 -0
- package/template/.agent/skills/prompt-engineer/SKILL.md +249 -0
- package/template/.agent/skills/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skills/resolve-ambiguity/SKILL.md +278 -0
- package/template/.agent/skills/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skills/security-scanning-security-hardening/SKILL.md +231 -0
- package/template/.agent/skills/session-continuity/SKILL.md +730 -0
- package/template/.agent/skills/session-continuity/protocols/01-session-resumption.md +38 -0
- package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +85 -0
- package/template/.agent/skills/session-continuity/protocols/03-progress-update.md +70 -0
- package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +60 -0
- package/template/.agent/skills/session-continuity/protocols/05-session-close.md +37 -0
- package/template/.agent/skills/session-continuity/protocols/06-decision-analysis.md +84 -0
- package/template/.agent/skills/session-continuity/protocols/07-spec-pipeline-generation.md +48 -0
- package/template/.agent/skills/session-continuity/protocols/08-spec-pipeline-update.md +43 -0
- package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +122 -0
- package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +104 -0
- package/template/.agent/skills/session-continuity/protocols/ambiguity-gates.md +48 -0
- package/template/.agent/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.agent/skills/skill-creator/README.md +270 -0
- package/template/.agent/skills/skill-creator/SKILL.md +590 -0
- package/template/.agent/skills/skill-creator/references/output-patterns.md +82 -0
- package/template/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/template/.agent/skills/skill-creator/scripts/init_skill.py +303 -0
- package/template/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/template/.agent/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/template/.agent/skills/spec-writing/SKILL.md +110 -0
- package/template/.agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/template/.agent/skills/systematic-debugging/SKILL.md +297 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/template/.agent/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/template/.agent/skills/systematic-debugging/find-polluter.sh +63 -0
- package/template/.agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/template/.agent/skills/systematic-debugging/test-academic.md +14 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/template/.agent/skills/tdd-workflow/SKILL.md +409 -0
- package/template/.agent/skills/tech-stack-catalog/SKILL.md +49 -0
- package/template/.agent/skills/tech-stack-catalog/references/constraint-questions.md +21 -0
- package/template/.agent/skills/tech-stack-catalog/references/dev-tooling-decisions.md +37 -0
- package/template/.agent/skills/tech-stack-catalog/references/surface-decision-tables.md +69 -0
- package/template/.agent/skills/technical-writer/SKILL.md +242 -0
- package/template/.agent/skills/testing-strategist/SKILL.md +932 -0
- package/template/.agent/skills/verification-before-completion/SKILL.md +145 -0
- package/template/.agent/skills/workflow-automation/SKILL.md +73 -0
- package/template/.agent/workflows/audit-ambiguity-execute.md +165 -0
- package/template/.agent/workflows/audit-ambiguity-rubrics.md +83 -0
- package/template/.agent/workflows/audit-ambiguity.md +64 -0
- package/template/.agent/workflows/bootstrap-agents-fill.md +201 -0
- package/template/.agent/workflows/bootstrap-agents-provision.md +197 -0
- package/template/.agent/workflows/bootstrap-agents.md +66 -0
- package/template/.agent/workflows/create-prd-architecture.md +119 -0
- package/template/.agent/workflows/create-prd-compile.md +138 -0
- package/template/.agent/workflows/create-prd-design-system.md +135 -0
- package/template/.agent/workflows/create-prd-security.md +113 -0
- package/template/.agent/workflows/create-prd-stack.md +91 -0
- package/template/.agent/workflows/create-prd.md +168 -0
- package/template/.agent/workflows/decompose-architecture-structure.md +82 -0
- package/template/.agent/workflows/decompose-architecture-validate.md +119 -0
- package/template/.agent/workflows/decompose-architecture.md +111 -0
- package/template/.agent/workflows/evolve-contract.md +98 -0
- package/template/.agent/workflows/evolve-feature-cascade.md +140 -0
- package/template/.agent/workflows/evolve-feature-classify.md +116 -0
- package/template/.agent/workflows/evolve-feature.md +56 -0
- package/template/.agent/workflows/ideate-discover.md +144 -0
- package/template/.agent/workflows/ideate-extract.md +129 -0
- package/template/.agent/workflows/ideate-validate.md +117 -0
- package/template/.agent/workflows/ideate.md +113 -0
- package/template/.agent/workflows/implement-slice-setup.md +113 -0
- package/template/.agent/workflows/implement-slice-tdd.md +198 -0
- package/template/.agent/workflows/implement-slice.md +50 -0
- package/template/.agent/workflows/plan-phase.md +202 -0
- package/template/.agent/workflows/propagate-decision-apply.md +135 -0
- package/template/.agent/workflows/propagate-decision-scan.md +147 -0
- package/template/.agent/workflows/propagate-decision.md +56 -0
- package/template/.agent/workflows/remediate-pipeline-assess.md +138 -0
- package/template/.agent/workflows/remediate-pipeline-execute.md +135 -0
- package/template/.agent/workflows/remediate-pipeline.md +55 -0
- package/template/.agent/workflows/resolve-ambiguity.md +82 -0
- package/template/.agent/workflows/sync-kit.md +209 -0
- package/template/.agent/workflows/update-architecture-map.md +74 -0
- package/template/.agent/workflows/validate-phase.md +219 -0
- package/template/.agent/workflows/verify-infrastructure.md +207 -0
- package/template/.agent/workflows/write-architecture-spec-deepen.md +139 -0
- package/template/.agent/workflows/write-architecture-spec-design.md +202 -0
- package/template/.agent/workflows/write-architecture-spec.md +63 -0
- package/template/.agent/workflows/write-be-spec-classify.md +165 -0
- package/template/.agent/workflows/write-be-spec-write.md +98 -0
- package/template/.agent/workflows/write-be-spec.md +76 -0
- package/template/.agent/workflows/write-fe-spec-classify.md +170 -0
- package/template/.agent/workflows/write-fe-spec-write.md +94 -0
- package/template/.agent/workflows/write-fe-spec.md +71 -0
- package/template/AGENTS.md +176 -0
- package/template/GEMINI.md +177 -0
- package/template/docs/README.md +187 -0
- package/template/docs/audits/.gitkeep +0 -0
- package/template/docs/audits/README.md +10 -0
- package/template/docs/plans/.gitkeep +0 -0
- package/template/docs/plans/README.md +21 -0
- package/template/docs/plans/be/.gitkeep +0 -0
- package/template/docs/plans/be/README.md +11 -0
- package/template/docs/plans/fe/.gitkeep +0 -0
- package/template/docs/plans/fe/README.md +11 -0
- package/template/docs/plans/ia/.gitkeep +0 -0
- package/template/docs/plans/ia/README.md +17 -0
- package/template/docs/plans/ia/deep-dives/.gitkeep +0 -0
- package/template/docs/plans/ia/deep-dives/README.md +5 -0
- package/template/docs/plans/phases/.gitkeep +0 -0
- package/template/docs/plans/phases/README.md +11 -0
|
@@ -0,0 +1,1175 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unity
|
|
3
|
+
description: "Comprehensive Unity game development guide (2022+) covering ECS vs MonoBehaviour patterns, component architecture, ScriptableObjects, new Input System, Addressables, Cinemachine, Timeline, UI Toolkit vs UGUI, physics, animation, async patterns, object pooling, NavMesh, rendering pipelines (URP/HDRP), multiplayer (Netcode for GameObjects), testing, and DOTS/Burst for performance. Use when building games with Unity."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Unity Game Development (2022+)
|
|
8
|
+
|
|
9
|
+
## 1. Component Architecture
|
|
10
|
+
|
|
11
|
+
### MonoBehaviour Lifecycle
|
|
12
|
+
|
|
13
|
+
```csharp
|
|
14
|
+
using UnityEngine;
|
|
15
|
+
|
|
16
|
+
public class Player : MonoBehaviour
|
|
17
|
+
{
|
|
18
|
+
// Called once when the script instance is loaded
|
|
19
|
+
private void Awake()
|
|
20
|
+
{
|
|
21
|
+
// Initialize references (runs before Start, even if disabled)
|
|
22
|
+
_rigidbody = GetComponent<Rigidbody>();
|
|
23
|
+
_animator = GetComponent<Animator>();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Called once before the first frame (only if enabled)
|
|
27
|
+
private void Start()
|
|
28
|
+
{
|
|
29
|
+
// Initialize state that depends on other objects being ready
|
|
30
|
+
_health = _stats.MaxHealth;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Called every frame (variable timestep)
|
|
34
|
+
private void Update()
|
|
35
|
+
{
|
|
36
|
+
// Input, UI updates, non-physics logic
|
|
37
|
+
HandleInput();
|
|
38
|
+
UpdateAnimations();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Called at fixed intervals (physics timestep, default 0.02s)
|
|
42
|
+
private void FixedUpdate()
|
|
43
|
+
{
|
|
44
|
+
// Physics movement, forces, raycasts
|
|
45
|
+
MoveCharacter();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Called after all Update functions
|
|
49
|
+
private void LateUpdate()
|
|
50
|
+
{
|
|
51
|
+
// Camera follow, UI updates that depend on movement
|
|
52
|
+
UpdateCameraFollow();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private void OnEnable() { /* Subscribe to events */ }
|
|
56
|
+
private void OnDisable() { /* Unsubscribe from events */ }
|
|
57
|
+
private void OnDestroy() { /* Final cleanup */ }
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Component Communication
|
|
62
|
+
|
|
63
|
+
```csharp
|
|
64
|
+
// PREFERRED: Events and interfaces over direct references
|
|
65
|
+
public interface IDamageable
|
|
66
|
+
{
|
|
67
|
+
void TakeDamage(float amount, DamageType type);
|
|
68
|
+
float CurrentHealth { get; }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public class Health : MonoBehaviour, IDamageable
|
|
72
|
+
{
|
|
73
|
+
[SerializeField] private float _maxHealth = 100f;
|
|
74
|
+
|
|
75
|
+
public float CurrentHealth { get; private set; }
|
|
76
|
+
|
|
77
|
+
// C# events for decoupled communication
|
|
78
|
+
public event System.Action<float, float> OnHealthChanged;
|
|
79
|
+
public event System.Action OnDeath;
|
|
80
|
+
|
|
81
|
+
private void Awake()
|
|
82
|
+
{
|
|
83
|
+
CurrentHealth = _maxHealth;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public void TakeDamage(float amount, DamageType type)
|
|
87
|
+
{
|
|
88
|
+
CurrentHealth = Mathf.Max(0f, CurrentHealth - amount);
|
|
89
|
+
OnHealthChanged?.Invoke(CurrentHealth, _maxHealth);
|
|
90
|
+
|
|
91
|
+
if (CurrentHealth <= 0f)
|
|
92
|
+
{
|
|
93
|
+
OnDeath?.Invoke();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Usage: query interfaces instead of concrete types
|
|
99
|
+
public class Projectile : MonoBehaviour
|
|
100
|
+
{
|
|
101
|
+
[SerializeField] private float _damage = 25f;
|
|
102
|
+
|
|
103
|
+
private void OnTriggerEnter(Collider other)
|
|
104
|
+
{
|
|
105
|
+
if (other.TryGetComponent<IDamageable>(out var damageable))
|
|
106
|
+
{
|
|
107
|
+
damageable.TakeDamage(_damage, DamageType.Projectile);
|
|
108
|
+
Destroy(gameObject);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Serialization Patterns
|
|
115
|
+
|
|
116
|
+
```csharp
|
|
117
|
+
public class EnemySpawner : MonoBehaviour
|
|
118
|
+
{
|
|
119
|
+
// Visible in Inspector, private in code
|
|
120
|
+
[SerializeField] private GameObject _enemyPrefab;
|
|
121
|
+
[SerializeField] private Transform[] _spawnPoints;
|
|
122
|
+
[SerializeField] private float _spawnInterval = 2f;
|
|
123
|
+
[SerializeField, Range(1, 50)] private int _maxEnemies = 10;
|
|
124
|
+
|
|
125
|
+
// Headers and tooltips for Inspector organization
|
|
126
|
+
[Header("Difficulty")]
|
|
127
|
+
[Tooltip("Multiplier applied to enemy health per wave")]
|
|
128
|
+
[SerializeField] private float _healthMultiplier = 1.1f;
|
|
129
|
+
|
|
130
|
+
[Space(10)]
|
|
131
|
+
[SerializeField] private bool _enableBossWaves = true;
|
|
132
|
+
|
|
133
|
+
// Hide public fields from Inspector
|
|
134
|
+
[HideInInspector] public int CurrentWave;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 2. ScriptableObjects for Data
|
|
141
|
+
|
|
142
|
+
### Data Containers
|
|
143
|
+
|
|
144
|
+
```csharp
|
|
145
|
+
[CreateAssetMenu(fileName = "New Weapon", menuName = "Game/Weapon Data")]
|
|
146
|
+
public class WeaponData : ScriptableObject
|
|
147
|
+
{
|
|
148
|
+
[Header("Identity")]
|
|
149
|
+
public string WeaponName;
|
|
150
|
+
public Sprite Icon;
|
|
151
|
+
[TextArea(3, 5)] public string Description;
|
|
152
|
+
|
|
153
|
+
[Header("Stats")]
|
|
154
|
+
public float Damage = 10f;
|
|
155
|
+
public float AttackSpeed = 1f;
|
|
156
|
+
public float Range = 2f;
|
|
157
|
+
public DamageType Type = DamageType.Physical;
|
|
158
|
+
|
|
159
|
+
[Header("Visual")]
|
|
160
|
+
public GameObject ModelPrefab;
|
|
161
|
+
public AudioClip AttackSound;
|
|
162
|
+
public ParticleSystem HitEffect;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Event Channels (Decoupled Communication)
|
|
167
|
+
|
|
168
|
+
```csharp
|
|
169
|
+
// ScriptableObject as event channel -- no direct references needed
|
|
170
|
+
[CreateAssetMenu(menuName = "Events/Void Event")]
|
|
171
|
+
public class VoidEventChannel : ScriptableObject
|
|
172
|
+
{
|
|
173
|
+
private readonly HashSet<System.Action> _listeners = new();
|
|
174
|
+
|
|
175
|
+
public void Register(System.Action listener) => _listeners.Add(listener);
|
|
176
|
+
public void Unregister(System.Action listener) => _listeners.Remove(listener);
|
|
177
|
+
|
|
178
|
+
public void Raise()
|
|
179
|
+
{
|
|
180
|
+
foreach (var listener in _listeners)
|
|
181
|
+
{
|
|
182
|
+
listener?.Invoke();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
[CreateAssetMenu(menuName = "Events/Int Event")]
|
|
188
|
+
public class IntEventChannel : ScriptableObject
|
|
189
|
+
{
|
|
190
|
+
private readonly HashSet<System.Action<int>> _listeners = new();
|
|
191
|
+
|
|
192
|
+
public void Register(System.Action<int> listener) => _listeners.Add(listener);
|
|
193
|
+
public void Unregister(System.Action<int> listener) => _listeners.Remove(listener);
|
|
194
|
+
|
|
195
|
+
public void Raise(int value)
|
|
196
|
+
{
|
|
197
|
+
foreach (var listener in _listeners)
|
|
198
|
+
{
|
|
199
|
+
listener?.Invoke(value);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Usage
|
|
205
|
+
public class ScoreManager : MonoBehaviour
|
|
206
|
+
{
|
|
207
|
+
[SerializeField] private IntEventChannel _onScoreChanged;
|
|
208
|
+
private int _score;
|
|
209
|
+
|
|
210
|
+
public void AddScore(int points)
|
|
211
|
+
{
|
|
212
|
+
_score += points;
|
|
213
|
+
_onScoreChanged.Raise(_score);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public class ScoreUI : MonoBehaviour
|
|
218
|
+
{
|
|
219
|
+
[SerializeField] private IntEventChannel _onScoreChanged;
|
|
220
|
+
[SerializeField] private TMPro.TMP_Text _scoreText;
|
|
221
|
+
|
|
222
|
+
private void OnEnable() => _onScoreChanged.Register(UpdateScore);
|
|
223
|
+
private void OnDisable() => _onScoreChanged.Unregister(UpdateScore);
|
|
224
|
+
|
|
225
|
+
private void UpdateScore(int score)
|
|
226
|
+
{
|
|
227
|
+
_scoreText.text = $"Score: {score}";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 3. New Input System
|
|
235
|
+
|
|
236
|
+
### Input Actions Setup
|
|
237
|
+
|
|
238
|
+
```csharp
|
|
239
|
+
// PlayerInput component reads from InputActionAsset (.inputactions file)
|
|
240
|
+
// Configure in the Input Actions editor:
|
|
241
|
+
// Action Map: "Player"
|
|
242
|
+
// Move: Vector2 (WASD, Left Stick)
|
|
243
|
+
// Jump: Button (Space, South Button)
|
|
244
|
+
// Attack: Button (Left Click, West Button)
|
|
245
|
+
// Look: Vector2 (Mouse Delta, Right Stick)
|
|
246
|
+
|
|
247
|
+
// Generated C# class from .inputactions file
|
|
248
|
+
using UnityEngine;
|
|
249
|
+
using UnityEngine.InputSystem;
|
|
250
|
+
|
|
251
|
+
public class PlayerController : MonoBehaviour
|
|
252
|
+
{
|
|
253
|
+
[SerializeField] private float _speed = 5f;
|
|
254
|
+
[SerializeField] private float _jumpForce = 10f;
|
|
255
|
+
|
|
256
|
+
private PlayerInputActions _input;
|
|
257
|
+
private Rigidbody _rb;
|
|
258
|
+
private Vector2 _moveInput;
|
|
259
|
+
|
|
260
|
+
private void Awake()
|
|
261
|
+
{
|
|
262
|
+
_input = new PlayerInputActions();
|
|
263
|
+
_rb = GetComponent<Rigidbody>();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private void OnEnable()
|
|
267
|
+
{
|
|
268
|
+
_input.Player.Enable();
|
|
269
|
+
_input.Player.Jump.performed += OnJump;
|
|
270
|
+
_input.Player.Attack.performed += OnAttack;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private void OnDisable()
|
|
274
|
+
{
|
|
275
|
+
_input.Player.Jump.performed -= OnJump;
|
|
276
|
+
_input.Player.Attack.performed -= OnAttack;
|
|
277
|
+
_input.Player.Disable();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private void Update()
|
|
281
|
+
{
|
|
282
|
+
_moveInput = _input.Player.Move.ReadValue<Vector2>();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private void FixedUpdate()
|
|
286
|
+
{
|
|
287
|
+
Vector3 move = new Vector3(_moveInput.x, 0f, _moveInput.y) * _speed;
|
|
288
|
+
_rb.MovePosition(_rb.position + move * Time.fixedDeltaTime);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private void OnJump(InputAction.CallbackContext ctx)
|
|
292
|
+
{
|
|
293
|
+
if (IsGrounded())
|
|
294
|
+
{
|
|
295
|
+
_rb.AddForce(Vector3.up * _jumpForce, ForceMode.Impulse);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private void OnAttack(InputAction.CallbackContext ctx)
|
|
300
|
+
{
|
|
301
|
+
// Trigger attack animation and damage
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private bool IsGrounded()
|
|
305
|
+
{
|
|
306
|
+
return Physics.Raycast(transform.position, Vector3.down, 1.1f);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 4. Addressables (Asset Management)
|
|
314
|
+
|
|
315
|
+
```csharp
|
|
316
|
+
using UnityEngine;
|
|
317
|
+
using UnityEngine.AddressableAssets;
|
|
318
|
+
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
319
|
+
|
|
320
|
+
public class LevelLoader : MonoBehaviour
|
|
321
|
+
{
|
|
322
|
+
[SerializeField] private AssetReference _levelPrefab;
|
|
323
|
+
|
|
324
|
+
private AsyncOperationHandle<GameObject> _handle;
|
|
325
|
+
|
|
326
|
+
public async void LoadLevel()
|
|
327
|
+
{
|
|
328
|
+
_handle = Addressables.InstantiateAsync(_levelPrefab);
|
|
329
|
+
await _handle.Task;
|
|
330
|
+
|
|
331
|
+
if (_handle.Status == AsyncOperationStatus.Succeeded)
|
|
332
|
+
{
|
|
333
|
+
Debug.Log("Level loaded successfully");
|
|
334
|
+
}
|
|
335
|
+
else
|
|
336
|
+
{
|
|
337
|
+
Debug.LogError("Failed to load level");
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// CRITICAL: Always release addressable handles
|
|
342
|
+
private void OnDestroy()
|
|
343
|
+
{
|
|
344
|
+
if (_handle.IsValid())
|
|
345
|
+
{
|
|
346
|
+
Addressables.ReleaseInstance(_handle);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Preload assets
|
|
352
|
+
public class AssetPreloader : MonoBehaviour
|
|
353
|
+
{
|
|
354
|
+
[SerializeField] private AssetLabelReference _preloadLabel;
|
|
355
|
+
|
|
356
|
+
private AsyncOperationHandle _downloadHandle;
|
|
357
|
+
|
|
358
|
+
public async void PreloadAssets()
|
|
359
|
+
{
|
|
360
|
+
var sizeHandle = Addressables.GetDownloadSizeAsync(_preloadLabel);
|
|
361
|
+
await sizeHandle.Task;
|
|
362
|
+
|
|
363
|
+
long downloadSize = sizeHandle.Result;
|
|
364
|
+
if (downloadSize > 0)
|
|
365
|
+
{
|
|
366
|
+
_downloadHandle = Addressables.DownloadDependenciesAsync(_preloadLabel);
|
|
367
|
+
await _downloadHandle.Task;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## 5. Cinemachine (Camera)
|
|
376
|
+
|
|
377
|
+
```csharp
|
|
378
|
+
using UnityEngine;
|
|
379
|
+
using Unity.Cinemachine;
|
|
380
|
+
|
|
381
|
+
// Cinemachine cameras are configured primarily in the Inspector.
|
|
382
|
+
// Each virtual camera has Priority -- highest active priority wins.
|
|
383
|
+
|
|
384
|
+
public class CameraManager : MonoBehaviour
|
|
385
|
+
{
|
|
386
|
+
[SerializeField] private CinemachineCamera _followCam;
|
|
387
|
+
[SerializeField] private CinemachineCamera _aimCam;
|
|
388
|
+
[SerializeField] private CinemachineCamera _cutsceneCam;
|
|
389
|
+
|
|
390
|
+
public void SwitchToAim()
|
|
391
|
+
{
|
|
392
|
+
_followCam.Priority = 0;
|
|
393
|
+
_aimCam.Priority = 10;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
public void SwitchToFollow()
|
|
397
|
+
{
|
|
398
|
+
_aimCam.Priority = 0;
|
|
399
|
+
_followCam.Priority = 10;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Camera shake via Impulse
|
|
403
|
+
public void ShakeCamera(Vector3 velocity)
|
|
404
|
+
{
|
|
405
|
+
var impulse = GetComponent<CinemachineImpulseSource>();
|
|
406
|
+
impulse.GenerateImpulse(velocity);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 6. Physics
|
|
414
|
+
|
|
415
|
+
### Rigidbody and Colliders
|
|
416
|
+
|
|
417
|
+
```csharp
|
|
418
|
+
public class PhysicsObject : MonoBehaviour
|
|
419
|
+
{
|
|
420
|
+
[SerializeField] private float _moveForce = 10f;
|
|
421
|
+
private Rigidbody _rb;
|
|
422
|
+
|
|
423
|
+
private void Awake()
|
|
424
|
+
{
|
|
425
|
+
_rb = GetComponent<Rigidbody>();
|
|
426
|
+
// Configure physics
|
|
427
|
+
_rb.interpolation = RigidbodyInterpolation.Interpolate;
|
|
428
|
+
_rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private void FixedUpdate()
|
|
432
|
+
{
|
|
433
|
+
// Apply forces in FixedUpdate only
|
|
434
|
+
_rb.AddForce(Vector3.forward * _moveForce);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Collision callbacks (requires Collider, NOT trigger)
|
|
438
|
+
private void OnCollisionEnter(Collision collision)
|
|
439
|
+
{
|
|
440
|
+
ContactPoint contact = collision.GetContact(0);
|
|
441
|
+
Debug.Log($"Hit {collision.gameObject.name} at {contact.point}");
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Trigger callbacks (Collider with Is Trigger = true)
|
|
445
|
+
private void OnTriggerEnter(Collider other)
|
|
446
|
+
{
|
|
447
|
+
if (other.CompareTag("Collectible"))
|
|
448
|
+
{
|
|
449
|
+
Destroy(other.gameObject);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Raycasting
|
|
456
|
+
|
|
457
|
+
```csharp
|
|
458
|
+
public class RaycastShooter : MonoBehaviour
|
|
459
|
+
{
|
|
460
|
+
[SerializeField] private float _range = 100f;
|
|
461
|
+
[SerializeField] private LayerMask _hitLayers;
|
|
462
|
+
[SerializeField] private float _damage = 25f;
|
|
463
|
+
|
|
464
|
+
public void Shoot()
|
|
465
|
+
{
|
|
466
|
+
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
|
467
|
+
|
|
468
|
+
if (Physics.Raycast(ray, out RaycastHit hit, _range, _hitLayers))
|
|
469
|
+
{
|
|
470
|
+
if (hit.collider.TryGetComponent<IDamageable>(out var target))
|
|
471
|
+
{
|
|
472
|
+
target.TakeDamage(_damage, DamageType.Projectile);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Spawn impact effect at hit point
|
|
476
|
+
SpawnImpact(hit.point, hit.normal);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// SphereCast for wider detection (melee attacks)
|
|
481
|
+
public bool CheckMeleeHit(out RaycastHit hit)
|
|
482
|
+
{
|
|
483
|
+
return Physics.SphereCast(
|
|
484
|
+
transform.position,
|
|
485
|
+
0.5f, // radius
|
|
486
|
+
transform.forward,
|
|
487
|
+
out hit,
|
|
488
|
+
2f, // max distance
|
|
489
|
+
_hitLayers
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## 7. Animation
|
|
498
|
+
|
|
499
|
+
### Animator Controller
|
|
500
|
+
|
|
501
|
+
```csharp
|
|
502
|
+
public class CharacterAnimator : MonoBehaviour
|
|
503
|
+
{
|
|
504
|
+
private Animator _animator;
|
|
505
|
+
private static readonly int SpeedHash = Animator.StringToHash("Speed");
|
|
506
|
+
private static readonly int JumpHash = Animator.StringToHash("Jump");
|
|
507
|
+
private static readonly int AttackHash = Animator.StringToHash("Attack");
|
|
508
|
+
private static readonly int IsGroundedHash = Animator.StringToHash("IsGrounded");
|
|
509
|
+
private static readonly int DieHash = Animator.StringToHash("Die");
|
|
510
|
+
|
|
511
|
+
private void Awake()
|
|
512
|
+
{
|
|
513
|
+
_animator = GetComponent<Animator>();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
public void SetSpeed(float speed)
|
|
517
|
+
{
|
|
518
|
+
// Use hashed parameter names for performance
|
|
519
|
+
_animator.SetFloat(SpeedHash, speed);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
public void TriggerJump() => _animator.SetTrigger(JumpHash);
|
|
523
|
+
public void TriggerAttack() => _animator.SetTrigger(AttackHash);
|
|
524
|
+
public void SetGrounded(bool grounded) => _animator.SetBool(IsGroundedHash, grounded);
|
|
525
|
+
public void TriggerDeath() => _animator.SetTrigger(DieHash);
|
|
526
|
+
|
|
527
|
+
// Animation Events (called from animation clips)
|
|
528
|
+
public void OnAttackHit()
|
|
529
|
+
{
|
|
530
|
+
// Deal damage at the exact frame configured in the animation
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
public void OnFootstep()
|
|
534
|
+
{
|
|
535
|
+
// Play footstep sound
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Blend Trees
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
// Configure in Animator window:
|
|
544
|
+
// 1D Blend Tree: Speed (0=idle, 0.5=walk, 1=run)
|
|
545
|
+
// 2D Blend Tree: MoveX + MoveY (directional movement)
|
|
546
|
+
|
|
547
|
+
// Set blend parameters from code:
|
|
548
|
+
_animator.SetFloat("MoveX", moveInput.x);
|
|
549
|
+
_animator.SetFloat("MoveY", moveInput.y);
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## 8. Async Patterns
|
|
555
|
+
|
|
556
|
+
### Coroutines
|
|
557
|
+
|
|
558
|
+
```csharp
|
|
559
|
+
public class SpawnManager : MonoBehaviour
|
|
560
|
+
{
|
|
561
|
+
[SerializeField] private GameObject _enemyPrefab;
|
|
562
|
+
[SerializeField] private float _spawnDelay = 2f;
|
|
563
|
+
|
|
564
|
+
private Coroutine _spawnRoutine;
|
|
565
|
+
|
|
566
|
+
private void Start()
|
|
567
|
+
{
|
|
568
|
+
_spawnRoutine = StartCoroutine(SpawnLoop());
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private IEnumerator SpawnLoop()
|
|
572
|
+
{
|
|
573
|
+
while (true)
|
|
574
|
+
{
|
|
575
|
+
SpawnEnemy();
|
|
576
|
+
yield return new WaitForSeconds(_spawnDelay);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
private IEnumerator FadeOut(CanvasGroup group, float duration)
|
|
581
|
+
{
|
|
582
|
+
float elapsed = 0f;
|
|
583
|
+
float startAlpha = group.alpha;
|
|
584
|
+
|
|
585
|
+
while (elapsed < duration)
|
|
586
|
+
{
|
|
587
|
+
elapsed += Time.deltaTime;
|
|
588
|
+
group.alpha = Mathf.Lerp(startAlpha, 0f, elapsed / duration);
|
|
589
|
+
yield return null; // Wait one frame
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
group.alpha = 0f;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private void OnDisable()
|
|
596
|
+
{
|
|
597
|
+
if (_spawnRoutine != null)
|
|
598
|
+
{
|
|
599
|
+
StopCoroutine(_spawnRoutine);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### Async/Await with UniTask
|
|
606
|
+
|
|
607
|
+
```csharp
|
|
608
|
+
// Install via Package Manager: com.cysharp.unitask
|
|
609
|
+
using Cysharp.Threading.Tasks;
|
|
610
|
+
using UnityEngine;
|
|
611
|
+
|
|
612
|
+
public class AsyncExample : MonoBehaviour
|
|
613
|
+
{
|
|
614
|
+
public async UniTaskVoid LoadAndSpawn()
|
|
615
|
+
{
|
|
616
|
+
// Delay without coroutine
|
|
617
|
+
await UniTask.Delay(System.TimeSpan.FromSeconds(2f));
|
|
618
|
+
|
|
619
|
+
// Wait for a condition
|
|
620
|
+
await UniTask.WaitUntil(() => _isReady);
|
|
621
|
+
|
|
622
|
+
// Parallel tasks
|
|
623
|
+
var (enemyData, levelData) = await UniTask.WhenAll(
|
|
624
|
+
LoadEnemyDataAsync(),
|
|
625
|
+
LoadLevelDataAsync()
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
// Frame-based delay
|
|
629
|
+
await UniTask.DelayFrame(5);
|
|
630
|
+
|
|
631
|
+
// Cancellation support
|
|
632
|
+
var cts = new System.Threading.CancellationTokenSource();
|
|
633
|
+
await UniTask.Delay(1000, cancellationToken: cts.Token);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
private void OnDestroy()
|
|
637
|
+
{
|
|
638
|
+
// Cancel pending tasks when destroyed
|
|
639
|
+
this.GetCancellationTokenOnDestroy();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## 9. Object Pooling
|
|
647
|
+
|
|
648
|
+
```csharp
|
|
649
|
+
using UnityEngine;
|
|
650
|
+
using UnityEngine.Pool;
|
|
651
|
+
|
|
652
|
+
public class ProjectilePool : MonoBehaviour
|
|
653
|
+
{
|
|
654
|
+
[SerializeField] private Projectile _prefab;
|
|
655
|
+
[SerializeField] private int _defaultCapacity = 20;
|
|
656
|
+
[SerializeField] private int _maxSize = 100;
|
|
657
|
+
|
|
658
|
+
private ObjectPool<Projectile> _pool;
|
|
659
|
+
|
|
660
|
+
private void Awake()
|
|
661
|
+
{
|
|
662
|
+
_pool = new ObjectPool<Projectile>(
|
|
663
|
+
createFunc: () =>
|
|
664
|
+
{
|
|
665
|
+
var obj = Instantiate(_prefab);
|
|
666
|
+
obj.SetPool(_pool);
|
|
667
|
+
return obj;
|
|
668
|
+
},
|
|
669
|
+
actionOnGet: obj =>
|
|
670
|
+
{
|
|
671
|
+
obj.gameObject.SetActive(true);
|
|
672
|
+
},
|
|
673
|
+
actionOnRelease: obj =>
|
|
674
|
+
{
|
|
675
|
+
obj.gameObject.SetActive(false);
|
|
676
|
+
},
|
|
677
|
+
actionOnDestroy: obj =>
|
|
678
|
+
{
|
|
679
|
+
Destroy(obj.gameObject);
|
|
680
|
+
},
|
|
681
|
+
collectionCheck: true,
|
|
682
|
+
defaultCapacity: _defaultCapacity,
|
|
683
|
+
maxSize: _maxSize
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
public Projectile Get() => _pool.Get();
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// In Projectile.cs
|
|
691
|
+
public class Projectile : MonoBehaviour
|
|
692
|
+
{
|
|
693
|
+
private ObjectPool<Projectile> _pool;
|
|
694
|
+
|
|
695
|
+
public void SetPool(ObjectPool<Projectile> pool) => _pool = pool;
|
|
696
|
+
|
|
697
|
+
public void ReturnToPool()
|
|
698
|
+
{
|
|
699
|
+
_pool.Release(this);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
## 10. NavMesh Navigation
|
|
707
|
+
|
|
708
|
+
```csharp
|
|
709
|
+
using UnityEngine;
|
|
710
|
+
using UnityEngine.AI;
|
|
711
|
+
|
|
712
|
+
[RequireComponent(typeof(NavMeshAgent))]
|
|
713
|
+
public class EnemyAI : MonoBehaviour
|
|
714
|
+
{
|
|
715
|
+
[SerializeField] private float _detectionRange = 15f;
|
|
716
|
+
[SerializeField] private float _attackRange = 2f;
|
|
717
|
+
[SerializeField] private float _patrolSpeed = 2f;
|
|
718
|
+
[SerializeField] private float _chaseSpeed = 5f;
|
|
719
|
+
[SerializeField] private Transform[] _patrolPoints;
|
|
720
|
+
|
|
721
|
+
private NavMeshAgent _agent;
|
|
722
|
+
private Transform _target;
|
|
723
|
+
private int _patrolIndex;
|
|
724
|
+
|
|
725
|
+
private void Awake()
|
|
726
|
+
{
|
|
727
|
+
_agent = GetComponent<NavMeshAgent>();
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
private void Update()
|
|
731
|
+
{
|
|
732
|
+
float distanceToTarget = _target != null
|
|
733
|
+
? Vector3.Distance(transform.position, _target.position)
|
|
734
|
+
: float.MaxValue;
|
|
735
|
+
|
|
736
|
+
if (distanceToTarget <= _attackRange)
|
|
737
|
+
{
|
|
738
|
+
Attack();
|
|
739
|
+
}
|
|
740
|
+
else if (distanceToTarget <= _detectionRange)
|
|
741
|
+
{
|
|
742
|
+
Chase();
|
|
743
|
+
}
|
|
744
|
+
else
|
|
745
|
+
{
|
|
746
|
+
Patrol();
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
private void Patrol()
|
|
751
|
+
{
|
|
752
|
+
_agent.speed = _patrolSpeed;
|
|
753
|
+
|
|
754
|
+
if (_patrolPoints.Length == 0) return;
|
|
755
|
+
|
|
756
|
+
if (!_agent.pathPending && _agent.remainingDistance < 0.5f)
|
|
757
|
+
{
|
|
758
|
+
_patrolIndex = (_patrolIndex + 1) % _patrolPoints.Length;
|
|
759
|
+
_agent.SetDestination(_patrolPoints[_patrolIndex].position);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
private void Chase()
|
|
764
|
+
{
|
|
765
|
+
_agent.speed = _chaseSpeed;
|
|
766
|
+
_agent.SetDestination(_target.position);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
private void Attack()
|
|
770
|
+
{
|
|
771
|
+
_agent.ResetPath();
|
|
772
|
+
transform.LookAt(_target);
|
|
773
|
+
// Trigger attack logic
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
---
|
|
779
|
+
|
|
780
|
+
## 11. Rendering (URP/HDRP)
|
|
781
|
+
|
|
782
|
+
### URP Setup
|
|
783
|
+
|
|
784
|
+
```csharp
|
|
785
|
+
// URP is configured via UniversalRenderPipelineAsset
|
|
786
|
+
// Project Settings > Graphics > Scriptable Render Pipeline Settings
|
|
787
|
+
|
|
788
|
+
// Shader Graph: visual shader editor (replaces writing HLSL by hand)
|
|
789
|
+
// Create > Shader Graph > URP > Lit Shader Graph
|
|
790
|
+
|
|
791
|
+
// Post-processing: add Volume component to a GameObject
|
|
792
|
+
// Add overrides: Bloom, Color Adjustments, Vignette, Tonemapping
|
|
793
|
+
|
|
794
|
+
// Custom Renderer Features (URP)
|
|
795
|
+
public class CustomRenderPass : ScriptableRenderPass
|
|
796
|
+
{
|
|
797
|
+
public override void Execute(ScriptableRenderContext context,
|
|
798
|
+
ref RenderingData renderingData)
|
|
799
|
+
{
|
|
800
|
+
// Custom rendering logic
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
## 12. UI Toolkit
|
|
808
|
+
|
|
809
|
+
### UXML Structure
|
|
810
|
+
|
|
811
|
+
```xml
|
|
812
|
+
<!-- Assets/UI/MainMenu.uxml -->
|
|
813
|
+
<ui:UXML xmlns:ui="UnityEngine.UIElements">
|
|
814
|
+
<ui:VisualElement name="root" class="menu-root">
|
|
815
|
+
<ui:Label name="title" text="My Game" class="title-label" />
|
|
816
|
+
<ui:Button name="play-btn" text="Play" class="menu-button" />
|
|
817
|
+
<ui:Button name="settings-btn" text="Settings" class="menu-button" />
|
|
818
|
+
<ui:Button name="quit-btn" text="Quit" class="menu-button" />
|
|
819
|
+
</ui:VisualElement>
|
|
820
|
+
</ui:UXML>
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### USS Styling
|
|
824
|
+
|
|
825
|
+
```css
|
|
826
|
+
/* Assets/UI/MainMenu.uss */
|
|
827
|
+
.menu-root {
|
|
828
|
+
flex-grow: 1;
|
|
829
|
+
align-items: center;
|
|
830
|
+
justify-content: center;
|
|
831
|
+
background-color: rgba(0, 0, 0, 0.8);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
.title-label {
|
|
835
|
+
font-size: 48px;
|
|
836
|
+
color: white;
|
|
837
|
+
margin-bottom: 40px;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.menu-button {
|
|
841
|
+
width: 200px;
|
|
842
|
+
height: 50px;
|
|
843
|
+
margin: 5px;
|
|
844
|
+
font-size: 20px;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
.menu-button:hover {
|
|
848
|
+
background-color: rgb(80, 80, 80);
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
### C# Controller
|
|
853
|
+
|
|
854
|
+
```csharp
|
|
855
|
+
using UnityEngine;
|
|
856
|
+
using UnityEngine.UIElements;
|
|
857
|
+
|
|
858
|
+
public class MainMenuController : MonoBehaviour
|
|
859
|
+
{
|
|
860
|
+
private void OnEnable()
|
|
861
|
+
{
|
|
862
|
+
var root = GetComponent<UIDocument>().rootVisualElement;
|
|
863
|
+
|
|
864
|
+
root.Q<Button>("play-btn").clicked += OnPlayClicked;
|
|
865
|
+
root.Q<Button>("settings-btn").clicked += OnSettingsClicked;
|
|
866
|
+
root.Q<Button>("quit-btn").clicked += OnQuitClicked;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
private void OnPlayClicked()
|
|
870
|
+
{
|
|
871
|
+
UnityEngine.SceneManagement.SceneManager.LoadScene("GameScene");
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
private void OnSettingsClicked()
|
|
875
|
+
{
|
|
876
|
+
// Show settings panel
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
private void OnQuitClicked()
|
|
880
|
+
{
|
|
881
|
+
Application.Quit();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## 13. Multiplayer (Netcode for GameObjects)
|
|
889
|
+
|
|
890
|
+
```csharp
|
|
891
|
+
using Unity.Netcode;
|
|
892
|
+
using UnityEngine;
|
|
893
|
+
|
|
894
|
+
public class NetworkPlayer : NetworkBehaviour
|
|
895
|
+
{
|
|
896
|
+
// NetworkVariable: automatically synced across clients
|
|
897
|
+
private NetworkVariable<int> _health = new(
|
|
898
|
+
100,
|
|
899
|
+
NetworkVariableReadPermission.Everyone,
|
|
900
|
+
NetworkVariableWritePermission.Server
|
|
901
|
+
);
|
|
902
|
+
|
|
903
|
+
private NetworkVariable<Vector3> _networkPosition = new(
|
|
904
|
+
default,
|
|
905
|
+
NetworkVariableReadPermission.Everyone,
|
|
906
|
+
NetworkVariableWritePermission.Owner
|
|
907
|
+
);
|
|
908
|
+
|
|
909
|
+
public override void OnNetworkSpawn()
|
|
910
|
+
{
|
|
911
|
+
_health.OnValueChanged += OnHealthChanged;
|
|
912
|
+
|
|
913
|
+
if (IsOwner)
|
|
914
|
+
{
|
|
915
|
+
// Enable input for local player only
|
|
916
|
+
GetComponent<PlayerInput>().enabled = true;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
private void OnHealthChanged(int oldValue, int newValue)
|
|
921
|
+
{
|
|
922
|
+
// Update health bar UI
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// ServerRpc: client calls, server executes
|
|
926
|
+
[ServerRpc]
|
|
927
|
+
public void TakeDamageServerRpc(int amount)
|
|
928
|
+
{
|
|
929
|
+
_health.Value = Mathf.Max(0, _health.Value - amount);
|
|
930
|
+
|
|
931
|
+
if (_health.Value <= 0)
|
|
932
|
+
{
|
|
933
|
+
DieClientRpc();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// ClientRpc: server calls, all clients execute
|
|
938
|
+
[ClientRpc]
|
|
939
|
+
private void DieClientRpc()
|
|
940
|
+
{
|
|
941
|
+
// Play death animation on all clients
|
|
942
|
+
GetComponent<Animator>().SetTrigger("Die");
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
---
|
|
948
|
+
|
|
949
|
+
## 14. Testing (Unity Test Framework)
|
|
950
|
+
|
|
951
|
+
### EditMode Tests
|
|
952
|
+
|
|
953
|
+
```csharp
|
|
954
|
+
using NUnit.Framework;
|
|
955
|
+
|
|
956
|
+
[TestFixture]
|
|
957
|
+
public class WeaponDataTests
|
|
958
|
+
{
|
|
959
|
+
[Test]
|
|
960
|
+
public void DamageCalculation_WithMultiplier_ReturnsCorrectValue()
|
|
961
|
+
{
|
|
962
|
+
float baseDamage = 10f;
|
|
963
|
+
float multiplier = 1.5f;
|
|
964
|
+
|
|
965
|
+
float result = DamageCalculator.Calculate(baseDamage, multiplier);
|
|
966
|
+
|
|
967
|
+
Assert.AreEqual(15f, result, 0.001f);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
[TestCase(0f, 1f, 0f)]
|
|
971
|
+
[TestCase(10f, 2f, 20f)]
|
|
972
|
+
[TestCase(5f, 0.5f, 2.5f)]
|
|
973
|
+
public void DamageCalculation_VariousInputs(float damage, float mult, float expected)
|
|
974
|
+
{
|
|
975
|
+
float result = DamageCalculator.Calculate(damage, mult);
|
|
976
|
+
Assert.AreEqual(expected, result, 0.001f);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
### PlayMode Tests
|
|
982
|
+
|
|
983
|
+
```csharp
|
|
984
|
+
using System.Collections;
|
|
985
|
+
using NUnit.Framework;
|
|
986
|
+
using UnityEngine;
|
|
987
|
+
using UnityEngine.TestTools;
|
|
988
|
+
|
|
989
|
+
public class PlayerMovementTests
|
|
990
|
+
{
|
|
991
|
+
private GameObject _player;
|
|
992
|
+
|
|
993
|
+
[UnitySetUp]
|
|
994
|
+
public IEnumerator SetUp()
|
|
995
|
+
{
|
|
996
|
+
_player = new GameObject("Player");
|
|
997
|
+
_player.AddComponent<Rigidbody>();
|
|
998
|
+
_player.AddComponent<PlayerController>();
|
|
999
|
+
yield return null; // Wait one frame for Start()
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
[UnityTearDown]
|
|
1003
|
+
public IEnumerator TearDown()
|
|
1004
|
+
{
|
|
1005
|
+
Object.Destroy(_player);
|
|
1006
|
+
yield return null;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
[UnityTest]
|
|
1010
|
+
public IEnumerator Player_FallsWithGravity()
|
|
1011
|
+
{
|
|
1012
|
+
float startY = _player.transform.position.y;
|
|
1013
|
+
yield return new WaitForSeconds(1f);
|
|
1014
|
+
Assert.Less(_player.transform.position.y, startY);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
---
|
|
1020
|
+
|
|
1021
|
+
## 15. DOTS/Burst for Performance
|
|
1022
|
+
|
|
1023
|
+
```csharp
|
|
1024
|
+
using Unity.Entities;
|
|
1025
|
+
using Unity.Transforms;
|
|
1026
|
+
using Unity.Mathematics;
|
|
1027
|
+
using Unity.Burst;
|
|
1028
|
+
|
|
1029
|
+
// Component (data only)
|
|
1030
|
+
public struct MoveSpeed : IComponentData
|
|
1031
|
+
{
|
|
1032
|
+
public float Value;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
public struct TargetPosition : IComponentData
|
|
1036
|
+
{
|
|
1037
|
+
public float3 Value;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// System (logic only)
|
|
1041
|
+
[BurstCompile]
|
|
1042
|
+
public partial struct MoveTowardSystem : ISystem
|
|
1043
|
+
{
|
|
1044
|
+
[BurstCompile]
|
|
1045
|
+
public void OnUpdate(ref SystemState state)
|
|
1046
|
+
{
|
|
1047
|
+
float deltaTime = SystemAPI.Time.DeltaTime;
|
|
1048
|
+
|
|
1049
|
+
foreach (var (transform, speed, target) in
|
|
1050
|
+
SystemAPI.Query<RefRW<LocalTransform>, RefRO<MoveSpeed>, RefRO<TargetPosition>>())
|
|
1051
|
+
{
|
|
1052
|
+
float3 direction = math.normalize(target.ValueRO.Value - transform.ValueRO.Position);
|
|
1053
|
+
transform.ValueRW.Position += direction * speed.ValueRO.Value * deltaTime;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// Jobs for parallel processing
|
|
1059
|
+
[BurstCompile]
|
|
1060
|
+
public partial struct MoveJob : IJobEntity
|
|
1061
|
+
{
|
|
1062
|
+
public float DeltaTime;
|
|
1063
|
+
|
|
1064
|
+
public void Execute(ref LocalTransform transform, in MoveSpeed speed, in TargetPosition target)
|
|
1065
|
+
{
|
|
1066
|
+
float3 direction = math.normalize(target.Value - transform.Position);
|
|
1067
|
+
transform.Position += direction * speed.Value * DeltaTime;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
## 16. Common Patterns
|
|
1075
|
+
|
|
1076
|
+
### Service Locator
|
|
1077
|
+
|
|
1078
|
+
```csharp
|
|
1079
|
+
public static class ServiceLocator
|
|
1080
|
+
{
|
|
1081
|
+
private static readonly Dictionary<System.Type, object> _services = new();
|
|
1082
|
+
|
|
1083
|
+
public static void Register<T>(T service) where T : class
|
|
1084
|
+
{
|
|
1085
|
+
_services[typeof(T)] = service;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
public static T Get<T>() where T : class
|
|
1089
|
+
{
|
|
1090
|
+
if (_services.TryGetValue(typeof(T), out var service))
|
|
1091
|
+
{
|
|
1092
|
+
return service as T;
|
|
1093
|
+
}
|
|
1094
|
+
throw new System.InvalidOperationException(
|
|
1095
|
+
$"Service {typeof(T).Name} not registered");
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
public static void Clear() => _services.Clear();
|
|
1099
|
+
}
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
### Save System
|
|
1103
|
+
|
|
1104
|
+
```csharp
|
|
1105
|
+
using UnityEngine;
|
|
1106
|
+
using System.IO;
|
|
1107
|
+
|
|
1108
|
+
public static class SaveSystem
|
|
1109
|
+
{
|
|
1110
|
+
private static string SavePath => Path.Combine(Application.persistentDataPath, "save.json");
|
|
1111
|
+
|
|
1112
|
+
public static void Save<T>(T data)
|
|
1113
|
+
{
|
|
1114
|
+
string json = JsonUtility.ToJson(data, prettyPrint: true);
|
|
1115
|
+
File.WriteAllText(SavePath, json);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
public static T Load<T>() where T : new()
|
|
1119
|
+
{
|
|
1120
|
+
if (!File.Exists(SavePath))
|
|
1121
|
+
{
|
|
1122
|
+
return new T();
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
string json = File.ReadAllText(SavePath);
|
|
1126
|
+
return JsonUtility.FromJson<T>(json);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
public static void Delete()
|
|
1130
|
+
{
|
|
1131
|
+
if (File.Exists(SavePath))
|
|
1132
|
+
{
|
|
1133
|
+
File.Delete(SavePath);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
[System.Serializable]
|
|
1139
|
+
public class SaveData
|
|
1140
|
+
{
|
|
1141
|
+
public int Level;
|
|
1142
|
+
public float PlayTime;
|
|
1143
|
+
public int Score;
|
|
1144
|
+
public Vector3 PlayerPosition;
|
|
1145
|
+
public List<string> UnlockedItems;
|
|
1146
|
+
}
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
---
|
|
1150
|
+
|
|
1151
|
+
## 17. Anti-Patterns
|
|
1152
|
+
|
|
1153
|
+
### NEVER
|
|
1154
|
+
|
|
1155
|
+
- Use `Find()`, `FindObjectOfType()`, or `FindWithTag()` in Update (cache references in Awake)
|
|
1156
|
+
- Use string-based `Invoke()` or `SendMessage()` (use events, interfaces, or direct calls)
|
|
1157
|
+
- Modify Rigidbody in Update (use FixedUpdate for physics)
|
|
1158
|
+
- Instantiate/Destroy frequently (use object pooling)
|
|
1159
|
+
- Use `Camera.main` in Update without caching (it calls FindWithTag internally)
|
|
1160
|
+
- Compare tags with `gameObject.tag == "Enemy"` (use `CompareTag("Enemy")` -- no allocation)
|
|
1161
|
+
- Use `GetComponent` in Update loops (cache in Awake)
|
|
1162
|
+
- Put all logic in one MonoBehaviour (split into focused components)
|
|
1163
|
+
|
|
1164
|
+
### ALWAYS
|
|
1165
|
+
|
|
1166
|
+
- Cache component references in Awake
|
|
1167
|
+
- Use `[SerializeField] private` instead of public fields
|
|
1168
|
+
- Use `TryGetComponent` instead of null-checking `GetComponent`
|
|
1169
|
+
- Use ScriptableObjects for shared data and event channels
|
|
1170
|
+
- Hash animator parameters with `Animator.StringToHash`
|
|
1171
|
+
- Use layer masks for physics queries (avoid checking everything)
|
|
1172
|
+
- Unsubscribe from events in OnDisable to prevent memory leaks
|
|
1173
|
+
- Use `CompareTag` for tag comparisons (zero allocation)
|
|
1174
|
+
- Profile with the Unity Profiler before optimizing
|
|
1175
|
+
- Use assembly definitions for faster compilation in large projects
|