nccgs 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/.claude/agents/nccgs-accessibility-specialist.md +26 -0
- package/.claude/agents/nccgs-adversarial-reviewer.md +26 -0
- package/.claude/agents/nccgs-ai-programmer.md +26 -0
- package/.claude/agents/nccgs-analytics-engineer.md +26 -0
- package/.claude/agents/nccgs-art-direction-lead.md +26 -0
- package/.claude/agents/nccgs-audio-direction-lead.md +26 -0
- package/.claude/agents/nccgs-creative-director.md +26 -0
- package/.claude/agents/nccgs-documentation-manager.md +26 -0
- package/.claude/agents/nccgs-economy-designer.md +26 -0
- package/.claude/agents/nccgs-engine-programmer.md +26 -0
- package/.claude/agents/nccgs-game-design-lead.md +26 -0
- package/.claude/agents/nccgs-game-designer.md +26 -0
- package/.claude/agents/nccgs-gameplay-programmer.md +26 -0
- package/.claude/agents/nccgs-level-designer.md +26 -0
- package/.claude/agents/nccgs-live-ops-designer.md +26 -0
- package/.claude/agents/nccgs-localization-lead.md +26 -0
- package/.claude/agents/nccgs-narrative-lead.md +26 -0
- package/.claude/agents/nccgs-network-programmer.md +26 -0
- package/.claude/agents/nccgs-performance-analyst.md +26 -0
- package/.claude/agents/nccgs-producer.md +26 -0
- package/.claude/agents/nccgs-production-coordinator.md +23 -0
- package/.claude/agents/nccgs-programming-lead.md +26 -0
- package/.claude/agents/nccgs-prototyper.md +26 -0
- package/.claude/agents/nccgs-qa-engineer.md +26 -0
- package/.claude/agents/nccgs-qa-lead.md +26 -0
- package/.claude/agents/nccgs-release-engineer.md +26 -0
- package/.claude/agents/nccgs-release-lead.md +26 -0
- package/.claude/agents/nccgs-security-engineer.md +26 -0
- package/.claude/agents/nccgs-sound-designer.md +26 -0
- package/.claude/agents/nccgs-systems-designer.md +26 -0
- package/.claude/agents/nccgs-technical-architect.md +26 -0
- package/.claude/agents/nccgs-technical-artist.md +26 -0
- package/.claude/agents/nccgs-technical-director.md +26 -0
- package/.claude/agents/nccgs-tools-programmer.md +26 -0
- package/.claude/agents/nccgs-ui-programmer.md +26 -0
- package/.claude/agents/nccgs-unity-build-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-content-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-implementer.md +26 -0
- package/.claude/agents/nccgs-unity-rendering-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-systems-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-ui-specialist.md +26 -0
- package/.claude/agents/nccgs-ux-designer.md +26 -0
- package/.claude/agents/nccgs-verification-engineer.md +26 -0
- package/.claude/agents/nccgs-world-builder.md +26 -0
- package/.claude/agents/nccgs-writer.md +26 -0
- package/.claude/nccgs/THIRD_PARTY_NOTICES.md +13 -0
- package/.claude/nccgs/VERSION +1 -0
- package/.claude/nccgs/constitution.md +131 -0
- package/.claude/nccgs/hooks/agent-audit.mjs +10 -0
- package/.claude/nccgs/hooks/common.mjs +41 -0
- package/.claude/nccgs/hooks/post-compact.mjs +1 -0
- package/.claude/nccgs/hooks/pre-compact.mjs +8 -0
- package/.claude/nccgs/hooks/protect-git.mjs +22 -0
- package/.claude/nccgs/hooks/protect-write.mjs +17 -0
- package/.claude/nccgs/hooks/session-start.mjs +15 -0
- package/.claude/nccgs/hooks/session-stop.mjs +7 -0
- package/.claude/nccgs/protocols/agent-contract.md +34 -0
- package/.claude/nccgs/protocols/context-packets.md +13 -0
- package/.claude/nccgs/protocols/evidence.md +15 -0
- package/.claude/nccgs/protocols/model-routing.md +22 -0
- package/.claude/nccgs/protocols/orchestration.md +26 -0
- package/.claude/nccgs/protocols/unity-boundary.md +11 -0
- package/.claude/nccgs/settings.fragment.json +77 -0
- package/.claude/nccgs/studio.json +347 -0
- package/.claude/nccgs/tools/configure-models.mjs +39 -0
- package/.claude/nccgs/unity-skills-manifest.json +727 -0
- package/.claude/nccgs/workflow-catalog.json +317 -0
- package/.claude/rules/nccgs-canonical-docs.md +19 -0
- package/.claude/rules/nccgs-editor-tools.md +12 -0
- package/.claude/rules/nccgs-localization.md +12 -0
- package/.claude/rules/nccgs-networking.md +13 -0
- package/.claude/rules/nccgs-performance.md +14 -0
- package/.claude/rules/nccgs-rendering.md +16 -0
- package/.claude/rules/nccgs-security.md +13 -0
- package/.claude/rules/nccgs-tests.md +18 -0
- package/.claude/rules/nccgs-ui.md +14 -0
- package/.claude/rules/nccgs-unity-assets.md +21 -0
- package/.claude/rules/nccgs-unity-code.md +22 -0
- package/.claude/skills/accessibility-review/SKILL.md +14 -0
- package/.claude/skills/architecture-decision/SKILL.md +14 -0
- package/.claude/skills/asset-audit/SKILL.md +14 -0
- package/.claude/skills/audit/SKILL.md +16 -0
- package/.claude/skills/audit/references/dimensions.md +46 -0
- package/.claude/skills/balance-review/SKILL.md +14 -0
- package/.claude/skills/bug-triage/SKILL.md +14 -0
- package/.claude/skills/build-live-game/SKILL.md +317 -0
- package/.claude/skills/build-live-game/references/achievements.md +779 -0
- package/.claude/skills/build-live-game/references/apis.md +280 -0
- package/.claude/skills/build-live-game/references/authentication.md +437 -0
- package/.claude/skills/build-live-game/references/battlepass.md +860 -0
- package/.claude/skills/build-live-game/references/cloud-code.md +563 -0
- package/.claude/skills/build-live-game/references/cloud-save.md +474 -0
- package/.claude/skills/build-live-game/references/deployment.md +216 -0
- package/.claude/skills/build-live-game/references/player-account.md +813 -0
- package/.claude/skills/build-live-game/references/remote-config.md +96 -0
- package/.claude/skills/build-live-game/references/tooling.md +431 -0
- package/.claude/skills/closure/SKILL.md +14 -0
- package/.claude/skills/code-review/SKILL.md +14 -0
- package/.claude/skills/compatibility-review/SKILL.md +14 -0
- package/.claude/skills/context-pack/SKILL.md +14 -0
- package/.claude/skills/dependency-review/SKILL.md +14 -0
- package/.claude/skills/design/SKILL.md +20 -0
- package/.claude/skills/design-review/SKILL.md +14 -0
- package/.claude/skills/evidence-review/SKILL.md +14 -0
- package/.claude/skills/hotfix/SKILL.md +14 -0
- package/.claude/skills/implement-in-app-purchases/README.md +233 -0
- package/.claude/skills/implement-in-app-purchases/SKILL.md +158 -0
- package/.claude/skills/implement-in-app-purchases/references/api-notes.md +562 -0
- package/.claude/skills/implement-in-app-purchases/references/codeless-catalog.md +331 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-adapty.md +268 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-essentialkit.md +239 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-revenuecat.md +275 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-unipay.md +145 -0
- package/.claude/skills/implement-in-app-purchases/references/migration-v4-to-v5.md +204 -0
- package/.claude/skills/implement-in-app-purchases/references/path-add-iap-to-new-project.md +198 -0
- package/.claude/skills/implement-in-app-purchases/references/path-convert-native-google-billing.md +356 -0
- package/.claude/skills/implement-in-app-purchases/references/path-convert-native-storekit.md +446 -0
- package/.claude/skills/implement-in-app-purchases/references/path-implement-iap-d2c.md +680 -0
- package/.claude/skills/implement-in-app-purchases/references/platform-notes.md +204 -0
- package/.claude/skills/implement-in-app-purchases/references/pre-check.md +205 -0
- package/.claude/skills/incident-recovery/SKILL.md +14 -0
- package/.claude/skills/initialize-ai-navigation/SKILL.md +95 -0
- package/.claude/skills/initialize-ai-navigation/references/navigation-system.md +794 -0
- package/.claude/skills/levelplay-unity-integration/CHANGELOG.md +57 -0
- package/.claude/skills/levelplay-unity-integration/README.md +74 -0
- package/.claude/skills/levelplay-unity-integration/SKILL.md +1126 -0
- package/.claude/skills/levelplay-unity-integration/references/banner-api.md +920 -0
- package/.claude/skills/levelplay-unity-integration/references/best-practices.md +536 -0
- package/.claude/skills/levelplay-unity-integration/references/ilrd-api.md +337 -0
- package/.claude/skills/levelplay-unity-integration/references/initialization-api.md +630 -0
- package/.claude/skills/levelplay-unity-integration/references/interstitial-api.md +899 -0
- package/.claude/skills/levelplay-unity-integration/references/ios-setup.md +491 -0
- package/.claude/skills/levelplay-unity-integration/references/migration-sdk-9.md +666 -0
- package/.claude/skills/levelplay-unity-integration/references/privacy-settings.md +608 -0
- package/.claude/skills/levelplay-unity-integration/references/rewarded-api.md +902 -0
- package/.claude/skills/localization/SKILL.md +135 -0
- package/.claude/skills/localization/references/api-notes.md +76 -0
- package/.claude/skills/localization/resources/L10nBatchProcessor.cs +69 -0
- package/.claude/skills/localization/resources/LocalizedFontAsset.cs +18 -0
- package/.claude/skills/localize-game/SKILL.md +14 -0
- package/.claude/skills/migrate-project/SKILL.md +21 -0
- package/.claude/skills/migrate-project/references/procedure.md +63 -0
- package/.claude/skills/milestone-review/SKILL.md +14 -0
- package/.claude/skills/new-unity-project/SKILL.md +179 -0
- package/.claude/skills/optimize-audio/SKILL.md +199 -0
- package/.claude/skills/optimize-audio/resources/audio-import-api.md +146 -0
- package/.claude/skills/optimize-audio/resources/platform-settings.md +48 -0
- package/.claude/skills/optimize-text-mesh-pro/SKILL.md +182 -0
- package/.claude/skills/optimize-web/SKILL.md +393 -0
- package/.claude/skills/optimize-web/resources/WebOptimizer.cs +21 -0
- package/.claude/skills/optimize-web/resources/toktx-examples.sh +11 -0
- package/.claude/skills/performance-audit/SKILL.md +14 -0
- package/.claude/skills/physics-3d-collision/SKILL.md +442 -0
- package/.claude/skills/physics-3d-collision/references/troubleshooting.md +41 -0
- package/.claude/skills/physics-3d-collision/resources/CollisionDebugger.cs +33 -0
- package/.claude/skills/plan-feature/SKILL.md +14 -0
- package/.claude/skills/playtest/SKILL.md +14 -0
- package/.claude/skills/project-stage/SKILL.md +14 -0
- package/.claude/skills/prototype-feature/SKILL.md +14 -0
- package/.claude/skills/qa-plan/SKILL.md +14 -0
- package/.claude/skills/release/SKILL.md +18 -0
- package/.claude/skills/release-readiness/SKILL.md +14 -0
- package/.claude/skills/retrospective/SKILL.md +14 -0
- package/.claude/skills/review/SKILL.md +16 -0
- package/.claude/skills/security-audit/SKILL.md +14 -0
- package/.claude/skills/setup-multiplayer-services/SKILL.md +39 -0
- package/.claude/skills/setup-multiplayer-services/references/dgs-entrypoint.md +79 -0
- package/.claude/skills/setup-multiplayer-services/references/entrypoints.md +213 -0
- package/.claude/skills/setup-multiplayer-services/references/examples.md +33 -0
- package/.claude/skills/setup-multiplayer-services/references/implementation-fit.md +30 -0
- package/.claude/skills/setup-multiplayer-services/references/underlying-services.md +11 -0
- package/.claude/skills/setup-multiplayer-services/references/workflows-prerequisites.md +17 -0
- package/.claude/skills/setup-vivox-voice-chat/SKILL.md +118 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/.env.example +6 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/README.md +101 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/promptfooconfig.yaml +32 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/tests/init-and-login.yaml +76 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/tests/text-chat.yaml +65 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/tests/voice-channels.yaml +74 -0
- package/.claude/skills/setup-vivox-voice-chat/references/events-and-participants.md +79 -0
- package/.claude/skills/setup-vivox-voice-chat/references/init-and-login.md +92 -0
- package/.claude/skills/setup-vivox-voice-chat/references/text-chat.md +93 -0
- package/.claude/skills/setup-vivox-voice-chat/references/troubleshooting.md +47 -0
- package/.claude/skills/setup-vivox-voice-chat/references/voice-channels.md +90 -0
- package/.claude/skills/shader-graph-create-custom-node/SKILL.md +25 -0
- package/.claude/skills/shader-graph-create-custom-node/resources/all_hints.hlsl +182 -0
- package/.claude/skills/sprint-plan/SKILL.md +14 -0
- package/.claude/skills/sprite-editor/SKILL.md +66 -0
- package/.claude/skills/sprite-editor/references/api_reference.md +151 -0
- package/.claude/skills/sprite-editor/references/background.md +112 -0
- package/.claude/skills/sprite-editor/references/templates.md +72 -0
- package/.claude/skills/sprite-editor/scripts/AutomaticSliceTexture.cs +40 -0
- package/.claude/skills/sprite-editor/scripts/GenerateNewSpriteRects.cs +200 -0
- package/.claude/skills/sprite-editor/scripts/GetTextureSourceImageSize.cs +32 -0
- package/.claude/skills/sprite-editor/scripts/GetTextureToSlice.cs +55 -0
- package/.claude/skills/sprite-editor/scripts/GridSliceTexture.cs +40 -0
- package/.claude/skills/sprite-editor/scripts/IsometricSliceTexture.cs +141 -0
- package/.claude/skills/sprite-editor/scripts/README.md +134 -0
- package/.claude/skills/sprite-editor/scripts/SetPivotExample.cs +58 -0
- package/.claude/skills/sprite-editor/scripts/SpriteToPng.cs +88 -0
- package/.claude/skills/status/SKILL.md +16 -0
- package/.claude/skills/story-readiness/SKILL.md +14 -0
- package/.claude/skills/test/SKILL.md +16 -0
- package/.claude/skills/ui/SKILL.md +142 -0
- package/.claude/skills/ui-imgui/SKILL.md +186 -0
- package/.claude/skills/ui-imgui/references/gui-elements.md +156 -0
- package/.claude/skills/ui-imgui/references/templates.md +141 -0
- package/.claude/skills/ui-review/SKILL.md +14 -0
- package/.claude/skills/ui-ugui/SKILL.md +282 -0
- package/.claude/skills/ui-ugui/references/scrollview-setup.md +29 -0
- package/.claude/skills/ui-uitk/SKILL.md +235 -0
- package/.claude/skills/ui-uitk/references/common-issues.md +74 -0
- package/.claude/skills/ui-uitk/references/custom-elements.md +241 -0
- package/.claude/skills/ui-uitk/references/painter2d.md +282 -0
- package/.claude/skills/ui-uitk/references/pointermanipulator-guide.md +94 -0
- package/.claude/skills/ui-uitk/references/svg-icons.md +136 -0
- package/.claude/skills/ui-uitk/references/ui-runtime-binding.md +234 -0
- package/.claude/skills/ui-uitk/references/uss-guide.md +138 -0
- package/.claude/skills/unity-cli/CHANGELOG.md +233 -0
- package/.claude/skills/unity-cli/SECURITY.md +22 -0
- package/.claude/skills/unity-cli/SKILL.md +414 -0
- package/.claude/skills/unity-cli/references/auth-license-cloud.md +146 -0
- package/.claude/skills/unity-cli/references/build-run-test.md +349 -0
- package/.claude/skills/unity-cli/references/collaboration.md +472 -0
- package/.claude/skills/unity-cli/references/config-hub.md +103 -0
- package/.claude/skills/unity-cli/references/diagnostics-maintenance.md +326 -0
- package/.claude/skills/unity-cli/references/editors-install.md +327 -0
- package/.claude/skills/unity-cli/references/integration-advanced.md +472 -0
- package/.claude/skills/unity-cli/references/projects-templates.md +574 -0
- package/.claude/skills/unity-package-management/SKILL.md +304 -0
- package/.claude/skills/unity-package-management/references/select-packages.md +108 -0
- package/.claude/skills/urp-postprocessing/SKILL.md +188 -0
- package/.claude/skills/urp-postprocessing/references/code-templates.md +119 -0
- package/.claude/skills/urp-postprocessing/references/effect-reference.md +86 -0
- package/.claude/skills/validate-urp-render-graph-renderer-feature/SKILL.md +269 -0
- package/.claude/skills/work/SKILL.md +36 -0
- package/.claude/skills/work/references/classification.md +41 -0
- package/.claude/skills/work/references/closure.md +50 -0
- package/.claude/skills/work/references/feature-contracts.md +32 -0
- package/.claude/skills/work/references/verification.md +26 -0
- package/CLAUDE.md +6 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/THIRD_PARTY_NOTICES.md +23 -0
- package/UPGRADING.md +32 -0
- package/VERSION +1 -0
- package/docs/ARCHITECTURE.md +73 -0
- package/docs/HUONG-DAN-MIGRATE-VA-SU-DUNG.md +324 -0
- package/docs/MIGRATION-MATRIX.md +23 -0
- package/docs/PROJECT-POLICY.md +67 -0
- package/docs/WORKFLOWS.md +61 -0
- package/package-assets/setup-vivox-voice-chat-evals.gitignore +7 -0
- package/package.json +41 -0
- package/scaffold/.nccgs/bugs/.gitkeep +1 -0
- package/scaffold/.nccgs/closures/.gitkeep +1 -0
- package/scaffold/.nccgs/context/.gitkeep +1 -0
- package/scaffold/.nccgs/decisions/.gitkeep +1 -0
- package/scaffold/.nccgs/evidence/.gitkeep +1 -0
- package/scaffold/.nccgs/features/.gitkeep +1 -0
- package/scaffold/.nccgs/migrations/.gitkeep +1 -0
- package/scaffold/.nccgs/playtests/.gitkeep +1 -0
- package/scaffold/.nccgs/project.yaml +103 -0
- package/scaffold/.nccgs/requirements.yaml +10 -0
- package/scaffold/.nccgs/reviews/.gitkeep +1 -0
- package/scaffold/.nccgs/state.md +40 -0
- package/scaffold/.nccgs/templates/agent-handoff.md +25 -0
- package/scaffold/.nccgs/templates/architecture-decision.md +27 -0
- package/scaffold/.nccgs/templates/closure-record.md +51 -0
- package/scaffold/.nccgs/templates/context-packet.yaml +17 -0
- package/scaffold/.nccgs/templates/evidence-record.md +23 -0
- package/scaffold/.nccgs/templates/feature-contract.md +35 -0
- package/scaffold/.nccgs/templates/migration-plan.md +40 -0
- package/scaffold/.nccgs/templates/waiver.md +11 -0
- package/scripts/cli.mjs +56 -0
- package/scripts/install.mjs +267 -0
- package/scripts/sync-unity-skills.mjs +126 -0
- package/scripts/validate.mjs +205 -0
- package/tests/framework.test.mjs +121 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Sprite Editor Code Templates
|
|
2
|
+
|
|
3
|
+
## Safe Core Pattern (MANDATORY)
|
|
4
|
+
|
|
5
|
+
Types are fully qualified because this runs through `eval`, which rejects `using` directives.
|
|
6
|
+
If you save it as a `.cs` file instead, add `using UnityEditor.U2D.Sprites;` and shorten them.
|
|
7
|
+
|
|
8
|
+
Use this structure for all sprite modification tasks.
|
|
9
|
+
|
|
10
|
+
**CRITICAL:** If capability checks fail, the script MUST return immediately. NEVER bypass capability checks even if you suspect the API might work - this can cause data corruption and violates Unity's data provider contract.
|
|
11
|
+
|
|
12
|
+
```csharp
|
|
13
|
+
// 1. Get and Init Data Provider
|
|
14
|
+
var importer = UnityEditor.AssetImporter.GetAtPath(assetPath);
|
|
15
|
+
var factory = new UnityEditor.U2D.Sprites.SpriteDataProviderFactories();
|
|
16
|
+
factory.Init();
|
|
17
|
+
var dataProvider = factory.GetSpriteEditorDataProviderFromObject(importer);
|
|
18
|
+
dataProvider.InitSpriteEditorDataProvider();
|
|
19
|
+
|
|
20
|
+
// 2. MANDATORY: Check Capabilities - ABORT if not supported
|
|
21
|
+
var editCapability = dataProvider.GetDataProvider<UnityEditor.U2D.Sprites.ISpriteFrameEditCapability>();
|
|
22
|
+
if (editCapability == null)
|
|
23
|
+
{
|
|
24
|
+
throw new System.Exception("Edit capability not supported by importer. Operation aborted.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var capability = editCapability.GetEditCapability();
|
|
28
|
+
// Check for: EditSpriteName, EditSpriteRect, EditBorder, EditPivot, CreateAndDeleteSprite
|
|
29
|
+
if (!capability.HasCapability(UnityEditor.U2D.Sprites.EEditCapability.EditSpriteName))
|
|
30
|
+
{
|
|
31
|
+
throw new System.Exception("Operation not supported by importer. User action aborted.");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 3. Read and Modify
|
|
35
|
+
var spriteRects = dataProvider.GetSpriteRects();
|
|
36
|
+
// ... logic here ...
|
|
37
|
+
|
|
38
|
+
// 4. Apply and Reimport
|
|
39
|
+
dataProvider.SetSpriteRects(spriteRects);
|
|
40
|
+
dataProvider.Apply();
|
|
41
|
+
importer.SaveAndReimport();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Capability Check Pattern
|
|
45
|
+
|
|
46
|
+
Before performing any modification operation, check if the importer supports it. **ABORT the user action if the capability is not supported.**
|
|
47
|
+
|
|
48
|
+
**DO NOT rationalize bypassing this check.** Even if you believe the API might accept the operation, capability checks are mandatory for data integrity. Return immediately on failure - no exceptions.
|
|
49
|
+
|
|
50
|
+
```csharp
|
|
51
|
+
var editCapability = dataProvider.GetDataProvider<UnityEditor.U2D.Sprites.ISpriteFrameEditCapability>();
|
|
52
|
+
if (editCapability == null)
|
|
53
|
+
{
|
|
54
|
+
throw new System.Exception("Edit capability not supported by importer. User action aborted.");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var capability = editCapability.GetEditCapability();
|
|
59
|
+
if (!capability.HasCapability(UnityEditor.U2D.Sprites.EEditCapability.EditSpriteName)) // Adjust based on task
|
|
60
|
+
{
|
|
61
|
+
throw new System.Exception("Importer does not support the requested operation. User action aborted.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Available Capabilities
|
|
67
|
+
|
|
68
|
+
- `EditSpriteName` - Modify sprite names
|
|
69
|
+
- `EditSpriteRect` - Modify sprite rectangles
|
|
70
|
+
- `EditBorder` - Modify 9-slice borders
|
|
71
|
+
- `EditPivot` - Modify pivot points
|
|
72
|
+
- `CreateAndDeleteSprite` - Add/remove sprites or perform slicing
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using UnityEditor.U2D.Sprites;
|
|
3
|
+
|
|
4
|
+
namespace Editor
|
|
5
|
+
{
|
|
6
|
+
static public partial class SpriteEditorUtility
|
|
7
|
+
{
|
|
8
|
+
/// <summary>
|
|
9
|
+
/// Automatically slices a texture by detecting visible pixel regions and creating sprite rectangles.
|
|
10
|
+
/// Uses Unity's internal automatic sprite detection algorithm to find sprite boundaries.
|
|
11
|
+
/// </summary>
|
|
12
|
+
/// <param name="spriteDataProvider">The sprite data provider for the texture.</param>
|
|
13
|
+
/// <param name="textureProvider">The texture data provider.</param>
|
|
14
|
+
/// <param name="minRectSize">Minimum size in pixels for detected sprite rectangles.</param>
|
|
15
|
+
/// <param name="extrudeSize">Number of pixels to extrude (expand) sprite boundaries.</param>
|
|
16
|
+
/// <param name="addNewSpriteMethod">Method for handling existing sprites (DeleteAll, Smart, Safe).</param>
|
|
17
|
+
/// <param name="nameGenerator">Function to generate sprite names based on index.</param>
|
|
18
|
+
/// <param name="kOverlapTolerance">Tolerance for detecting overlapping sprites.</param>
|
|
19
|
+
/// <param name="kBestFitTolerance">Tolerance for best-fit matching.</param>
|
|
20
|
+
/// <param name="bestFit">Whether to use best-fit algorithm for overlap detection.</param>
|
|
21
|
+
/// <returns>True if slicing succeeded, false if texture is not readable.</returns>
|
|
22
|
+
static public bool AutomaticSliceTexture(ISpriteEditorDataProvider spriteDataProvider, ITextureDataProvider textureProvider,
|
|
23
|
+
int minRectSize, int extrudeSize, AddNewSpriteMethod addNewSpriteMethod, Func<int, string> nameGenerator,
|
|
24
|
+
float kOverlapTolerance= 0.00001f, float kBestFitTolerance = 0.5f, bool bestFit = false)
|
|
25
|
+
{
|
|
26
|
+
var texture = GetTextureToSlice(textureProvider);
|
|
27
|
+
if (texture == null)
|
|
28
|
+
{
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var rects = UnityEditorInternal.InternalSpriteUtility.GenerateAutomaticSpriteRectangles(texture, minRectSize, extrudeSize);
|
|
33
|
+
|
|
34
|
+
var newRects = GenerateNewSpriteRects(spriteDataProvider, rects, addNewSpriteMethod, nameGenerator, kOverlapTolerance, kBestFitTolerance, bestFit);
|
|
35
|
+
spriteDataProvider.SetSpriteRects(newRects.ToArray());
|
|
36
|
+
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEditor;
|
|
4
|
+
using UnityEditor.U2D.Sprites;
|
|
5
|
+
using UnityEngine;
|
|
6
|
+
|
|
7
|
+
namespace Editor
|
|
8
|
+
{
|
|
9
|
+
static public partial class SpriteEditorUtility
|
|
10
|
+
{
|
|
11
|
+
public enum AddNewSpriteMethod
|
|
12
|
+
{
|
|
13
|
+
DeleteAll, // Remove all existing sprites and create new ones
|
|
14
|
+
Smart, // Update overlapping sprites, add non-overlapping ones
|
|
15
|
+
Safe // Only add sprites that don't overlap with existing ones
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// <summary>
|
|
19
|
+
/// Generates new sprite rectangles from a collection of rects, handling existing sprites based on the specified method.
|
|
20
|
+
/// Automatically assigns unique names using the provided name generator function.
|
|
21
|
+
/// </summary>
|
|
22
|
+
/// <param name="spriteDataProvider">The sprite data provider containing existing sprites.</param>
|
|
23
|
+
/// <param name="rects">Collection of rectangles to create sprites from.</param>
|
|
24
|
+
/// <param name="addNewSpriteMethod">Strategy for handling existing sprites.</param>
|
|
25
|
+
/// <param name="nameGenerator">Function that takes an index and returns a sprite name.</param>
|
|
26
|
+
/// <param name="kOverlapTolerance">Minimum overlap area ratio to consider sprites overlapping.</param>
|
|
27
|
+
/// <param name="kBestFitTolerance">Maximum overlap ratio difference for best-fit matching.</param>
|
|
28
|
+
/// <param name="bestFit">If true, finds the best matching existing sprite; if false, uses first match.</param>
|
|
29
|
+
/// <returns>List of sprite rectangles ready to be set on the data provider.</returns>
|
|
30
|
+
public static List<SpriteRect> GenerateNewSpriteRects(ISpriteEditorDataProvider spriteDataProvider, IEnumerable<Rect> rects, AddNewSpriteMethod addNewSpriteMethod, Func<int, string> nameGenerator,
|
|
31
|
+
float kOverlapTolerance= 0.00001f, float kBestFitTolerance = 0.5f, bool bestFit = false)
|
|
32
|
+
{
|
|
33
|
+
const int k_NameFindBreakLimit = 1000000;
|
|
34
|
+
var existingSpriteRects = spriteDataProvider.GetSpriteRects();
|
|
35
|
+
List<SpriteRect> newRects = new List<SpriteRect>();
|
|
36
|
+
HashSet<string> existingNames = new HashSet<string>();
|
|
37
|
+
|
|
38
|
+
Func<Rect, SpriteRect> newRectLambda = (frame) =>
|
|
39
|
+
{
|
|
40
|
+
int nameIndex = existingNames.Count;
|
|
41
|
+
var spriteName = "";
|
|
42
|
+
while (nameIndex < k_NameFindBreakLimit)
|
|
43
|
+
{
|
|
44
|
+
spriteName = nameGenerator(nameIndex++);
|
|
45
|
+
if (!existingNames.Contains(spriteName))
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if(nameIndex >= k_NameFindBreakLimit)
|
|
50
|
+
{
|
|
51
|
+
Debug.LogError("Failed to generate unique sprite name for automatic slicing. Please check the name generator function.");
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
existingNames.Add(spriteName);
|
|
56
|
+
return new SpriteRect()
|
|
57
|
+
{
|
|
58
|
+
name = spriteName,
|
|
59
|
+
alignment = SpriteAlignment.Center,
|
|
60
|
+
rect = frame,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
Action<Rect> deleteAllSliceMethodLambda = (frame) =>
|
|
65
|
+
{
|
|
66
|
+
var newRect = newRectLambda(frame);
|
|
67
|
+
if (newRect != null)
|
|
68
|
+
{
|
|
69
|
+
newRects.Add(newRect);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
Action<Rect> smartSliceMethodLambda = (frame) =>
|
|
74
|
+
{
|
|
75
|
+
var outSprite = GetExistingOverlappingSprite(spriteDataProvider, frame, kOverlapTolerance, kBestFitTolerance, bestFit);
|
|
76
|
+
if (outSprite != -1)
|
|
77
|
+
{
|
|
78
|
+
var existingRect = existingSpriteRects[outSprite];
|
|
79
|
+
existingRect.rect = frame;
|
|
80
|
+
if (existingNames.Contains(existingRect.name))
|
|
81
|
+
{
|
|
82
|
+
// Handle name conflict by renaming the previous sprite
|
|
83
|
+
var conflictRect = newRects.FindIndex(x => x.name == existingRect.name);
|
|
84
|
+
if (conflictRect != -1)
|
|
85
|
+
{
|
|
86
|
+
int nameIndex = existingNames.Count;
|
|
87
|
+
var spriteName = "";
|
|
88
|
+
while (nameIndex < k_NameFindBreakLimit)
|
|
89
|
+
{
|
|
90
|
+
spriteName = nameGenerator(nameIndex++);
|
|
91
|
+
if (!existingNames.Contains(spriteName))
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
if(nameIndex >= k_NameFindBreakLimit)
|
|
95
|
+
{
|
|
96
|
+
Debug.LogError("Failed to generate unique sprite name for automatic slicing. Removing conflicting sprite.");
|
|
97
|
+
newRects.RemoveAt(conflictRect);
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
newRects[conflictRect].name = spriteName;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else
|
|
104
|
+
existingNames.Add(existingRect.name);
|
|
105
|
+
newRects.Add(existingRect);
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
{
|
|
109
|
+
var newRect = newRectLambda(frame);
|
|
110
|
+
if (newRect != null)
|
|
111
|
+
{
|
|
112
|
+
newRects.Add(newRect);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
Action<Rect> safeSliceMethodLambda = (frame) =>
|
|
118
|
+
{
|
|
119
|
+
var outSprite = GetExistingOverlappingSprite(spriteDataProvider, frame, kOverlapTolerance, kBestFitTolerance, bestFit);
|
|
120
|
+
if (outSprite == -1)
|
|
121
|
+
{
|
|
122
|
+
var newRect = newRectLambda(frame);
|
|
123
|
+
if (newRect != null)
|
|
124
|
+
{
|
|
125
|
+
newRects.Add(newRect);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
Action<Rect> sliceMethodLambda = safeSliceMethodLambda;
|
|
131
|
+
switch (addNewSpriteMethod)
|
|
132
|
+
{
|
|
133
|
+
case AddNewSpriteMethod.DeleteAll:
|
|
134
|
+
sliceMethodLambda = deleteAllSliceMethodLambda;
|
|
135
|
+
break;
|
|
136
|
+
case AddNewSpriteMethod.Smart:
|
|
137
|
+
sliceMethodLambda = smartSliceMethodLambda;
|
|
138
|
+
break;
|
|
139
|
+
case AddNewSpriteMethod.Safe:
|
|
140
|
+
// Preserve all existing sprites
|
|
141
|
+
foreach(var existingRect in existingSpriteRects)
|
|
142
|
+
{
|
|
143
|
+
existingNames.Add(existingRect.name);
|
|
144
|
+
}
|
|
145
|
+
newRects.AddRange(existingSpriteRects);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
foreach (var frame in rects)
|
|
150
|
+
{
|
|
151
|
+
sliceMethodLambda(frame);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return newRects;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private static int GetExistingOverlappingSprite(ISpriteEditorDataProvider dataProvider, Rect rect, float kOverlapTolerance= 0.00001f, float kBestFitTolerance = 0.5f, bool bestFit = false)
|
|
158
|
+
{
|
|
159
|
+
var spriteRects = dataProvider.GetSpriteRects();
|
|
160
|
+
var count = spriteRects.Length;
|
|
161
|
+
var bestRect = -1;
|
|
162
|
+
var rectArea = rect.width * rect.height;
|
|
163
|
+
if (rectArea < kOverlapTolerance)
|
|
164
|
+
return bestRect;
|
|
165
|
+
|
|
166
|
+
var bestRatio = float.MaxValue;
|
|
167
|
+
var bestArea = float.MaxValue;
|
|
168
|
+
for (int i = 0; i < count; i++)
|
|
169
|
+
{
|
|
170
|
+
Rect existingRect = spriteRects[i].rect;
|
|
171
|
+
if (existingRect.Overlaps(rect))
|
|
172
|
+
{
|
|
173
|
+
if (bestFit)
|
|
174
|
+
{
|
|
175
|
+
var dx = Math.Min(rect.xMax, existingRect.xMax) - Math.Max(rect.xMin, existingRect.xMin);
|
|
176
|
+
var dy = Math.Min(rect.yMax, existingRect.yMax) - Math.Max(rect.yMin, existingRect.yMin);
|
|
177
|
+
var overlapArea = dx * dy;
|
|
178
|
+
var overlapRatio = Math.Abs((overlapArea / rectArea) - 1.0f);
|
|
179
|
+
var existingArea = existingRect.width * existingRect.height;
|
|
180
|
+
if (overlapRatio < bestRatio || (overlapRatio < kOverlapTolerance && existingArea < bestArea))
|
|
181
|
+
{
|
|
182
|
+
bestRatio = overlapRatio;
|
|
183
|
+
if (overlapRatio < kOverlapTolerance)
|
|
184
|
+
bestArea = existingArea;
|
|
185
|
+
bestRect = i;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else
|
|
189
|
+
{
|
|
190
|
+
bestRect = i;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (bestFit && bestRatio > kBestFitTolerance)
|
|
196
|
+
return -1;
|
|
197
|
+
return bestRect;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
using UnityEditor;
|
|
2
|
+
using UnityEditor.U2D.Sprites;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
|
|
5
|
+
namespace Editor
|
|
6
|
+
{
|
|
7
|
+
static public partial class SpriteEditorUtility
|
|
8
|
+
{
|
|
9
|
+
/// <summary>
|
|
10
|
+
/// Get the original source image size of a texture, which is different from the texture size when the texture is imported with "Max Size" smaller than the original image size.
|
|
11
|
+
/// This method will try to get the original source image size from TextureImporter, if it fails, it will return the texture size as fallback.
|
|
12
|
+
/// </summary>
|
|
13
|
+
/// <param name="texture"></param>
|
|
14
|
+
/// <param name="width"></param>
|
|
15
|
+
/// <param name="height"></param>
|
|
16
|
+
static public void GetTextureSourceImageSize(Texture2D texture, out int width, out int height)
|
|
17
|
+
{
|
|
18
|
+
SpriteDataProviderFactories factories = new SpriteDataProviderFactories();
|
|
19
|
+
factories.Init();
|
|
20
|
+
var dataProvider = factories.GetSpriteEditorDataProviderFromObject(texture);
|
|
21
|
+
var textureDataProvider = dataProvider?.GetDataProvider<ITextureDataProvider>();
|
|
22
|
+
if(textureDataProvider != null)
|
|
23
|
+
{
|
|
24
|
+
textureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
width = texture.width;
|
|
29
|
+
height = texture.height;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
using UnityEditor;
|
|
2
|
+
using UnityEditor.U2D.Sprites;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
using UnityEngine.Experimental.Rendering;
|
|
5
|
+
|
|
6
|
+
namespace Editor
|
|
7
|
+
{
|
|
8
|
+
static public partial class SpriteEditorUtility
|
|
9
|
+
{
|
|
10
|
+
/// <summary>
|
|
11
|
+
/// Gets a readable texture for slicing operations, upscaling if necessary to match original dimensions.
|
|
12
|
+
/// This ensures slicing is performed on the original image size, not the imported texture size.
|
|
13
|
+
/// </summary>
|
|
14
|
+
/// <param name="textureDataProvider">The texture data provider.</param>
|
|
15
|
+
/// <returns>A readable Texture2D at original dimensions, or null if texture is not readable.</returns>
|
|
16
|
+
static public Texture2D GetTextureToSlice(ITextureDataProvider textureDataProvider)
|
|
17
|
+
{
|
|
18
|
+
textureDataProvider.GetTextureActualWidthAndHeight(out var width, out var height);
|
|
19
|
+
var readableTexture = textureDataProvider.GetReadableTexture2D();
|
|
20
|
+
if (readableTexture == null || (readableTexture.width == width && readableTexture.height == height))
|
|
21
|
+
return readableTexture;
|
|
22
|
+
|
|
23
|
+
// Upscale the imported texture to match original dimensions for accurate slicing
|
|
24
|
+
var texture = CreateTemporaryDuplicate(readableTexture, width, height);
|
|
25
|
+
texture.hideFlags = HideFlags.HideAndDontSave;
|
|
26
|
+
|
|
27
|
+
return texture;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// <summary>
|
|
31
|
+
/// Creates a temporary duplicate of a texture at a specified size using RenderTexture.
|
|
32
|
+
/// Used internally to upscale textures for slicing operations.
|
|
33
|
+
/// </summary>
|
|
34
|
+
public static Texture2D CreateTemporaryDuplicate(Texture2D original, int width, int height)
|
|
35
|
+
{
|
|
36
|
+
if (!ShaderUtil.hardwareSupportsRectRenderTexture || !(bool) (Object) original)
|
|
37
|
+
return null;
|
|
38
|
+
|
|
39
|
+
RenderTexture active = RenderTexture.active;
|
|
40
|
+
RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, SystemInfo.GetGraphicsFormat(DefaultFormat.LDR));
|
|
41
|
+
Graphics.Blit(original, temporary);
|
|
42
|
+
RenderTexture.active = temporary;
|
|
43
|
+
|
|
44
|
+
bool flag = width >= SystemInfo.maxTextureSize || height >= SystemInfo.maxTextureSize;
|
|
45
|
+
Texture2D temporaryDuplicate = new Texture2D(width, height, TextureFormat.RGBA32, original.mipmapCount > 1 | flag);
|
|
46
|
+
temporaryDuplicate.ReadPixels(new Rect(0.0f, 0.0f, width, height), 0, 0);
|
|
47
|
+
temporaryDuplicate.Apply();
|
|
48
|
+
|
|
49
|
+
RenderTexture.ReleaseTemporary(temporary);
|
|
50
|
+
temporaryDuplicate.alphaIsTransparency = original.alphaIsTransparency;
|
|
51
|
+
|
|
52
|
+
return temporaryDuplicate;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using UnityEngine;
|
|
3
|
+
using UnityEditor.U2D.Sprites;
|
|
4
|
+
|
|
5
|
+
namespace Editor
|
|
6
|
+
{
|
|
7
|
+
static public partial class SpriteEditorUtility
|
|
8
|
+
{
|
|
9
|
+
/// <summary>
|
|
10
|
+
/// Slices a texture into a regular grid of sprite rectangles based on specified cell size, offset, and padding.
|
|
11
|
+
/// Optionally keeps or discards empty rectangles based on pixel alpha values.
|
|
12
|
+
/// </summary>
|
|
13
|
+
/// <param name="spriteDataProvider">The sprite data provider for the texture.</param>
|
|
14
|
+
/// <param name="textureProvider">The texture data provider.</param>
|
|
15
|
+
/// <param name="offset">The offset from the top-left corner to start the grid.</param>
|
|
16
|
+
/// <param name="size">The size of each grid cell (width x height).</param>
|
|
17
|
+
/// <param name="padding">The padding between grid cells.</param>
|
|
18
|
+
/// <param name="addNewSpriteMethod">Method for handling existing sprites (DeleteAll, Smart, Safe).</param>
|
|
19
|
+
/// <param name="nameGenerator">Function to generate sprite names based on index.</param>
|
|
20
|
+
/// <param name="keepEmptyRects">Whether to keep sprites with no visible pixels.</param>
|
|
21
|
+
/// <param name="kOverlapTolerance">Tolerance for detecting overlapping sprites.</param>
|
|
22
|
+
/// <param name="kBestFitTolerance">Tolerance for best-fit matching.</param>
|
|
23
|
+
/// <param name="bestFit">Whether to use best-fit algorithm for overlap detection.</param>
|
|
24
|
+
/// <returns>True if slicing succeeded, false if texture is not readable.</returns>
|
|
25
|
+
static public bool GridSliceTexture(ISpriteEditorDataProvider spriteDataProvider, ITextureDataProvider textureProvider, Vector2 offset, Vector2 size, Vector2 padding,
|
|
26
|
+
AddNewSpriteMethod addNewSpriteMethod, Func<int, string> nameGenerator,
|
|
27
|
+
bool keepEmptyRects =false, float kOverlapTolerance= 0.00001f, float kBestFitTolerance = 0.5f, bool bestFit = false)
|
|
28
|
+
{
|
|
29
|
+
var textureToUse = GetTextureToSlice(textureProvider);
|
|
30
|
+
if (textureToUse == null)
|
|
31
|
+
{
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
var rects = UnityEditorInternal.InternalSpriteUtility.GenerateGridSpriteRectangles(textureToUse, offset, size, padding, keepEmptyRects);
|
|
35
|
+
var newRects = GenerateNewSpriteRects(spriteDataProvider, rects, addNewSpriteMethod, nameGenerator, kOverlapTolerance, kBestFitTolerance, bestFit);
|
|
36
|
+
spriteDataProvider.SetSpriteRects(newRects.ToArray());
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEditor.U2D.Sprites;
|
|
4
|
+
using UnityEngine;
|
|
5
|
+
|
|
6
|
+
namespace Editor
|
|
7
|
+
{
|
|
8
|
+
static public class IsometricSliceUtility
|
|
9
|
+
{
|
|
10
|
+
/// <summary>
|
|
11
|
+
/// Slices a texture into isometric tiles based on specified size and offset.
|
|
12
|
+
/// Creates sprite rectangles in an isometric diamond pattern and optionally sets diamond-shaped outlines.
|
|
13
|
+
/// </summary>
|
|
14
|
+
/// <param name="spriteDataProvider">The sprite data provider for the texture.</param>
|
|
15
|
+
/// <param name="textureProvider">The texture data provider.</param>
|
|
16
|
+
/// <param name="size">The size of each isometric tile (width x height).</param>
|
|
17
|
+
/// <param name="offset">The offset from the top-left corner to start slicing.</param>
|
|
18
|
+
/// <param name="alignment">The alignment value for sprites.</param>
|
|
19
|
+
/// <param name="pivot">The pivot point for sprites.</param>
|
|
20
|
+
/// <param name="slicingMethod">Method for handling existing sprites (DeleteAll, Smart, Safe).</param>
|
|
21
|
+
/// <param name="nameGenerator">Function to generate sprite names based on index.</param>
|
|
22
|
+
/// <param name="kOverlapTolerance">Tolerance for detecting overlapping sprites.</param>
|
|
23
|
+
/// <param name="kBestFitTolerance">Tolerance for best-fit matching.</param>
|
|
24
|
+
/// <param name="bestFit">Whether to use best-fit algorithm for overlap detection.</param>
|
|
25
|
+
/// <param name="keepEmptyRects">Whether to keep sprites with no visible pixels.</param>
|
|
26
|
+
/// <param name="isAlternate">Whether to start with alternating row offset.</param>
|
|
27
|
+
/// <returns>True if slicing succeeded, false if texture is not readable.</returns>
|
|
28
|
+
static public bool IsometricSliceTexture(ISpriteEditorDataProvider spriteDataProvider, ITextureDataProvider textureProvider,
|
|
29
|
+
Vector2 size, Vector2 offset, int alignment, Vector2 pivot, SpriteEditorUtility.AddNewSpriteMethod slicingMethod, Func<int, string> nameGenerator,
|
|
30
|
+
float kOverlapTolerance = 0.00001f, float kBestFitTolerance = 0.5f, bool bestFit = false, bool keepEmptyRects = false, bool isAlternate = false)
|
|
31
|
+
{
|
|
32
|
+
var texture = SpriteEditorUtility.GetTextureToSlice(textureProvider);
|
|
33
|
+
if (texture == null)
|
|
34
|
+
{
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
var rects = GetIsometricRects(texture, size, offset, isAlternate, keepEmptyRects);
|
|
38
|
+
var newRects = SpriteEditorUtility.GenerateNewSpriteRects(spriteDataProvider, rects, slicingMethod, nameGenerator, kOverlapTolerance, kBestFitTolerance, bestFit);
|
|
39
|
+
spriteDataProvider.SetSpriteRects(newRects.ToArray());
|
|
40
|
+
|
|
41
|
+
// Set diamond-shaped outlines for isometric sprites
|
|
42
|
+
var outlineDataProvider = spriteDataProvider.GetDataProvider<ISpriteOutlineDataProvider>();
|
|
43
|
+
if (outlineDataProvider != null)
|
|
44
|
+
{
|
|
45
|
+
List<Vector2[]> outlines = new List<Vector2[]>(4);
|
|
46
|
+
outlines.Add(new[] {
|
|
47
|
+
new Vector2(0.0f, -size.y / 2),
|
|
48
|
+
new Vector2(size.x / 2, 0.0f),
|
|
49
|
+
new Vector2(0.0f, size.y / 2),
|
|
50
|
+
new Vector2(-size.x / 2, 0.0f)
|
|
51
|
+
});
|
|
52
|
+
foreach (var rect in newRects)
|
|
53
|
+
{
|
|
54
|
+
outlineDataProvider.SetOutlines(rect.spriteID, outlines);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private static bool PixelHasAlpha(int x, int y, int width, bool[] alphaPixelCache)
|
|
62
|
+
{
|
|
63
|
+
var index = y * width + x;
|
|
64
|
+
return alphaPixelCache[index];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// <summary>
|
|
68
|
+
/// Generates rectangles for isometric tile slicing by walking the texture in an isometric pattern.
|
|
69
|
+
/// Optionally filters out empty rectangles based on alpha pixel density.
|
|
70
|
+
/// </summary>
|
|
71
|
+
public static IEnumerable<Rect> GetIsometricRects(Texture2D textureToUse, Vector2 size, Vector2 offset, bool isAlternate, bool keepEmptyRects)
|
|
72
|
+
{
|
|
73
|
+
var alphaPixelCache = new bool[textureToUse.width * textureToUse.height];
|
|
74
|
+
Color32[] pixels = textureToUse.GetPixels32();
|
|
75
|
+
for (int i = 0; i < pixels.Length; i++)
|
|
76
|
+
alphaPixelCache[i] = pixels[i].a != 0;
|
|
77
|
+
|
|
78
|
+
var gradient = (size.x / 2) / (size.y / 2);
|
|
79
|
+
bool isAlt = isAlternate;
|
|
80
|
+
float x = offset.x;
|
|
81
|
+
if (isAlt)
|
|
82
|
+
x += size.x / 2;
|
|
83
|
+
float y = textureToUse.height - offset.y;
|
|
84
|
+
|
|
85
|
+
while (y - size.y >= 0)
|
|
86
|
+
{
|
|
87
|
+
while (x + size.x <= textureToUse.width)
|
|
88
|
+
{
|
|
89
|
+
var rect = new Rect(x, y - size.y, size.x, size.y);
|
|
90
|
+
if (!keepEmptyRects)
|
|
91
|
+
{
|
|
92
|
+
// Check if the isometric diamond area has sufficient alpha pixels
|
|
93
|
+
int sx = (int)rect.x;
|
|
94
|
+
int sy = (int)rect.y;
|
|
95
|
+
int width = (int)size.x;
|
|
96
|
+
int odd = ((int)size.y) % 2;
|
|
97
|
+
int topY = ((int)size.y / 2) - 1;
|
|
98
|
+
int bottomY = topY + odd;
|
|
99
|
+
int totalPixels = 0;
|
|
100
|
+
int alphaPixels = 0;
|
|
101
|
+
|
|
102
|
+
// Sample pixels in diamond shape
|
|
103
|
+
for (int ry = 0; ry <= topY; ry++)
|
|
104
|
+
{
|
|
105
|
+
var pixelOffset = Mathf.CeilToInt(gradient * ry);
|
|
106
|
+
for (int rx = pixelOffset; rx < width - pixelOffset; ++rx)
|
|
107
|
+
{
|
|
108
|
+
if (PixelHasAlpha(sx + rx, sy + topY - ry, textureToUse.width, alphaPixelCache))
|
|
109
|
+
alphaPixels++;
|
|
110
|
+
if (PixelHasAlpha(sx + rx, sy + bottomY + ry, textureToUse.width, alphaPixelCache))
|
|
111
|
+
alphaPixels++;
|
|
112
|
+
totalPixels += 2;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (odd > 0)
|
|
117
|
+
{
|
|
118
|
+
int ry = topY + 1;
|
|
119
|
+
for (int rx = 0; rx < size.x; ++rx)
|
|
120
|
+
{
|
|
121
|
+
if (PixelHasAlpha(sx + rx, sy + ry, textureToUse.width, alphaPixelCache))
|
|
122
|
+
alphaPixels++;
|
|
123
|
+
totalPixels++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (totalPixels > 0 && ((float)alphaPixels) / totalPixels > 0.01f)
|
|
127
|
+
yield return rect;
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
yield return rect;
|
|
131
|
+
x += size.x;
|
|
132
|
+
}
|
|
133
|
+
isAlt = !isAlt;
|
|
134
|
+
x = offset.x;
|
|
135
|
+
if (isAlt)
|
|
136
|
+
x += size.x / 2;
|
|
137
|
+
y -= size.y / 2;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|