@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-hurtbox-animator';
|
|
7
|
+
const title = 'ヒットボックス・ハートボックス アニメーター';
|
|
8
|
+
const description = 'スプライトフレームごとに当たり判定レイヤーを描画。オニオンスキンで動きをプレビューし、ピクセル単位で座標を編集してJSON出力。';
|
|
9
|
+
|
|
10
|
+
const faq = [
|
|
11
|
+
{
|
|
12
|
+
question: 'Hitbox(攻撃判定)とHurtbox(食らい判定)の違いは何ですか?',
|
|
13
|
+
answer: 'Hitboxは攻撃を発生させる領域、Hurtboxはダメージを受ける領域を指します。Pushboxはキャラクター同士の押し出し判定、Grabboxは 掴み判定、Sensorは検出エリアとして機能します。',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: 'スプライト画像が外部 server に送信されることはありますか?',
|
|
17
|
+
answer: 'いいえ。画像のデコード、切り出し、描画、出力処理はすべてブラウザ内でローカルに完結します。ローカルストレージには編集設定のみが保存されます。',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: 'JSON出力ではどの座標系が使用されますか?',
|
|
21
|
+
answer: '各フレームは切り出された枠の左上を原点(0,0)としてピクセル単位で測定されます。矩形や円のサイズはピボット位置とともに保存されます。',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'スプライトシートと個別フレーム画像の両方を編集できますか?',
|
|
25
|
+
answer: 'はい。PNGやWebPのスプライトシートの縦横分割数を指定するか、連番の個別画像ファイルを一括選択して読み込むことができます。',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
question: '出力データはすべてのゲームエンジンでそのまま使えますか?',
|
|
29
|
+
answer: 'JSONはエンジンに依存しない汎用フォーマットです。フレーム範囲、ピボット、レイヤー名、形状データが記録されています。',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const howTo = [
|
|
34
|
+
{ name: 'アニメーション画像を読み込む', text: 'PNG/WebPのスプライトシートまたは連番画像を選択します。処理はすべてローカルで完了します。' },
|
|
35
|
+
{ name: 'フレームの分割設定', text: 'スプライトシートの行数と列数を入力し、タイムラインで正確に切り出されているか確認します。' },
|
|
36
|
+
{ name: '当たり判定レイヤーの描画', text: 'Hitbox、Hurtbox、Pushboxなどの種類を選択し、アクティブなフレーム上に矩形や円を描画します。' },
|
|
37
|
+
{ name: '動きの調整', text: '座標を数 uniform 値で微調整し、隣接フレームへ形状をコピーしながら、オニオンスキンで動きのつながりを確認します。' },
|
|
38
|
+
{ name: 'プロジェクトの出力', text: '汎用JSONプロジェクトファイルとPNGのコンタクトシートをダウンロードします。元画像と一緒に保管してください。' },
|
|
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: 'スプライトアニメーションを読み込み、分割を確認して攻撃・喰らい・物理押し出しなどの判定エリアを描画します。',
|
|
72
|
+
privacyNote: 'ローカル専用アニメーションデスク。画像が送信されることはありません。',
|
|
73
|
+
loadSprite: '画像素材を作業台に配置',
|
|
74
|
+
loadHint: 'スプライトシートまたは連番PNG/WebP画像を選択してください。',
|
|
75
|
+
chooseImages: '画像を選択',
|
|
76
|
+
slicingTitle: 'フレーム分割',
|
|
77
|
+
rowsLabel: '行数',
|
|
78
|
+
columnsLabel: '列数',
|
|
79
|
+
applySlicing: '分割を適用',
|
|
80
|
+
playbackTitle: 'モーションプレビュー',
|
|
81
|
+
previousFrame: '前のフレーム',
|
|
82
|
+
play: '再生',
|
|
83
|
+
pause: '一時停止',
|
|
84
|
+
nextFrame: '次のフレーム',
|
|
85
|
+
fpsLabel: 'FPS (フレーム/秒)',
|
|
86
|
+
onionPrevious: '前のオニオンスキン',
|
|
87
|
+
onionNext: '次のオニオンスキン',
|
|
88
|
+
layerTitle: '判定レイヤー',
|
|
89
|
+
typeHitbox: 'Hitbox (攻撃)',
|
|
90
|
+
typeHurtbox: 'Hurtbox (喰らい)',
|
|
91
|
+
typePushbox: 'Pushbox (押し出し)',
|
|
92
|
+
typeGrabbox: 'Grabbox (掴み)',
|
|
93
|
+
typeSensor: 'Sensor (検出)',
|
|
94
|
+
typeCustom: 'カスタム',
|
|
95
|
+
shapeRectangle: '矩形',
|
|
96
|
+
shapeCircle: '円形',
|
|
97
|
+
drawShape: '描画',
|
|
98
|
+
selectShape: '選択',
|
|
99
|
+
stageLabel: 'アニメーションステージ',
|
|
100
|
+
emptyStage: '画像を読み込んで当たり判定の描画を開始してください。',
|
|
101
|
+
frameReadout: 'フレーム {current} / {total}',
|
|
102
|
+
timelineTitle: 'タイムライン',
|
|
103
|
+
inspectorTitle: '形状プロパティ',
|
|
104
|
+
noSelection: '形状を選択すると詳細な座標やサイズを編集できます。',
|
|
105
|
+
nameLabel: 'レイヤー名',
|
|
106
|
+
xLabel: 'X座標 (px)',
|
|
107
|
+
yLabel: 'Y座標 (px)',
|
|
108
|
+
widthLabel: '幅 (px)',
|
|
109
|
+
heightLabel: '高さ (px)',
|
|
110
|
+
radiusLabel: '半径 (px)',
|
|
111
|
+
duplicateShape: '複製',
|
|
112
|
+
mirrorShape: '左右反転',
|
|
113
|
+
deleteShape: '選択中形状を削除',
|
|
114
|
+
copyPrevious: '前のフレームからコピー',
|
|
115
|
+
copyAll: '全フレームにコピー',
|
|
116
|
+
pivotTitle: '原点ピボット',
|
|
117
|
+
pivotXLabel: 'ピボット X',
|
|
118
|
+
pivotYLabel: 'ピボット Y',
|
|
119
|
+
exportTitle: 'プロジェクト出力',
|
|
120
|
+
exportJson: 'JSONダウンロード',
|
|
121
|
+
importJson: 'JSONインポート',
|
|
122
|
+
exportContactSheet: 'コンタクトシート保存',
|
|
123
|
+
resetProject: 'レイヤーをリセット',
|
|
124
|
+
undo: '元に戻す',
|
|
125
|
+
redo: 'やり直し',
|
|
126
|
+
statusReady: '作業台が準備できました。',
|
|
127
|
+
statusImageLoaded: '{count} 個の画像ファイルを読み込みました。',
|
|
128
|
+
statusShapeCreated: '新しい判定形状を追加しました。',
|
|
129
|
+
statusShapeUpdated: '判定形状を更新しました。',
|
|
130
|
+
statusImported: 'プロジェクトをインポートしました。',
|
|
131
|
+
statusExported: '出力ファイルの準備が完了しました。',
|
|
132
|
+
statusError: 'ファイルの読み込みに失敗しました。',
|
|
133
|
+
framesBadge: '{count} フレーム',
|
|
134
|
+
shapesBadge: '{count} 個の形状',
|
|
135
|
+
coverageBadge: 'カバー率 {percent}%',
|
|
136
|
+
coordinatesNote: '座標は各フレーム画像の左上を原点(0,0)として計算されます。',
|
|
137
|
+
localOnlyDisclosure: 'JSONファイルには画像名、ピボット、判定形状のみが保存され、画像ピクセルは含まれません。',
|
|
138
|
+
limitationDisclosure: '本ツールは判定範囲の設計用です。実際の動作は使用するゲームエンジン上で確認してください。',
|
|
139
|
+
},
|
|
140
|
+
seo: [
|
|
141
|
+
{
|
|
142
|
+
type: 'title',
|
|
143
|
+
level: 2,
|
|
144
|
+
text: 'アニメーションの動きに合わせて正確なHitboxとHurtboxを設計',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'paragraph',
|
|
148
|
+
html: 'フレーム単体を個別に調整すると、アニメーション全体での当たり判定のつながりが崩れやすくなります。本ツールではスプライト画像、判定レイヤー、オニオンスキン、タイムラインを1つの画面で確認しながら一貫性のある判定を設定できます。',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'title',
|
|
152
|
+
level: 2,
|
|
153
|
+
text: 'ゲーム内での役割に応じた判定レイヤーの使い分け',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'table',
|
|
157
|
+
headers: ['レイヤー', '主な役割', '確認ポイント'],
|
|
158
|
+
rows: [
|
|
159
|
+
['Hitbox', '攻撃や効果を発生させる領域', '意図したアクティブフレームでのみ発生しているか'],
|
|
160
|
+
['Hurtbox', '攻撃を受けるキャラクターの体判定', 'キャラクターの輪郭に自然に沿っているか'],
|
|
161
|
+
['Pushbox', 'キャラクター同士の重なりを防ぐ物理判定', '画面のつきはなしやガタツキを防ぐため安定しているか'],
|
|
162
|
+
['Grabbox', '掴み技や投げを発生させる間合い判定', '発生タイミングや範囲がモーションと一致しているか'],
|
|
163
|
+
['Sensor', '壁や地面、トリガーなどを検出する領域', '目的が明確に名前付けされているか'],
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'title',
|
|
168
|
+
level: 2,
|
|
169
|
+
text: '座標系の定義とゲームエンジンへの適用',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'paragraph',
|
|
173
|
+
html: '出力されるJSONデータは、切り出された各フレームの左上を(0,0)としてX・Y座標をピクセル単位で記録します。',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'tip',
|
|
177
|
+
title: '予備動作・発生・硬直を通して全体の流れを確認する',
|
|
178
|
+
html: '1フレームの調整が終わったら必ず全再生を行い、モーション全体の判定がスムーズに変化しているかチェックしてください。',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: 'title',
|
|
182
|
+
level: 2,
|
|
183
|
+
text: 'コンタクトシートを活用したチーム内レビュー',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: 'paragraph',
|
|
187
|
+
html: 'PNG形式のコンタクトシートを出力すれば、全フレームの当たり判定レイヤーを一覧表示でき、アニメーターやプログラマーとの意思疎通がスムーズになります。',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
faq,
|
|
191
|
+
bibliographyTitle: '当たり判定設計の参考資料',
|
|
192
|
+
bibliography: bibliographyEntries,
|
|
193
|
+
howTo,
|
|
194
|
+
schemas: [softwareApplication, faqPage, howToSchema],
|
|
195
|
+
};
|
|
@@ -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-hurtbox-animator';
|
|
7
|
+
const title = '스프라이트 히트박스 & 허트박스 애니메이터';
|
|
8
|
+
const description = '스프라이트 프레임별 충돌 레이어를 작성하고, 어니언 스킨으로 동작을 미리보며, 정확한 피셀 좌표를 편집하여 JSON으로 내보내세요.';
|
|
9
|
+
|
|
10
|
+
const faq = [
|
|
11
|
+
{
|
|
12
|
+
question: 'Hitbox(공격 판정)와 Hurtbox(피격 판정)의 차이는 무엇인가요?',
|
|
13
|
+
answer: 'Hitbox는 공격을 가하는 영역이며, Hurtbox는 피격 판정을 받는 영역입니다. Pushbox는 캐릭터 간 거리를 유지하며, Grabbox는 잡기 범위를 설정하고, Sensor는 감지 영역으로 기능합니다.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: '스프라이트 이미지가 외부 서버로 전송되나요?',
|
|
17
|
+
answer: '아니요. 이미지 디코딩, 잘라내기, 그리기 및 내보내기는 100% 브라우저 내부에서 실행됩니다. 설정값만 로컬 저장소에 보관됩니다.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: '내보낸 JSON 파일은 어떤 좌표계를 사용하나요?',
|
|
21
|
+
answer: '각 프레임은 자른 영역의 좌측 상단을 (0,0) 원점으로 측정합니다. 사각형과 원의 좌표 및 피봇 위치는 원점 기준 피셀 단위로 저장됩니다.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: '스프라이트 시트와 개별 프레임 이미지를 모두 편집할 수 있나요?',
|
|
25
|
+
answer: '네. PNG 또는 WebP 스프라이트 시트의 행과 열 개수를 입력하여 균일하게 나누거나, 정렬된 개별 이미지 파일들을 선택할 수 있습니다.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
question: '내보낸 데이터는 모든 게임 엔진에서 바로 사용할 수 있나요?',
|
|
29
|
+
answer: 'JSON 형식은 특정 엔진에 종속되지 않는 범용 구조입니다. 프레임 영역, 피봇, 레이어 이름 및 기하학 데이터를 저장합니다.',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const howTo = [
|
|
34
|
+
{ name: '애니메이션 이미지 불러오기', text: 'PNG/WebP 스프라이트 시트 또는 정렬된 이미지 파일들을 선택합니다. 모든 처리는 로컬에서 수행됩니다.' },
|
|
35
|
+
{ name: '프레임 분할 설정', text: '스프라이트 시트의 행과 열 개수를 설정하고 필름 스트립에서 잘라낸 프레임을 확인합니다.' },
|
|
36
|
+
{ name: '충돌 레이어 그리기', text: 'Hitbox, Hurtbox, Pushbox 등 원하는 레이어를 선택하고 사각형 또는 원형 영역을 그립니다.' },
|
|
37
|
+
{ name: '동작 정밀 조정', text: '좌표 수치를 직접 편집하고 어니언 스킨으로 이전 프레임과의 동작 연결성을 확인합니다.' },
|
|
38
|
+
{ name: '프로젝트 내보내기', text: '범용 JSON 프로젝트 파일과 PNG 컨택트 시트를 다운로드합니다. 원본 이미지와 함께 보관하세요.' },
|
|
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: '스프라이트 애니메이션을 로드하고 프레임을 확인한 뒤 공격, 피격, 물리 충돌 영역을 지정하세요.',
|
|
72
|
+
privacyNote: '로컬 전용 애니메이션 작업대. 이미지가 전송되지 않습니다.',
|
|
73
|
+
loadSprite: '작업대에 이미지 배치',
|
|
74
|
+
loadHint: '스프라이트 시트 또는 정렬된 PNG/WebP 이미지를 선택하세요.',
|
|
75
|
+
chooseImages: '이미지 선택',
|
|
76
|
+
slicingTitle: '프레임 분할',
|
|
77
|
+
rowsLabel: '행 개수',
|
|
78
|
+
columnsLabel: '열 개수',
|
|
79
|
+
applySlicing: '분할 적용',
|
|
80
|
+
playbackTitle: '동작 미리보기',
|
|
81
|
+
previousFrame: '이전 프레임',
|
|
82
|
+
play: '재생',
|
|
83
|
+
pause: '일시정지',
|
|
84
|
+
nextFrame: '다음 프레임',
|
|
85
|
+
fpsLabel: 'FPS (초당 프레임)',
|
|
86
|
+
onionPrevious: '이전 어니언 스킨',
|
|
87
|
+
onionNext: '다음 어니언 스킨',
|
|
88
|
+
layerTitle: '충돌 레이어',
|
|
89
|
+
typeHitbox: 'Hitbox (공격)',
|
|
90
|
+
typeHurtbox: 'Hurtbox (피격)',
|
|
91
|
+
typePushbox: 'Pushbox (밀기)',
|
|
92
|
+
typeGrabbox: 'Grabbox (잡기)',
|
|
93
|
+
typeSensor: 'Sensor (감지)',
|
|
94
|
+
typeCustom: '사용자 지정',
|
|
95
|
+
shapeRectangle: '사각형',
|
|
96
|
+
shapeCircle: '원형',
|
|
97
|
+
drawShape: '그리기',
|
|
98
|
+
selectShape: '선택',
|
|
99
|
+
stageLabel: '애니메이션 무대',
|
|
100
|
+
emptyStage: '이미지를 불러와 충돌 레이어 작업을 시작하세요.',
|
|
101
|
+
frameReadout: '프레임 {current} / {total}',
|
|
102
|
+
timelineTitle: '타임라인',
|
|
103
|
+
inspectorTitle: '도형 속성',
|
|
104
|
+
noSelection: '도형을 선택하면 정밀 좌표와 크기를 편집할 수 있습니다.',
|
|
105
|
+
nameLabel: '레이어 이름',
|
|
106
|
+
xLabel: 'X 좌표 (px)',
|
|
107
|
+
yLabel: 'Y 좌표 (px)',
|
|
108
|
+
widthLabel: '너비 (px)',
|
|
109
|
+
heightLabel: '높이 (px)',
|
|
110
|
+
radiusLabel: '반지름 (px)',
|
|
111
|
+
duplicateShape: '복제',
|
|
112
|
+
mirrorShape: '좌우 반전',
|
|
113
|
+
deleteShape: '선택 도형 삭제',
|
|
114
|
+
copyPrevious: '이전 프레임 복사해오기',
|
|
115
|
+
copyAll: '현재 프레임 전체 적용',
|
|
116
|
+
pivotTitle: '피봇 (기준점)',
|
|
117
|
+
pivotXLabel: '피봇 X',
|
|
118
|
+
pivotYLabel: '피봇 Y',
|
|
119
|
+
exportTitle: '프로젝트 내보내기',
|
|
120
|
+
exportJson: 'JSON 다운로드',
|
|
121
|
+
importJson: 'JSON 가져오기',
|
|
122
|
+
exportContactSheet: '컨택트 시트 다운로드',
|
|
123
|
+
resetProject: '레이어 초기화',
|
|
124
|
+
undo: '실행 취소',
|
|
125
|
+
redo: '다시 실행',
|
|
126
|
+
statusReady: '작업대가 준비되었습니다.',
|
|
127
|
+
statusImageLoaded: '{count}개 이미지 파일 로드 완료.',
|
|
128
|
+
statusShapeCreated: '새 충돌 도형이 추가되었습니다.',
|
|
129
|
+
statusShapeUpdated: '충돌 도형이 수정되었습니다.',
|
|
130
|
+
statusImported: '프로젝트를 가져왔습니다.',
|
|
131
|
+
statusExported: '내보내기 파일이 준비되었습니다.',
|
|
132
|
+
statusError: '파일을 읽을 수 없습니다.',
|
|
133
|
+
framesBadge: '{count}개 프레임',
|
|
134
|
+
shapesBadge: '{count}개 도형',
|
|
135
|
+
coverageBadge: '커버리지 {percent}%',
|
|
136
|
+
coordinatesNote: '좌표는 자른 프레임의 좌측 상단을 (0,0) 원점으로 계산합니다.',
|
|
137
|
+
localOnlyDisclosure: 'JSON 파일에는 파일명, 피봇 및 충돌 기하 구조만 저장되며 픽셀 정보는 포함되지 않습니다.',
|
|
138
|
+
limitationDisclosure: '본 레이어는 디자인 영역 설정용입니다. 실제 동작은 게임 엔진에서 테스트하세요.',
|
|
139
|
+
},
|
|
140
|
+
seo: [
|
|
141
|
+
{
|
|
142
|
+
type: 'title',
|
|
143
|
+
level: 2,
|
|
144
|
+
text: '스프라이트 애니메이션 흐름에 맞춘 판정 영역 설계',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'paragraph',
|
|
148
|
+
html: '개별 프레임만 보고 판정을 설정하면 애니메이션 전체 흐름에서 충돌 영역이 어색해질 수 있습니다. 본 에디터는 스프라이트 화면, 판정 레이어, 어니언 스킨, 타임라인을 한 화면에 제공합니다.',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'title',
|
|
152
|
+
level: 2,
|
|
153
|
+
text: '게임 기능에 따른 충돌 레이어 선택',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'table',
|
|
157
|
+
headers: ['레이어', '주요 역할', '확인 요소'],
|
|
158
|
+
rows: [
|
|
159
|
+
['Hitbox', '공격이나 효과를 적용하는 영역', '의도한 공격 프레임에서만 생성되는가?'],
|
|
160
|
+
['Hurtbox', '피격 판정을 받는 영역', '캐릭터 외형을 자연스럽게 감싸고 있는가?'],
|
|
161
|
+
['Pushbox', '캐릭터 간 겹침을 방지하는 물리 영역', '화면 덜컹거림을 막기 위해 안정적인가?'],
|
|
162
|
+
['Grabbox', '잡기를 시작하는 범위 판정', '타이밍과 범위가 모션과 일치하는가?'],
|
|
163
|
+
['Sensor', '이벤트 및 상호작용 감지 영역', '레이어 이름이 명확히 지정되었는가?'],
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'title',
|
|
168
|
+
level: 2,
|
|
169
|
+
text: '프레임 좌표계 이해 및 적용',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'paragraph',
|
|
173
|
+
html: '내보낸 프로젝트는 자른 프레임의 좌상단을 (0,0) 원점으로 피셀 좌표를 저장합니다.',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'tip',
|
|
177
|
+
title: '동작의 선딜레이, 타격, 후딜레이를 연속으로 확인하세요',
|
|
178
|
+
html: '프레임 수정 후 반드시 애니메이션을 전 구간 재생하여 프레임 간 판정이 매끄럽게 이어지는지 점검하세요.',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: 'title',
|
|
182
|
+
level: 2,
|
|
183
|
+
text: '팀 협업을 위한 컨택트 시트 활용',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: 'paragraph',
|
|
187
|
+
html: 'PNG 컨택트 시트를 출력하면 전 프레임의 충돌 레이어를 한눈에 파악할 수 있어 개발팀 간 논의가 훨씬 수월해집니다.',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
faq,
|
|
191
|
+
bibliographyTitle: '충돌 판정 설계 참고 자료',
|
|
192
|
+
bibliography: bibliographyEntries,
|
|
193
|
+
howTo,
|
|
194
|
+
schemas: [softwareApplication, faqPage, howToSchema],
|
|
195
|
+
};
|
|
@@ -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-en-hurtbox-animator-sprites';
|
|
7
|
+
const title = 'Hitbox en Hurtbox Animator voor Sprites';
|
|
8
|
+
const description = 'Teken botsingslagen op elk sprite-frame, bekijk animaties met uienvellen, bewerk exacte pixelcoördinaten en exporteer een motorneutraal JSON-bestand.';
|
|
9
|
+
|
|
10
|
+
const faq = [
|
|
11
|
+
{
|
|
12
|
+
question: 'Wat is het verschil tussen een hitbox en een hurtbox?',
|
|
13
|
+
answer: 'Een hitbox markeert het aanvalsgebied, terwijl een hurtbox het kwetsbare gebied aangeeft dat de aanval ontvangt. Pushboxes houden personages op afstand en grabboxes bepalen het bereik van een worp.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: 'Verlaten mijn sprite-bestanden de browser?',
|
|
17
|
+
answer: 'Nee. Afbeeldingen worden volledig binnen uw browser verwerkt en geëxporteerd. Alleen uw bewerkingsvoorkeuren worden lokaal opgeslagen.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Welk coördinatensysteem gebruikt de JSON-export?',
|
|
21
|
+
answer: 'Elk frame meet de pixelcoördinaten vanaf de linkerbovenhoek van de uitsnede. De breedte en hoogte worden opgeslagen als positieve waarden met een eigen draaipunt.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Kan ik een spritesheet en losse frame-afbeeldingen bewerken?',
|
|
25
|
+
answer: 'Ja. U kunt een PNG- of WebP-spritesheet laden door rijen en kolommen op te geven, of meerdere geordende afbeeldingen selecteren.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
question: 'Werkt de export direct in elke game-engine?',
|
|
29
|
+
answer: 'Het JSON-formaat is neutraal. Het slaat frame-rechthoeken, draaipunten en geometrische lagen op zonder een specifieke engine af te dwingen.',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const howTo = [
|
|
34
|
+
{ name: 'Sprite-afbeeldingen laden', text: 'Selecteer een PNG/WebP-spritesheet of geordende afbeeldingen. De verwerking blijft lokaal op uw apparaat.' },
|
|
35
|
+
{ name: 'Frames instellen', text: 'Geef het aantal rijen en kolommen op en controleer de uitsnede op de animatiestrip.' },
|
|
36
|
+
{ name: 'Botsingslagen tekenen', text: 'Kies een hitbox-, hurtbox-, pushbox- of sensorlaag en teken een rechthoek of cirkel.' },
|
|
37
|
+
{ name: 'Beweging aanpassen', text: 'Bewerk exacte coördinaten, kopieer vormen naar naburige frames en gebruik uienvellen om de beweging te vergelijken.' },
|
|
38
|
+
{ name: 'Project exporteren', text: 'Download het neutrale JSON-bestand en een PNG-contactblad. Bewaar de originele afbeeldingen bij het JSON-bestand.' },
|
|
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: 'Laad een animatie, controleer de frame-uitsneden en teken de gebieden voor aanval, schade, fysieke botsing of detectie.',
|
|
72
|
+
privacyNote: 'Lokale animatietafel. Afbeeldingen worden niet geüpload.',
|
|
73
|
+
loadSprite: 'Afbeeldingen op de werkbank plaatsen',
|
|
74
|
+
loadHint: 'Kies een spritesheet of een reeks geordende PNG/WebP-afbeeldingen.',
|
|
75
|
+
chooseImages: 'Afbeeldingen kiezen',
|
|
76
|
+
slicingTitle: 'Frame-uitsnede',
|
|
77
|
+
rowsLabel: 'Rijen',
|
|
78
|
+
columnsLabel: 'Kolommen',
|
|
79
|
+
applySlicing: 'Uitsnijden',
|
|
80
|
+
playbackTitle: 'Bewegingsvoorbeeld',
|
|
81
|
+
previousFrame: 'Vorig frame',
|
|
82
|
+
play: 'Afspelen',
|
|
83
|
+
pause: 'Pauze',
|
|
84
|
+
nextFrame: 'Volgend frame',
|
|
85
|
+
fpsLabel: 'Frames per seconde',
|
|
86
|
+
onionPrevious: 'Vorig uienvel',
|
|
87
|
+
onionNext: 'Volgend uienvel',
|
|
88
|
+
layerTitle: 'Botsingslagen',
|
|
89
|
+
typeHitbox: 'Hitbox',
|
|
90
|
+
typeHurtbox: 'Hurtbox',
|
|
91
|
+
typePushbox: 'Pushbox',
|
|
92
|
+
typeGrabbox: 'Grabbox',
|
|
93
|
+
typeSensor: 'Sensor',
|
|
94
|
+
typeCustom: 'Aangepast',
|
|
95
|
+
shapeRectangle: 'Rechthoek',
|
|
96
|
+
shapeCircle: 'Cirkel',
|
|
97
|
+
drawShape: 'Tekenen',
|
|
98
|
+
selectShape: 'Selecteren',
|
|
99
|
+
stageLabel: 'Werkgebied',
|
|
100
|
+
emptyStage: 'Laad afbeeldingen om te beginnen met het tekenen van botsingslagen.',
|
|
101
|
+
frameReadout: 'Frame {current} van {total}',
|
|
102
|
+
timelineTitle: 'Animatiestrip',
|
|
103
|
+
inspectorTitle: 'Vorminspecteur',
|
|
104
|
+
noSelection: 'Selecteer een vorm om de exacte coördinaten te bewerken.',
|
|
105
|
+
nameLabel: 'Laagnaam',
|
|
106
|
+
xLabel: 'X in pixels',
|
|
107
|
+
yLabel: 'Y in pixels',
|
|
108
|
+
widthLabel: 'Breedte in pixels',
|
|
109
|
+
heightLabel: 'Hoogte in pixels',
|
|
110
|
+
radiusLabel: 'Radius in pixels',
|
|
111
|
+
duplicateShape: 'Dupliceren',
|
|
112
|
+
mirrorShape: 'Horizontaal spiegelen',
|
|
113
|
+
deleteShape: 'Vorm verwijderen',
|
|
114
|
+
copyPrevious: 'Vorig frame hierheen kopiëren',
|
|
115
|
+
copyAll: 'Dit frame naar alles kopiëren',
|
|
116
|
+
pivotTitle: 'Draaipunt',
|
|
117
|
+
pivotXLabel: 'Draaipunt X',
|
|
118
|
+
pivotYLabel: 'Draaipunt Y',
|
|
119
|
+
exportTitle: 'Project exporteren',
|
|
120
|
+
exportJson: 'JSON downloaden',
|
|
121
|
+
importJson: 'JSON importeren',
|
|
122
|
+
exportContactSheet: 'Contactblad downloaden',
|
|
123
|
+
resetProject: 'Lagen herstellen',
|
|
124
|
+
undo: 'Ongedaan maken',
|
|
125
|
+
redo: 'Opnieuw uitvoeren',
|
|
126
|
+
statusReady: 'De animatietafel is gereed.',
|
|
127
|
+
statusImageLoaded: '{count} afbeeldingsbestanden geladen.',
|
|
128
|
+
statusShapeCreated: 'Nieuwe botsingsvorm toegevoegd.',
|
|
129
|
+
statusShapeUpdated: 'Vorm bijgewerkt.',
|
|
130
|
+
statusImported: 'Project geïmporteerd.',
|
|
131
|
+
statusExported: 'Export gereed.',
|
|
132
|
+
statusError: 'Kan het gekozen bestand niet lezen.',
|
|
133
|
+
framesBadge: '{count} frames',
|
|
134
|
+
shapesBadge: '{count} vormen',
|
|
135
|
+
coverageBadge: '{percent}% gedekt',
|
|
136
|
+
coordinatesNote: 'Coördinaten gebruiken de linkerbovenhoek van elk frame als oorsprong (0,0).',
|
|
137
|
+
localOnlyDisclosure: 'Het JSON-bestand slaat afbeeldingsnamen, draaipunten en vormen op zonder de pixels te bevatten.',
|
|
138
|
+
limitationDisclosure: 'Lagen definiëren geometrische ontwerpzones. Test het gedrag direct in uw game-engine.',
|
|
139
|
+
},
|
|
140
|
+
seo: [
|
|
141
|
+
{
|
|
142
|
+
type: 'title',
|
|
143
|
+
level: 2,
|
|
144
|
+
text: 'Ontwerp hitboxes en hurtboxes afgestemd op de beweging van de sprite',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'paragraph',
|
|
148
|
+
html: 'Botsingen instellen wordt lastig wanneer elk frame afzonderlijk wordt beoordeeld. Deze editor brengt de afbeelding, botsingslagen, uienvellen en de tijdlijn samen voor een vloeiende beweging.',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'title',
|
|
152
|
+
level: 2,
|
|
153
|
+
text: 'Kies elke botsingslaag op basis van de spelfunctie',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'table',
|
|
157
|
+
headers: ['Laag', 'Hoofdfunctie', 'Controlepunt'],
|
|
158
|
+
rows: [
|
|
159
|
+
['Hitbox', 'Gebied dat een aanval of effect veroorzaakt', 'Verschijnt deze alleen tijdens de actieve frames?'],
|
|
160
|
+
['Hurtbox', 'Gebied dat schade of aanvallen ontvangt', 'Volgt deze het personage zonder onnatuurlijke gaten?'],
|
|
161
|
+
['Pushbox', 'Fysieke botsingslaag tussen personages', 'Blijft deze stabiel om schokken te voorkomen?'],
|
|
162
|
+
['Grabbox', 'Bereik voor het starten van een worp', 'Komt de timing overeen met de visuele animatie?'],
|
|
163
|
+
['Sensor', 'Detectiegebied voor interacties', 'Is de naam duidelijk genoeg gedefinieerd?'],
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'title',
|
|
168
|
+
level: 2,
|
|
169
|
+
text: 'Het coördinatensysteem begrijpen en toepassen',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'paragraph',
|
|
173
|
+
html: 'Het geëxporteerde project meet X en Y vanaf de linkerbovenhoek van elk uitgesneden frame. De afmetingen zijn positieve waarden in pixels.',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'tip',
|
|
177
|
+
title: 'Controleer de gehele animatiereeks',
|
|
178
|
+
html: 'Speel de volledige animatie af na het aanpassen van een frame om een vloeiende overgang te waarborgen.',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: 'title',
|
|
182
|
+
level: 2,
|
|
183
|
+
text: 'Gebruik het contactblad voor overleg in het team',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: 'paragraph',
|
|
187
|
+
html: 'Het PNG-contactblad toont alle frames en hun kleurlagen op één afbeelding, ideaal voor overleg tussen ontwikkelaars en vormgevers.',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
faq,
|
|
191
|
+
bibliographyTitle: 'Referenties voor botsingsontwerp',
|
|
192
|
+
bibliography: bibliographyEntries,
|
|
193
|
+
howTo,
|
|
194
|
+
schemas: [softwareApplication, faqPage, howToSchema],
|
|
195
|
+
};
|