@jjlmoya/utils-games-development 1.62.0 → 1.63.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/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +4 -1
- package/src/index.ts +1 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/hitboxHurtboxAnimator/bibliography.astro +12 -0
- package/src/tool/hitboxHurtboxAnimator/bibliography.ts +10 -0
- package/src/tool/hitboxHurtboxAnimator/canvas-interactions.ts +59 -0
- package/src/tool/hitboxHurtboxAnimator/canvas-metrics.ts +8 -0
- package/src/tool/hitboxHurtboxAnimator/component.astro +148 -0
- package/src/tool/hitboxHurtboxAnimator/controller.ts +247 -0
- package/src/tool/hitboxHurtboxAnimator/dom-views.ts +194 -0
- package/src/tool/hitboxHurtboxAnimator/editor-state.ts +19 -0
- package/src/tool/hitboxHurtboxAnimator/entry.ts +28 -0
- package/src/tool/hitboxHurtboxAnimator/evaluator.ts +15 -0
- package/src/tool/hitboxHurtboxAnimator/file-io.ts +82 -0
- package/src/tool/hitboxHurtboxAnimator/hitbox-hurtbox-animator.css +614 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/de.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/en.ts +191 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/es.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/fr.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/id.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/it.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/ja.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/ko.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/nl.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/pl.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/pt.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/ru.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/sv.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/tr.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/zh.ts +195 -0
- package/src/tool/hitboxHurtboxAnimator/index.ts +11 -0
- package/src/tool/hitboxHurtboxAnimator/logic.test.ts +97 -0
- package/src/tool/hitboxHurtboxAnimator/logic.ts +173 -0
- package/src/tool/hitboxHurtboxAnimator/project-actions.ts +128 -0
- package/src/tool/hitboxHurtboxAnimator/seo.astro +13 -0
- package/src/tool/hitboxHurtboxAnimator/storage.ts +27 -0
- package/src/tool/hitboxHurtboxAnimator/ui.ts +70 -0
- package/src/tools.ts +2 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
import type { HitboxHurtboxAnimatorUI } from '../ui';
|
|
4
|
+
import { bibliographyEntries } from '../bibliography';
|
|
5
|
+
|
|
6
|
+
const slug = 'hitbox-und-hurtbox-animator-sprites';
|
|
7
|
+
const title = 'Hitbox und Hurtbox Animator für Sprites';
|
|
8
|
+
const description = 'Zeichnen Sie Kollisions-Ebenen auf jedes Sprite-Frame, nutzen Sie Zwiebelschalen-Vorschauen, bearbeiten Sie exakte Pixel-Koordinaten und exportieren Sie ein motorneutrales JSON.';
|
|
9
|
+
|
|
10
|
+
const faq = [
|
|
11
|
+
{
|
|
12
|
+
question: 'Was ist der Unterschied zwischen einer Hitbox und einer Hurtbox?',
|
|
13
|
+
answer: 'Eine Hitbox markiert einen angreifenden Bereich, während eine Hurtbox den Bereich definiert, der Schaden empfangen kann. Pushboxes halten Charaktere auf Abstand, Grabboxes definieren die Griffreichweite und Sensoren dienen als Erkennungszonen.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: 'Verlassen meine Sprite-Dateien den Browser?',
|
|
17
|
+
answer: 'Nein. Bilder werden vollständig im Browser verarbeitet und exportiert. Das Werkzeug speichert lediglich Einstellungen wie die Abspielgeschwindigkeit im lokalen Speicher.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Welches Koordinatensystem nutzt der JSON-Export?',
|
|
21
|
+
answer: 'Jedes Frame misst Pixel ausgehend von der oberen linken Ecke. Die Abmessungen von Rechtecken und Kreisen speichern positive Werte für x, y, Breite und Höhe inklusive des eigenen Drehpunktes.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Kann ich Spritesheets und einzelne Frame-Bilder bearbeiten?',
|
|
25
|
+
answer: 'Ja. Sie können ein PNG- oder WebP-Spritesheet mit Zeilen- und Spaltenanzahl laden oder mehrere geordnete Einzelbilder auswählen.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
question: 'Funktioniert der Export direkt in jeder Game-Engine?',
|
|
29
|
+
answer: 'Das JSON ist bewusst engine-neutral gestaltet. Es speichert Frame-Rechtecke, Drehpunkte, Ebenennamen und geometrische Formen ohne Bindung an eine bestimmte Engine.',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const howTo = [
|
|
34
|
+
{ name: 'Grafikmaterial laden', text: 'Wählen Sie ein PNG/WebP-Spritesheet oder eine Sequenz geordneter Einzelbilder aus. Die Bearbeitung erfolgt lokal.' },
|
|
35
|
+
{ name: 'Frames definieren', text: 'Geben Sie bei einem Spritesheet die Anzahl der Zeilen und Spalten an und überprüfen Sie die Einzelbilder in der Leiste.' },
|
|
36
|
+
{ name: 'Kollisions-Ebenen zeichnen', text: 'Wählen Sie Hitbox, Hurtbox, Pushbox, Grabbox oder Sensor und ziehen Sie Rechtecke oder Kreise über das aktive Frame.' },
|
|
37
|
+
{ name: 'Bewegung im Zeitverlauf anpassen', text: 'Bearbeiten Sie Koordinaten exakt, kopieren Sie Formen auf benachbarte Frames und nutzen Sie die Zwiebelschalenansicht.' },
|
|
38
|
+
{ name: 'JSON-Projekt exportieren', text: 'Laden Sie das neutrale JSON-Projekt und ein Übersichts-PNG herunter. Bewahren Sie die Quellbilder zusammen mit dem JSON auf.' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const softwareApplication: WithContext<SoftwareApplication> = {
|
|
42
|
+
'@context': 'https://schema.org',
|
|
43
|
+
'@type': 'SoftwareApplication',
|
|
44
|
+
name: title,
|
|
45
|
+
applicationCategory: 'DeveloperApplication',
|
|
46
|
+
operatingSystem: 'Any',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const faqPage: WithContext<FAQPage> = {
|
|
50
|
+
'@context': 'https://schema.org',
|
|
51
|
+
'@type': 'FAQPage',
|
|
52
|
+
mainEntity: faq.map((item) => ({
|
|
53
|
+
'@type': 'Question',
|
|
54
|
+
name: item.question,
|
|
55
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const howToSchema: WithContext<HowTo> = {
|
|
60
|
+
'@context': 'https://schema.org',
|
|
61
|
+
'@type': 'HowTo',
|
|
62
|
+
name: title,
|
|
63
|
+
step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const content: ToolLocaleContent<HitboxHurtboxAnimatorUI> = {
|
|
67
|
+
slug,
|
|
68
|
+
title,
|
|
69
|
+
description,
|
|
70
|
+
ui: {
|
|
71
|
+
onboarding: 'Laden Sie eine Animation, überprüfen Sie die Frame-Zuschnitte und zeichnen Sie Bereiche für Angriff, Treffer, Physik oder Erkennung.',
|
|
72
|
+
privacyNote: 'Lokale Bearbeitung. Bilder werden nicht hochgeladen.',
|
|
73
|
+
loadSprite: 'Grafikmaterial laden',
|
|
74
|
+
loadHint: 'Wählen Sie ein Spritesheet oder eine Reihe geordneter PNG/WebP-Bilder.',
|
|
75
|
+
chooseImages: 'Bilder auswählen',
|
|
76
|
+
slicingTitle: 'Frame-Zuschnitt',
|
|
77
|
+
rowsLabel: 'Zeilen',
|
|
78
|
+
columnsLabel: 'Spalten',
|
|
79
|
+
applySlicing: 'Zuschneiden',
|
|
80
|
+
playbackTitle: 'Vorschau',
|
|
81
|
+
previousFrame: 'Vorheriges Frame',
|
|
82
|
+
play: 'Abspielen',
|
|
83
|
+
pause: 'Pause',
|
|
84
|
+
nextFrame: 'Nächstes Frame',
|
|
85
|
+
fpsLabel: 'Bilder pro Sekunde',
|
|
86
|
+
onionPrevious: 'Vorherige Folie',
|
|
87
|
+
onionNext: 'Nächste Folie',
|
|
88
|
+
layerTitle: 'Kollisions-Ebenen',
|
|
89
|
+
typeHitbox: 'Hitbox',
|
|
90
|
+
typeHurtbox: 'Hurtbox',
|
|
91
|
+
typePushbox: 'Pushbox',
|
|
92
|
+
typeGrabbox: 'Grabbox',
|
|
93
|
+
typeSensor: 'Sensor',
|
|
94
|
+
typeCustom: 'Benutzerdefiniert',
|
|
95
|
+
shapeRectangle: 'Rechteck',
|
|
96
|
+
shapeCircle: 'Kreis',
|
|
97
|
+
drawShape: 'Zeichnen',
|
|
98
|
+
selectShape: 'Auswählen',
|
|
99
|
+
stageLabel: 'Arbeitsfläche',
|
|
100
|
+
emptyStage: 'Laden Sie Grafiken, um mit dem Zeichnen von Kollisions-Ebenen zu beginnen.',
|
|
101
|
+
frameReadout: 'Frame {current} von {total}',
|
|
102
|
+
timelineTitle: 'Frame-Leiste',
|
|
103
|
+
inspectorTitle: 'Form-Inspektor',
|
|
104
|
+
noSelection: 'Wählen Sie eine Form aus, um deren Koordinaten zu bearbeiten.',
|
|
105
|
+
nameLabel: 'Ebenenname',
|
|
106
|
+
xLabel: 'X in Pixeln',
|
|
107
|
+
yLabel: 'Y in Pixeln',
|
|
108
|
+
widthLabel: 'Breite in Pixeln',
|
|
109
|
+
heightLabel: 'Höhe in Pixeln',
|
|
110
|
+
radiusLabel: 'Radius in Pixeln',
|
|
111
|
+
duplicateShape: 'Duplizieren',
|
|
112
|
+
mirrorShape: 'Horizontal spiegeln',
|
|
113
|
+
deleteShape: 'Form löschen',
|
|
114
|
+
copyPrevious: 'Vorheriges Frame hierher kopieren',
|
|
115
|
+
copyAll: 'Auf alle Frames kopieren',
|
|
116
|
+
pivotTitle: 'Drehpunkt',
|
|
117
|
+
pivotXLabel: 'Drehpunkt X',
|
|
118
|
+
pivotYLabel: 'Drehpunkt Y',
|
|
119
|
+
exportTitle: 'Projekt exportieren',
|
|
120
|
+
exportJson: 'JSON herunterladen',
|
|
121
|
+
importJson: 'JSON importieren',
|
|
122
|
+
exportContactSheet: 'Übersichtsbild herunterladen',
|
|
123
|
+
resetTool: 'Ebenen zurücksetzen',
|
|
124
|
+
undo: 'Rückgängig',
|
|
125
|
+
redo: 'Wiederholen',
|
|
126
|
+
statusReady: 'Arbeitsfläche bereit.',
|
|
127
|
+
statusImageLoaded: '{count} Bilddateien geladen.',
|
|
128
|
+
statusShapeCreated: 'Neue Form hinzugefügt.',
|
|
129
|
+
statusShapeUpdated: 'Form angepasst.',
|
|
130
|
+
statusImported: 'Projekt importiert.',
|
|
131
|
+
statusExported: 'Export vorbereitet.',
|
|
132
|
+
statusError: 'Datei konnte nicht gelesen werden.',
|
|
133
|
+
framesBadge: '{count} Frames',
|
|
134
|
+
shapesBadge: '{count} Formen',
|
|
135
|
+
coverageBadge: '{percent}% Abdeckung',
|
|
136
|
+
coordinatesNote: 'Koordinaten nutzen die obere linke Ecke des Frames als Ursprung (0,0).',
|
|
137
|
+
localOnlyDisclosure: 'Die JSON-Datei speichert Bildnamen, Drehpunkte und Geometrien. Sie enthält keine Bilddaten.',
|
|
138
|
+
limitationDisclosure: 'Die Ebenen beschreiben gezeichnete Bereiche. Prüfen Sie das Verhalten in Ihrer Game-Engine.',
|
|
139
|
+
},
|
|
140
|
+
seo: [
|
|
141
|
+
{
|
|
142
|
+
type: 'title',
|
|
143
|
+
level: 2,
|
|
144
|
+
text: 'Hitboxes und Hurtboxes direkt am Sprite-Bewegungsablauf ausrichten',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'paragraph',
|
|
148
|
+
html: 'Die Festlegung von Kollisionen wird schwierig, wenn jedes Einzelbild isoliert betrachtet wird. Dieser Editor vereint Sprite-Grafik, Kollisions-Ebenen, Zwiebelschalenansicht und Frame-Leiste. So sehen Sie genau, wann ein Angriff startet und wie die Trefferfläche der Bewegung folgt.',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'title',
|
|
152
|
+
level: 2,
|
|
153
|
+
text: 'Kollisions-Ebenen nach ihrer Spiellogik wählen',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'table',
|
|
157
|
+
headers: ['Ebene', 'Typische Funktion', 'Prüffrage'],
|
|
158
|
+
rows: [
|
|
159
|
+
['Hitbox', 'Bereich, der einen Angriff auslöst', 'Erscheint sie nur in den aktiven Frames?'],
|
|
160
|
+
['Hurtbox', 'Bereich, der Treffer empfängt', 'Folgt sie dem Körper ohne unnatürliche Lücken?'],
|
|
161
|
+
['Pushbox', 'Physikalischer Bereich zur Abstandshaltung', 'Bleibt sie stabil, um Ruckeln zu vermeiden?'],
|
|
162
|
+
['Grabbox', 'Reichweite für Würfe oder Griffe', 'Passt das Timing zur visuellen Animation?'],
|
|
163
|
+
['Sensor', 'Erkennungszone für Interaktionen', 'Ist die Bezeichnung eindeutig?'],
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'title',
|
|
168
|
+
level: 2,
|
|
169
|
+
text: 'Das Koordinatensystem verstehen und nutzen',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'paragraph',
|
|
173
|
+
html: 'Das exportierte Projekt misst X und Y ausgehend von der oberen linken Ecke des zugeschnittenen Frames. Breite und Höhe sind Pixelwerte, und der Drehpunkt nutzt dieselben lokalen Koordinaten.',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'tip',
|
|
177
|
+
title: 'Ablauf von Angriff, Treffer und Erholung gemeinsam prüfen',
|
|
178
|
+
html: 'Spielen Sie die Gesamtanimation nach jeder Anpassung ab, um flüssige Übergänge ohne abrupte Sprünge sicherzustellen.',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: 'title',
|
|
182
|
+
level: 2,
|
|
183
|
+
text: 'Die Kontaktbogen-Übersicht im Entwicklerteam nutzen',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: 'paragraph',
|
|
187
|
+
html: 'Das Übersichts-PNG zeigt alle Frames mit ihren eingezeichneten Ebenen auf einer Fläche. Nutzen Sie es zusammen mit dem JSON-Export für die Abstimmung zwischen Grafik, Design und Programmierung.',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
faq,
|
|
191
|
+
bibliographyTitle: 'Referenzen zur Kollisionsentwicklung',
|
|
192
|
+
bibliography: bibliographyEntries,
|
|
193
|
+
howTo,
|
|
194
|
+
schemas: [softwareApplication, faqPage, howToSchema],
|
|
195
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
import type { HitboxHurtboxAnimatorUI } from '../ui';
|
|
4
|
+
import { bibliographyEntries } from '../bibliography';
|
|
5
|
+
|
|
6
|
+
const faq = [
|
|
7
|
+
{
|
|
8
|
+
question: 'What is the difference between a hitbox and a hurtbox?',
|
|
9
|
+
answer: 'A hitbox usually marks an attacking region, while a hurtbox marks a region that can receive that attack. Pushboxes can keep characters apart, grabboxes can define throw reach, and sensors can represent detection areas. Your engine and game rules decide the final behavior.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
question: 'Do my sprite files leave the browser?',
|
|
13
|
+
answer: 'No. Images are decoded, sliced, drawn, and exported in your browser. The tool stores only editor preferences such as playback speed and onion skin visibility. Save the JSON file yourself if you want to keep the project.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: 'Which coordinate system does the JSON export use?',
|
|
17
|
+
answer: 'Every frame uses pixels measured from its top left corner. Rectangle and circle bounds store nonnegative x, y, width, and height values. Each frame also stores its own pivot in the same coordinate system.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Can I edit a sprite sheet and separate frame images?',
|
|
21
|
+
answer: 'Yes. Load one PNG or WebP and set its rows and columns for uniform slicing, or select several ordered images and the tool will use one image per frame. File names with numbers are sorted naturally before the sequence is created.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Does the export work directly in every game engine?',
|
|
25
|
+
answer: 'The JSON is intentionally engine neutral. It records frame rectangles, pivots, semantic layer names, and primitive shapes without claiming a universal engine mapping. Convert those values in your project while accounting for its origin, scale, and collider conventions.',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const howTo = [
|
|
30
|
+
{ name: 'Load animation artwork', text: 'Choose one PNG or WebP sprite sheet, or select an ordered sequence of frame images. All image work stays on your device.' },
|
|
31
|
+
{ name: 'Define the frames', text: 'For a uniform sheet, enter its row and column count. Step through the filmstrip and confirm that every crop contains one animation frame.' },
|
|
32
|
+
{ name: 'Draw semantic collision layers', text: 'Choose a hitbox, hurtbox, pushbox, grabbox, sensor, or custom layer, then drag a rectangle or circle over the active frame.' },
|
|
33
|
+
{ name: 'Refine motion across time', text: 'Select shapes to drag or edit exact coordinates, copy useful geometry to neighboring frames, and use onion skin layers to compare motion.' },
|
|
34
|
+
{ name: 'Export reproducible data', text: 'Download the engine neutral JSON project and a PNG contact sheet. Keep the original images beside the JSON because image pixels are not embedded.' },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const softwareApplication: WithContext<SoftwareApplication> = {
|
|
38
|
+
'@context': 'https://schema.org',
|
|
39
|
+
'@type': 'SoftwareApplication',
|
|
40
|
+
name: 'Hitbox and Hurtbox Animator for Sprites',
|
|
41
|
+
applicationCategory: 'DeveloperApplication',
|
|
42
|
+
operatingSystem: 'Any',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const faqPage: WithContext<FAQPage> = {
|
|
46
|
+
'@context': 'https://schema.org',
|
|
47
|
+
'@type': 'FAQPage',
|
|
48
|
+
mainEntity: faq.map((item) => ({
|
|
49
|
+
'@type': 'Question',
|
|
50
|
+
name: item.question,
|
|
51
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
52
|
+
})),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const howToSchema: WithContext<HowTo> = {
|
|
56
|
+
'@context': 'https://schema.org',
|
|
57
|
+
'@type': 'HowTo',
|
|
58
|
+
name: 'How to animate hitboxes and hurtboxes from a sprite sheet',
|
|
59
|
+
step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const content: ToolLocaleContent<HitboxHurtboxAnimatorUI> = {
|
|
63
|
+
slug: 'hitbox-hurtbox-animator',
|
|
64
|
+
title: 'Hitbox and Hurtbox Animator for Sprites',
|
|
65
|
+
description: 'Draw collision layers on every sprite frame, preview them with onion skins, edit exact pixel coordinates, and export engine neutral JSON without uploading artwork.',
|
|
66
|
+
ui: {
|
|
67
|
+
onboarding: 'Load the current animation, confirm its frame cuts, then draw the regions that attack, receive damage, block movement, grab, or sense. The filmstrip shows where every layer exists.',
|
|
68
|
+
privacyNote: 'Local animation desk. Artwork is not uploaded.',
|
|
69
|
+
loadSprite: 'Bring artwork to the light table',
|
|
70
|
+
loadHint: 'Choose one uniform sprite sheet or several ordered PNG and WebP frames. Numbered file names are sorted naturally.',
|
|
71
|
+
chooseImages: 'Choose sprite images',
|
|
72
|
+
slicingTitle: 'Frame slicing',
|
|
73
|
+
rowsLabel: 'Rows',
|
|
74
|
+
columnsLabel: 'Columns',
|
|
75
|
+
applySlicing: 'Cut filmstrip',
|
|
76
|
+
playbackTitle: 'Motion preview',
|
|
77
|
+
previousFrame: 'Previous frame',
|
|
78
|
+
play: 'Play',
|
|
79
|
+
pause: 'Pause',
|
|
80
|
+
nextFrame: 'Next frame',
|
|
81
|
+
fpsLabel: 'Frames per second',
|
|
82
|
+
onionPrevious: 'Previous acetate',
|
|
83
|
+
onionNext: 'Next acetate',
|
|
84
|
+
layerTitle: 'Collision inks',
|
|
85
|
+
typeHitbox: 'Hitbox',
|
|
86
|
+
typeHurtbox: 'Hurtbox',
|
|
87
|
+
typePushbox: 'Pushbox',
|
|
88
|
+
typeGrabbox: 'Grabbox',
|
|
89
|
+
typeSensor: 'Sensor',
|
|
90
|
+
typeCustom: 'Custom',
|
|
91
|
+
shapeRectangle: 'Rectangle',
|
|
92
|
+
shapeCircle: 'Circle',
|
|
93
|
+
drawShape: 'Draw',
|
|
94
|
+
selectShape: 'Select',
|
|
95
|
+
stageLabel: 'Animation light table',
|
|
96
|
+
emptyStage: 'Load sprite artwork to reveal the frame and start drawing collision layers.',
|
|
97
|
+
frameReadout: 'Frame {current} of {total}',
|
|
98
|
+
timelineTitle: 'Collision filmstrip',
|
|
99
|
+
inspectorTitle: 'Selected acetate',
|
|
100
|
+
noSelection: 'Switch to Select and choose a shape to edit exact values.',
|
|
101
|
+
nameLabel: 'Layer name',
|
|
102
|
+
xLabel: 'X in pixels',
|
|
103
|
+
yLabel: 'Y in pixels',
|
|
104
|
+
widthLabel: 'Width in pixels',
|
|
105
|
+
heightLabel: 'Height in pixels',
|
|
106
|
+
radiusLabel: 'Radius in pixels',
|
|
107
|
+
duplicateShape: 'Duplicate',
|
|
108
|
+
mirrorShape: 'Mirror horizontally',
|
|
109
|
+
deleteShape: 'Delete selected shape',
|
|
110
|
+
copyPrevious: 'Copy previous frame here',
|
|
111
|
+
copyAll: 'Copy this frame to all',
|
|
112
|
+
pivotTitle: 'Frame pivot',
|
|
113
|
+
pivotXLabel: 'Pivot X',
|
|
114
|
+
pivotYLabel: 'Pivot Y',
|
|
115
|
+
exportTitle: 'Take the animation plan with you',
|
|
116
|
+
exportJson: 'Download JSON',
|
|
117
|
+
importJson: 'Import JSON',
|
|
118
|
+
exportContactSheet: 'Download contact sheet',
|
|
119
|
+
resetProject: 'Clear collision layers',
|
|
120
|
+
undo: 'Undo',
|
|
121
|
+
redo: 'Redo',
|
|
122
|
+
statusReady: 'The animation desk is ready.',
|
|
123
|
+
statusImageLoaded: '{count} image files loaded locally.',
|
|
124
|
+
statusShapeCreated: 'A new collision shape was added to this frame.',
|
|
125
|
+
statusShapeUpdated: 'Collision geometry updated.',
|
|
126
|
+
statusImported: 'Collision project imported. Reconnect its source images to preview the artwork.',
|
|
127
|
+
statusExported: 'Export prepared on this device.',
|
|
128
|
+
statusError: 'The selected file could not be read as a supported image or collision project.',
|
|
129
|
+
framesBadge: '{count} frames',
|
|
130
|
+
shapesBadge: '{count} shapes',
|
|
131
|
+
coverageBadge: '{percent}% covered',
|
|
132
|
+
coordinatesNote: 'Coordinates use the top left of each frame as zero. Bounds are clamped to the visible frame and stored in pixels.',
|
|
133
|
+
localOnlyDisclosure: 'The JSON stores image names, frame cuts, pivots, and collision geometry. It does not embed the image pixels.',
|
|
134
|
+
limitationDisclosure: 'Collision layers describe authored regions only. Test them inside your game because timing, scale, transforms, physics settings, and combat rules remain engine specific.',
|
|
135
|
+
},
|
|
136
|
+
seo: [
|
|
137
|
+
{
|
|
138
|
+
type: 'title',
|
|
139
|
+
level: 2,
|
|
140
|
+
text: 'Build hitboxes and hurtboxes around the animation itself',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
type: 'paragraph',
|
|
144
|
+
html: 'Collision authoring becomes difficult when every frame is judged in isolation. This editor puts the sprite, semantic collision inks, neighboring onion skins, and filmstrip in one place. You can see where an attack begins, how a hurt region follows the body, and whether a pushbox stays stable while the artwork stretches or turns.',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'title',
|
|
148
|
+
level: 2,
|
|
149
|
+
text: 'Choose each collision layer by gameplay responsibility',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'table',
|
|
153
|
+
headers: ['Layer', 'Typical responsibility', 'Question to review'],
|
|
154
|
+
rows: [
|
|
155
|
+
['Hitbox', 'Area that delivers an attack or active effect', 'Does it appear only on the intended active frames?'],
|
|
156
|
+
['Hurtbox', 'Area that can receive an attack or effect', 'Does it follow the readable body volume without unnecessary gaps?'],
|
|
157
|
+
['Pushbox', 'Area used to keep actors from occupying the same space', 'Does it remain stable enough to avoid visible shoving or jitter?'],
|
|
158
|
+
['Grabbox', 'Reach used to begin a throw or hold', 'Does its timing and range match the animation cue?'],
|
|
159
|
+
['Sensor', 'Detection area for interactions, ledges, targets, or triggers', 'Is its purpose named clearly enough for implementation?'],
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
type: 'title',
|
|
164
|
+
level: 2,
|
|
165
|
+
text: 'Read and preserve the coordinate contract',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
type: 'paragraph',
|
|
169
|
+
html: 'The exported project measures x and y from the top left of each cropped frame. Width and height are nonnegative pixel dimensions, and the pivot uses the same frame local coordinates. This makes save and import cycles reproducible, but your engine adapter still needs to account for texture scale, centered origins, world transforms, and its own shape representation.',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'tip',
|
|
173
|
+
title: 'Review anticipation, contact, and recovery together',
|
|
174
|
+
html: 'Play the complete motion after editing a single frame. A geometrically tidy box can still start too early, disappear before the visual follow through, or drift between frames. Use the previous and next acetates to judge continuity without hiding intentional changes.',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
type: 'title',
|
|
178
|
+
level: 2,
|
|
179
|
+
text: 'Use the contact sheet as an implementation review artifact',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
type: 'paragraph',
|
|
183
|
+
html: 'The PNG contact sheet reveals every authored frame and its colored layers in one image. Pair it with the JSON export when reviewing combat timing with design, animation, and engineering. The image explains the motion at a glance, while the JSON carries the exact frame rectangles, pivots, semantic names, and primitive bounds.',
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
faq,
|
|
187
|
+
bibliographyTitle: 'Collision authoring references',
|
|
188
|
+
bibliography: bibliographyEntries,
|
|
189
|
+
howTo,
|
|
190
|
+
schemas: [softwareApplication, faqPage, howToSchema],
|
|
191
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
import type { HitboxHurtboxAnimatorUI } from '../ui';
|
|
4
|
+
import { bibliographyEntries } from '../bibliography';
|
|
5
|
+
|
|
6
|
+
const slug = 'animador-hitbox-hurtbox-sprites';
|
|
7
|
+
const title = 'Animador de Hitbox y Hurtbox para Sprites';
|
|
8
|
+
const description = 'Dibuja capas de colisión en cada fotograma de tu sprite, previsualiza secuencias con papel cebolla, ajusta coordenadas exactas en píxeles y exporta JSON neutro.';
|
|
9
|
+
|
|
10
|
+
const faq = [
|
|
11
|
+
{
|
|
12
|
+
question: '¿Cuál es la diferencia entre una hitbox y una hurtbox?',
|
|
13
|
+
answer: 'Una hitbox define la zona que genera un ataque, mientras que una hurtbox delimita el área vulnerable que recibe dicho ataque. Las pushboxes mantienen alejados a los personajes, las grabboxes delimitan agarres y los sensores detectan eventos. El motor y las reglas del juego definen el comportamiento final.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: '¿Mis archivos de sprites salen del navegador?',
|
|
17
|
+
answer: 'No. Las imágenes se decodifican, recortan, dibujan y exportan íntegramente dentro de tu navegador. La herramienta solo guarda en el almacenamiento local preferencias de edición como la velocidad de reproducción y la visibilidad del papel cebolla. Guarda el JSON para conservar tu proyecto.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: '¿Qué sistema de coordenadas utiliza el archivo JSON exportado?',
|
|
21
|
+
answer: 'Cada fotograma mide sus coordenadas en píxeles tomando como origen (0,0) la esquina superior izquierda del recorte. Las dimensiones de rectángulos y círculos almacenan valores no negativos de x, y, ancho y alto. Cada fotograma incluye también su propio punto de pivote en este mismo sistema local.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: '¿Puedo editar hojas de sprites completas e imágenes de fotogramas sueltos?',
|
|
25
|
+
answer: 'Sí. Puedes cargar una imagen PNG o WebP indicando filas y columnas para una división uniforme, o bien seleccionar varias imágenes ordenadas para crear una secuencia cuadro a cuadro. Los archivos numerados se ordenan de forma natural.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
question: '¿El archivo exportado funciona directamente en cualquier motor de juego?',
|
|
29
|
+
answer: 'El formato JSON es deliberadamente neutro. Registra recortes de fotogramas, pivotes, nombres semánticos de capa y geometría primitiva sin imponer la estructura de un motor específico. Convierte estos datos en tu proyecto considerando su escala, origen y sistema de colisiones.',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const howTo = [
|
|
34
|
+
{ name: 'Cargar el arte de animación', text: 'Selecciona una hoja de sprites PNG o WebP, o bien una secuencia ordenada de imágenes. Todo el procesamiento se realiza localmente en tu dispositivo.' },
|
|
35
|
+
{ name: 'Definir los fotogramas', text: 'Para una hoja uniforme, especifica el número de filas y columnas. Recorre la cinta de animación para verificar que cada recorte encuadre un fotograma limpio.' },
|
|
36
|
+
{ name: 'Dibujar capas semánticas de colisión', text: 'Elige una capa de hitbox, hurtbox, pushbox, grabbox, sensor o personalizada, y arrastra un rectángulo o círculo sobre el fotograma activo.' },
|
|
37
|
+
{ name: 'Refinar el movimiento en el tiempo', text: 'Selecciona figuras para ajustar sus coordenadas numéricas, copia geometría a fotogramas adyacentes y activa capas de papel cebolla para comparar el movimiento.' },
|
|
38
|
+
{ name: 'Exportar datos reproducibles', text: 'Descarga el proyecto JSON neutro y una hoja de contactos en PNG. Conserva las imágenes originales junto al JSON, ya que los píxeles no se incrustan en el archivo.' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const softwareApplication: WithContext<SoftwareApplication> = {
|
|
42
|
+
'@context': 'https://schema.org',
|
|
43
|
+
'@type': 'SoftwareApplication',
|
|
44
|
+
name: title,
|
|
45
|
+
applicationCategory: 'DeveloperApplication',
|
|
46
|
+
operatingSystem: 'Any',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const faqPage: WithContext<FAQPage> = {
|
|
50
|
+
'@context': 'https://schema.org',
|
|
51
|
+
'@type': 'FAQPage',
|
|
52
|
+
mainEntity: faq.map((item) => ({
|
|
53
|
+
'@type': 'Question',
|
|
54
|
+
name: item.question,
|
|
55
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const howToSchema: WithContext<HowTo> = {
|
|
60
|
+
'@context': 'https://schema.org',
|
|
61
|
+
'@type': 'HowTo',
|
|
62
|
+
name: title,
|
|
63
|
+
step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const content: ToolLocaleContent<HitboxHurtboxAnimatorUI> = {
|
|
67
|
+
slug,
|
|
68
|
+
title,
|
|
69
|
+
description,
|
|
70
|
+
ui: {
|
|
71
|
+
onboarding: 'Carga una animación, confirma los recortes de fotograma y dibuja las áreas de ataque, daño, colisión física, agarre o detección. La cinta de fotogramas mostrará las capas activas.',
|
|
72
|
+
privacyNote: 'Mesa de animación local. Las imágenes no se suben a ningún servidor.',
|
|
73
|
+
loadSprite: 'Colocar material gráfico en la mesa óptica',
|
|
74
|
+
loadHint: 'Selecciona una hoja de sprites uniforme o varias imágenes ordenadas en PNG o WebP. Los archivos numerados se ordenan de forma natural.',
|
|
75
|
+
chooseImages: 'Seleccionar imágenes de sprite',
|
|
76
|
+
slicingTitle: 'División de fotogramas',
|
|
77
|
+
rowsLabel: 'Filas',
|
|
78
|
+
columnsLabel: 'Columnas',
|
|
79
|
+
applySlicing: 'Cortar cinta',
|
|
80
|
+
playbackTitle: 'Vista previa de movimiento',
|
|
81
|
+
previousFrame: 'Fotograma anterior',
|
|
82
|
+
play: 'Reproducir',
|
|
83
|
+
pause: 'Pausar',
|
|
84
|
+
nextFrame: 'Siguiente fotograma',
|
|
85
|
+
fpsLabel: 'Fotogramas por segundo',
|
|
86
|
+
onionPrevious: 'Acetato anterior',
|
|
87
|
+
onionNext: 'Acetato siguiente',
|
|
88
|
+
layerTitle: 'Tintas de colisión',
|
|
89
|
+
typeHitbox: 'Hitbox (Ataque)',
|
|
90
|
+
typeHurtbox: 'Hurtbox (Vulnerable)',
|
|
91
|
+
typePushbox: 'Pushbox (Física)',
|
|
92
|
+
typeGrabbox: 'Grabbox (Agarre)',
|
|
93
|
+
typeSensor: 'Sensor',
|
|
94
|
+
typeCustom: 'Personalizada',
|
|
95
|
+
shapeRectangle: 'Rectángulo',
|
|
96
|
+
shapeCircle: 'Círculo',
|
|
97
|
+
drawShape: 'Dibujar',
|
|
98
|
+
selectShape: 'Seleccionar',
|
|
99
|
+
stageLabel: 'Mesa óptica de animación',
|
|
100
|
+
emptyStage: 'Carga material gráfico para visualizar el fotograma y comenzar a dibujar capas de colisión.',
|
|
101
|
+
frameReadout: 'Fotograma {current} de {total}',
|
|
102
|
+
timelineTitle: 'Cinta de colisiones',
|
|
103
|
+
inspectorTitle: 'Acetato seleccionado',
|
|
104
|
+
noSelection: 'Activa la herramienta Seleccionar y haz clic en una figura para editar sus dimensiones exactas.',
|
|
105
|
+
nameLabel: 'Nombre de la capa',
|
|
106
|
+
xLabel: 'X en píxeles',
|
|
107
|
+
yLabel: 'Y en píxeles',
|
|
108
|
+
widthLabel: 'Ancho en píxeles',
|
|
109
|
+
heightLabel: 'Alto en píxeles',
|
|
110
|
+
radiusLabel: 'Radio en píxeles',
|
|
111
|
+
duplicateShape: 'Duplicar',
|
|
112
|
+
mirrorShape: 'Reflejar horizontalmente',
|
|
113
|
+
deleteShape: 'Eliminar figura seleccionada',
|
|
114
|
+
copyPrevious: 'Copiar fotograma anterior aquí',
|
|
115
|
+
copyAll: 'Copiar este fotograma a todos',
|
|
116
|
+
pivotTitle: 'Pivote del fotograma',
|
|
117
|
+
pivotXLabel: 'Pivote X',
|
|
118
|
+
pivotYLabel: 'Pivote Y',
|
|
119
|
+
exportTitle: 'Exportar plan de animación',
|
|
120
|
+
exportJson: 'Descargar JSON',
|
|
121
|
+
importJson: 'Importar JSON',
|
|
122
|
+
exportContactSheet: 'Descargar hoja de contactos',
|
|
123
|
+
resetProject: 'Limpiar capas de colisión',
|
|
124
|
+
undo: 'Deshacer',
|
|
125
|
+
redo: 'Rehacer',
|
|
126
|
+
statusReady: 'La mesa de animación está lista.',
|
|
127
|
+
statusImageLoaded: 'Se han cargado {count} archivos de imagen en local.',
|
|
128
|
+
statusShapeCreated: 'Se ha añadido una nueva figura de colisión a este fotograma.',
|
|
129
|
+
statusShapeUpdated: 'Geometría de colisión actualizada.',
|
|
130
|
+
statusImported: 'Proyecto de colisión importado. Vuelve a enlazar las imágenes de origen para previsualizar el arte.',
|
|
131
|
+
statusExported: 'Exportación preparada en este dispositivo.',
|
|
132
|
+
statusError: 'El archivo seleccionado no se pudo leer como una imagen o proyecto válido.',
|
|
133
|
+
framesBadge: '{count} fotogramas',
|
|
134
|
+
shapesBadge: '{count} figuras',
|
|
135
|
+
coverageBadge: '{percent}% cubierto',
|
|
136
|
+
coordinatesNote: 'Las coordenadas toman la esquina superior izquierda de cada fotograma recortado como origen (0,0). Los límites se miden en píxeles dentro del fotograma.',
|
|
137
|
+
localOnlyDisclosure: 'El archivo JSON almacena nombres de imagen, recortes, pivotes y geometría de colisión. No incrusta los píxeles de las imágenes.',
|
|
138
|
+
limitationDisclosure: 'Las capas definen regiones geométricas de diseño. Pruébalas en tu motor de juego, ya que la sincronización, escala, físicas y reglas de combate dependen del código de tu proyecto.',
|
|
139
|
+
},
|
|
140
|
+
seo: [
|
|
141
|
+
{
|
|
142
|
+
type: 'title',
|
|
143
|
+
level: 2,
|
|
144
|
+
text: 'Diseña hitboxes y hurtboxes alineadas con el movimiento real del sprite',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'paragraph',
|
|
148
|
+
html: 'Configurar colisiones resulta complejo cuando cada fotograma se evalúa de manera aislada. Este editor integra en una sola vista el fotograma del sprite, las capas de colisión semánticas, los acetatos adyacentes en papel cebolla y la cinta temporal. Esto permite verificar cuándo se activa un golpe, cómo sigue el área vulnerable al volumen del personaje y si la caja física mantiene estabilidad durante estiramientos o giros.',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'title',
|
|
152
|
+
level: 2,
|
|
153
|
+
text: 'Elige cada capa de colisión según su función técnica en el juego',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'table',
|
|
157
|
+
headers: ['Capa', 'Responsabilidad habitual', 'Aspecto a revisar'],
|
|
158
|
+
rows: [
|
|
159
|
+
['Hitbox', 'Área que aplica un golpe, daño o efecto activo', '¿Aparece únicamente durante los fotogramas de impacto previstos?'],
|
|
160
|
+
['Hurtbox', 'Área que recibe ataques o impactos del oponente', '¿Ajusta bien al volumen del personaje sin dejar huecos antinaturales?'],
|
|
161
|
+
['Pushbox', 'Caja de colisión física para impedir que dos personajes se solapen', '¿Mantiene dimensiones estables para evitar empujones o temblores bruscos?'],
|
|
162
|
+
['Grabbox', 'Alcance necesario para iniciar un agarre o llave', '¿Coincide la ventana de tiempo y el rango con la clave visual de la animación?'],
|
|
163
|
+
['Sensor', 'Área de detección para interacciones, bordes, objetivos o disparadores', '¿El nombre asignado describe con claridad su propósito técnico?'],
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'title',
|
|
168
|
+
level: 2,
|
|
169
|
+
text: 'Interpretación y estructura del sistema de coordenadas',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'paragraph',
|
|
173
|
+
html: 'El proyecto exportado mide las coordenadas X e Y desde la esquina superior izquierda de cada fotograma recortado. Las dimensiones de ancho y alto son siempre valores no negativos en píxeles, y el pivote utiliza este mismo sistema local. Esto asegura que los ciclos de guardado e importación sean completamente reproducibles, aunque el adaptador de tu motor deberá considerar la escala de textura, los orígenes centrados y las transformaciones del mundo.',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'tip',
|
|
177
|
+
title: 'Revisa las fases de anticipación, impacto y recuperación en conjunto',
|
|
178
|
+
html: 'Reproduce la animación completa tras ajustar un fotograma clave. Una caja bien dibujada en reposo puede activarse demasiado pronto, desaparecer antes de tiempo o desplazarse de forma discontinua. Utiliza los acetatos anterior y posterior para verificar la fluidez del movimiento sin perder las modificaciones intencionadas.',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: 'title',
|
|
182
|
+
level: 2,
|
|
183
|
+
text: 'La hoja de contactos como herramienta de revisión para el equipo',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: 'paragraph',
|
|
187
|
+
html: 'La hoja de contactos en PNG muestra todos los fotogramas de la secuencia con sus capas de color superpuestas en un único lienzo. Utilízala junto al archivo JSON para revisar la sincronización de combate con los equipos de arte, diseño y programación. La imagen proporciona una visión de conjunto inmediata, mientras que el JSON aporta los valores exactos de recortes, pivotes y volúmenes de colisión.',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
faq,
|
|
191
|
+
bibliographyTitle: 'Referencias de diseño de colisiones',
|
|
192
|
+
bibliography: bibliographyEntries,
|
|
193
|
+
howTo,
|
|
194
|
+
schemas: [softwareApplication, faqPage, howToSchema],
|
|
195
|
+
};
|