@jjlmoya/utils-games-development 1.64.0 → 1.65.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.
Files changed (161) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +4 -1
  4. package/src/tests/locale_completeness.test.ts +2 -2
  5. package/src/tests/seo_parity.test.ts +2 -2
  6. package/src/tests/seo_translation_completeness.test.ts +69 -0
  7. package/src/tests/tool_validation.test.ts +2 -2
  8. package/src/tests/translation_copy.test.ts +21 -18
  9. package/src/tool/audioLoopPointFinder/i18n/de.ts +13 -0
  10. package/src/tool/audioLoopPointFinder/i18n/fr.ts +26 -0
  11. package/src/tool/audioLoopPointFinder/i18n/id.ts +4 -0
  12. package/src/tool/audioLoopPointFinder/i18n/it.ts +3 -0
  13. package/src/tool/audioLoopPointFinder/i18n/nl.ts +4 -0
  14. package/src/tool/audioLoopPointFinder/i18n/pl.ts +4 -0
  15. package/src/tool/audioLoopPointFinder/i18n/pt.ts +3 -0
  16. package/src/tool/audioLoopPointFinder/i18n/ru.ts +4 -0
  17. package/src/tool/audioLoopPointFinder/i18n/sv.ts +4 -0
  18. package/src/tool/audioLoopPointFinder/i18n/tr.ts +4 -0
  19. package/src/tool/audioLoopPointFinder/i18n/zh.ts +4 -0
  20. package/src/tool/damageFormulaLab/i18n/de.ts +13 -0
  21. package/src/tool/damageFormulaLab/i18n/fr.ts +13 -0
  22. package/src/tool/damageFormulaLab/i18n/id.ts +13 -0
  23. package/src/tool/damageFormulaLab/i18n/it.ts +1 -0
  24. package/src/tool/damageFormulaLab/i18n/ja.ts +1 -0
  25. package/src/tool/damageFormulaLab/i18n/ko.ts +1 -0
  26. package/src/tool/damageFormulaLab/i18n/nl.ts +1 -0
  27. package/src/tool/damageFormulaLab/i18n/pl.ts +1 -0
  28. package/src/tool/damageFormulaLab/i18n/pt.ts +1 -0
  29. package/src/tool/damageFormulaLab/i18n/ru.ts +2 -0
  30. package/src/tool/damageFormulaLab/i18n/sv.ts +2 -0
  31. package/src/tool/damageFormulaLab/i18n/tr.ts +2 -0
  32. package/src/tool/damageFormulaLab/i18n/zh.ts +2 -0
  33. package/src/tool/gameDeltaTimeFixedTimestepLab/bibliography.astro +22 -0
  34. package/src/tool/gameDeltaTimeFixedTimestepLab/bibliography.ts +10 -0
  35. package/src/tool/gameDeltaTimeFixedTimestepLab/component.astro +51 -0
  36. package/src/tool/gameDeltaTimeFixedTimestepLab/controller.ts +79 -0
  37. package/src/tool/gameDeltaTimeFixedTimestepLab/dom-views.ts +88 -0
  38. package/src/tool/gameDeltaTimeFixedTimestepLab/entry.ts +27 -0
  39. package/src/tool/gameDeltaTimeFixedTimestepLab/evaluator.ts +21 -0
  40. package/src/tool/gameDeltaTimeFixedTimestepLab/game-delta-time-fixed-timestep-lab.css +491 -0
  41. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/de.ts +56 -0
  42. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/en.ts +180 -0
  43. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/es.ts +56 -0
  44. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/fr.ts +46 -0
  45. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/id.ts +46 -0
  46. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/it.ts +46 -0
  47. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/ja.ts +46 -0
  48. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/ko.ts +46 -0
  49. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/nl.ts +46 -0
  50. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/pl.ts +46 -0
  51. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/pt.ts +46 -0
  52. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/ru.ts +46 -0
  53. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/sv.ts +46 -0
  54. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/tr.ts +46 -0
  55. package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/zh.ts +46 -0
  56. package/src/tool/gameDeltaTimeFixedTimestepLab/index.ts +11 -0
  57. package/src/tool/gameDeltaTimeFixedTimestepLab/logic.test.ts +35 -0
  58. package/src/tool/gameDeltaTimeFixedTimestepLab/logic.ts +140 -0
  59. package/src/tool/gameDeltaTimeFixedTimestepLab/seo.astro +15 -0
  60. package/src/tool/gameDeltaTimeFixedTimestepLab/seo_contract.test.ts +25 -0
  61. package/src/tool/gameDeltaTimeFixedTimestepLab/storage.ts +23 -0
  62. package/src/tool/gameDeltaTimeFixedTimestepLab/ui.ts +54 -0
  63. package/src/tool/gameUIAccessibilityTester/i18n/de.ts +13 -0
  64. package/src/tool/gameUIAccessibilityTester/i18n/fr.ts +13 -0
  65. package/src/tool/gameUIAccessibilityTester/i18n/id.ts +13 -0
  66. package/src/tool/gameUIAccessibilityTester/i18n/it.ts +1 -0
  67. package/src/tool/gameUIAccessibilityTester/i18n/nl.ts +5 -0
  68. package/src/tool/gameUIAccessibilityTester/i18n/pl.ts +5 -0
  69. package/src/tool/gameUIAccessibilityTester/i18n/pt.ts +2 -0
  70. package/src/tool/gameUIAccessibilityTester/i18n/ru.ts +1 -0
  71. package/src/tool/gameUIAccessibilityTester/i18n/sv.ts +1 -0
  72. package/src/tool/gameUIAccessibilityTester/i18n/tr.ts +2 -0
  73. package/src/tool/gameUIAccessibilityTester/i18n/zh.ts +1 -0
  74. package/src/tool/hitboxHurtboxAnimator/i18n/de.ts +14 -0
  75. package/src/tool/hitboxHurtboxAnimator/i18n/fr.ts +13 -0
  76. package/src/tool/hitboxHurtboxAnimator/i18n/id.ts +13 -0
  77. package/src/tool/hitboxHurtboxAnimator/i18n/it.ts +1 -0
  78. package/src/tool/hitboxHurtboxAnimator/i18n/nl.ts +1 -0
  79. package/src/tool/hitboxHurtboxAnimator/i18n/pl.ts +2 -0
  80. package/src/tool/hitboxHurtboxAnimator/i18n/pt.ts +2 -0
  81. package/src/tool/hitboxHurtboxAnimator/i18n/ru.ts +2 -0
  82. package/src/tool/hitboxHurtboxAnimator/i18n/sv.ts +1 -0
  83. package/src/tool/hitboxHurtboxAnimator/i18n/tr.ts +2 -0
  84. package/src/tool/itchioGameTester/i18n/de.ts +13 -0
  85. package/src/tool/itchioGameTester/i18n/es.ts +2 -2
  86. package/src/tool/itchioGameTester/i18n/fr.ts +26 -0
  87. package/src/tool/itchioGameTester/i18n/id.ts +3 -0
  88. package/src/tool/itchioGameTester/i18n/it.ts +3 -0
  89. package/src/tool/itchioGameTester/i18n/ja.ts +3 -0
  90. package/src/tool/itchioGameTester/i18n/ko.ts +3 -0
  91. package/src/tool/itchioGameTester/i18n/nl.ts +3 -0
  92. package/src/tool/itchioGameTester/i18n/pl.ts +3 -0
  93. package/src/tool/itchioGameTester/i18n/pt.ts +3 -0
  94. package/src/tool/itchioGameTester/i18n/ru.ts +3 -0
  95. package/src/tool/itchioGameTester/i18n/sv.ts +3 -0
  96. package/src/tool/itchioGameTester/i18n/tr.ts +3 -0
  97. package/src/tool/itchioGameTester/i18n/zh.ts +3 -0
  98. package/src/tool/localizationSanitizer/i18n/de.ts +13 -0
  99. package/src/tool/localizationSanitizer/i18n/es.ts +26 -0
  100. package/src/tool/localizationSanitizer/i18n/fr.ts +4 -0
  101. package/src/tool/localizationSanitizer/i18n/id.ts +4 -0
  102. package/src/tool/localizationSanitizer/i18n/it.ts +4 -0
  103. package/src/tool/localizationSanitizer/i18n/ja.ts +4 -0
  104. package/src/tool/localizationSanitizer/i18n/ko.ts +4 -0
  105. package/src/tool/localizationSanitizer/i18n/nl.ts +5 -0
  106. package/src/tool/localizationSanitizer/i18n/pl.ts +5 -0
  107. package/src/tool/localizationSanitizer/i18n/pt.ts +5 -0
  108. package/src/tool/localizationSanitizer/i18n/ru.ts +5 -0
  109. package/src/tool/localizationSanitizer/i18n/sv.ts +5 -0
  110. package/src/tool/localizationSanitizer/i18n/tr.ts +5 -0
  111. package/src/tool/localizationSanitizer/i18n/zh.ts +6 -0
  112. package/src/tool/pixelArtPaletteSwapper/i18n/ja.ts +13 -0
  113. package/src/tool/pixelArtPaletteSwapper/i18n/ko.ts +13 -0
  114. package/src/tool/pixelArtPaletteSwapper/i18n/zh.ts +18 -0
  115. package/src/tool/retroSfxGenerator/i18n/de.ts +13 -0
  116. package/src/tool/retroSfxGenerator/i18n/fr.ts +13 -0
  117. package/src/tool/retroSfxGenerator/i18n/id.ts +13 -0
  118. package/src/tool/retroSfxGenerator/i18n/it.ts +1 -0
  119. package/src/tool/retroSfxGenerator/i18n/nl.ts +1 -0
  120. package/src/tool/retroSfxGenerator/i18n/pl.ts +1 -0
  121. package/src/tool/retroSfxGenerator/i18n/pt.ts +1 -0
  122. package/src/tool/retroSfxGenerator/i18n/ru.ts +1 -0
  123. package/src/tool/retroSfxGenerator/i18n/sv.ts +2 -0
  124. package/src/tool/retroSfxGenerator/i18n/tr.ts +1 -0
  125. package/src/tool/retroSfxGenerator/i18n/zh.ts +1 -0
  126. package/src/tool/spriteSheetPacker/i18n/de.ts +13 -0
  127. package/src/tool/spriteSheetPacker/i18n/es.ts +2 -2
  128. package/src/tool/spriteSheetPacker/i18n/fr.ts +26 -0
  129. package/src/tool/spriteSheetPacker/i18n/id.ts +4 -0
  130. package/src/tool/spriteSheetPacker/i18n/it.ts +4 -0
  131. package/src/tool/spriteSheetPacker/i18n/ja.ts +3 -0
  132. package/src/tool/spriteSheetPacker/i18n/ko.ts +3 -0
  133. package/src/tool/spriteSheetPacker/i18n/nl.ts +4 -0
  134. package/src/tool/spriteSheetPacker/i18n/pl.ts +4 -0
  135. package/src/tool/spriteSheetPacker/i18n/pt.ts +4 -0
  136. package/src/tool/spriteSheetPacker/i18n/ru.ts +4 -0
  137. package/src/tool/spriteSheetPacker/i18n/sv.ts +4 -0
  138. package/src/tool/spriteSheetPacker/i18n/tr.ts +4 -0
  139. package/src/tool/spriteSheetPacker/i18n/zh.ts +4 -0
  140. package/src/tool/steamBbcodeTranslator/i18n/de.ts +22 -0
  141. package/src/tool/steamBbcodeTranslator/i18n/es.ts +23 -1
  142. package/src/tool/steamBbcodeTranslator/i18n/fr.ts +6 -1
  143. package/src/tool/steamBbcodeTranslator/i18n/id.ts +7 -1
  144. package/src/tool/steamBbcodeTranslator/i18n/it.ts +6 -1
  145. package/src/tool/steamBbcodeTranslator/i18n/ja.ts +5 -1
  146. package/src/tool/steamBbcodeTranslator/i18n/ko.ts +5 -1
  147. package/src/tool/steamBbcodeTranslator/i18n/nl.ts +7 -1
  148. package/src/tool/steamBbcodeTranslator/i18n/pl.ts +7 -1
  149. package/src/tool/steamBbcodeTranslator/i18n/pt.ts +7 -1
  150. package/src/tool/steamBbcodeTranslator/i18n/ru.ts +7 -1
  151. package/src/tool/steamBbcodeTranslator/i18n/sv.ts +7 -1
  152. package/src/tool/steamBbcodeTranslator/i18n/tr.ts +7 -1
  153. package/src/tool/steamBbcodeTranslator/i18n/zh.ts +5 -1
  154. package/src/tool/steamCapsuleGenerator/i18n/de.ts +2 -2
  155. package/src/tool/steamCapsuleGenerator/i18n/es.ts +2 -2
  156. package/src/tool/steamCapsuleGenerator/i18n/fr.ts +2 -2
  157. package/src/tool/steamCapsuleGenerator/i18n/pl.ts +13 -0
  158. package/src/tool/steamCapsuleGenerator/i18n/ru.ts +26 -0
  159. package/src/tool/steamCapsuleGenerator/i18n/sv.ts +1 -0
  160. package/src/tool/steamCapsuleGenerator/i18n/tr.ts +1 -0
  161. package/src/tools.ts +2 -0
@@ -0,0 +1,491 @@
1
+ .game-loop-lab {
2
+ --n-ink: #19251f;
3
+ --n-muted: #52645a;
4
+ --n-paper: #f8f5ec;
5
+ --n-panel: #fffdf7;
6
+ --n-line: #d9dfd2;
7
+ --n-variable: #cc5c3e;
8
+ --n-fixed: #276c70;
9
+ --n-accent: #c58a32;
10
+ --n-success: #2f754d;
11
+ --n-warning: #9e4c26;
12
+ --n-primary: #276c70;
13
+
14
+ color: var(--n-ink);
15
+ background-color: var(--n-paper);
16
+ border: 1px solid var(--n-line);
17
+ border-radius: 1.25rem;
18
+ overflow: hidden;
19
+ }
20
+
21
+ .theme-dark .game-loop-lab {
22
+ --n-ink: #f2f0e7;
23
+ --n-muted: #b5c3b8;
24
+ --n-paper: #202622;
25
+ --n-panel: #29312b;
26
+ --n-line: #4a574d;
27
+ --n-variable: #f08a63;
28
+ --n-fixed: #78c8c0;
29
+ --n-accent: #e2b75d;
30
+ --n-success: #8ed39c;
31
+ --n-warning: #f4a274;
32
+ --n-primary: #78c8c0;
33
+ }
34
+
35
+ .gll-stage-heading p,
36
+ .gll-timeline-heading p,
37
+ .gll-model-note,
38
+ .gll-privacy {
39
+ color: var(--n-muted);
40
+ line-height: 1.6;
41
+ }
42
+
43
+ .gll-kicker {
44
+ margin: 0 0 .35rem;
45
+ color: var(--n-accent);
46
+ font-size: .76rem;
47
+ font-weight: 800;
48
+ letter-spacing: .12em;
49
+ text-transform: uppercase;
50
+ }
51
+
52
+ .gll-shell {
53
+ display: grid;
54
+ grid-template-columns: minmax(15rem, 20rem) minmax(0, 1fr);
55
+ gap: 1px;
56
+ background-color: var(--n-line);
57
+ }
58
+
59
+ .gll-controls,
60
+ .gll-stage {
61
+ background-color: var(--n-panel);
62
+ }
63
+
64
+ .gll-controls {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: .8rem;
68
+ padding: 1.35rem;
69
+ }
70
+
71
+ .gll-control-heading {
72
+ padding-bottom: .35rem;
73
+ border-bottom: 1px solid var(--n-line);
74
+ }
75
+
76
+ .gll-range-field,
77
+ .gll-number-field {
78
+ display: grid;
79
+ grid-template-columns: 1fr auto;
80
+ align-items: center;
81
+ gap: .45rem .65rem;
82
+ }
83
+
84
+ .gll-range-field span,
85
+ .gll-number-field span {
86
+ font-size: .88rem;
87
+ font-weight: 700;
88
+ }
89
+
90
+ .gll-range-field output {
91
+ min-width: 4.7rem;
92
+ padding: .28rem .45rem;
93
+ border: 1px solid var(--n-line);
94
+ border-radius: .4rem;
95
+ color: var(--n-fixed);
96
+ text-align: right;
97
+ font-variant-numeric: tabular-nums;
98
+ }
99
+
100
+ .gll-range-field input[type='range'] {
101
+ grid-column: 1 / -1;
102
+ width: 100%;
103
+ accent-color: var(--n-fixed);
104
+ }
105
+
106
+ .gll-number-field input {
107
+ width: 5.5rem;
108
+ padding: .38rem .45rem;
109
+ border: 1px solid var(--n-line);
110
+ border-radius: .4rem;
111
+ background-color: var(--n-paper);
112
+ color: var(--n-ink);
113
+ font: inherit;
114
+ text-align: right;
115
+ }
116
+
117
+ .gll-field-hint {
118
+ margin: -.35rem 0 .1rem;
119
+ color: var(--n-muted);
120
+ font-size: .75rem;
121
+ line-height: 1.4;
122
+ }
123
+
124
+ .gll-clamp {
125
+ padding-top: .8rem;
126
+ border-top: 1px solid var(--n-line);
127
+ }
128
+
129
+ .gll-check-field {
130
+ display: flex;
131
+ align-items: center;
132
+ gap: .55rem;
133
+ font-size: .88rem;
134
+ font-weight: 700;
135
+ }
136
+
137
+ .gll-check-field input {
138
+ width: 1rem;
139
+ height: 1rem;
140
+ accent-color: var(--n-fixed);
141
+ }
142
+
143
+ .gll-clamp[data-disabled='true'] {
144
+ opacity: .55;
145
+ }
146
+
147
+ .gll-actions {
148
+ display: flex;
149
+ gap: .6rem;
150
+ padding-top: .5rem;
151
+ }
152
+
153
+ .gll-actions button {
154
+ flex: 1;
155
+ min-height: 2.5rem;
156
+ padding: .5rem .7rem;
157
+ border-radius: .5rem;
158
+ font: inherit;
159
+ font-size: .84rem;
160
+ font-weight: 800;
161
+ cursor: pointer;
162
+ }
163
+
164
+ .gll-primary {
165
+ border: 1px solid var(--n-fixed);
166
+ background-color: var(--n-fixed);
167
+ color: var(--n-panel);
168
+ }
169
+
170
+ .gll-secondary {
171
+ border: 1px solid var(--n-line);
172
+ background-color: var(--n-paper);
173
+ color: var(--n-ink);
174
+ }
175
+
176
+ .gll-actions button:focus-visible,
177
+ .gll-range-field input:focus-visible,
178
+ .gll-number-field input:focus-visible,
179
+ .gll-check-field input:focus-visible {
180
+ outline: 3px solid var(--n-accent);
181
+ outline-offset: 2px;
182
+ }
183
+
184
+ .gll-stage {
185
+ min-width: 0;
186
+ padding: 1.35rem;
187
+ }
188
+
189
+ .gll-stage-heading {
190
+ display: flex;
191
+ justify-content: space-between;
192
+ gap: 1rem;
193
+ align-items: start;
194
+ }
195
+
196
+ .gll-stage-heading h2 {
197
+ margin: 0;
198
+ font-size: clamp(1.25rem, 2.5vw, 1.75rem);
199
+ }
200
+
201
+ .gll-stage-heading p {
202
+ margin: .35rem 0 0;
203
+ font-size: .9rem;
204
+ }
205
+
206
+ .gll-live-badge {
207
+ display: inline-flex;
208
+ padding: .35rem .55rem;
209
+ border: 1px solid var(--n-line);
210
+ border-radius: 99rem;
211
+ color: var(--n-success);
212
+ font-size: .76rem;
213
+ font-weight: 800;
214
+ white-space: nowrap;
215
+ }
216
+
217
+ .gll-stage-visual {
218
+ margin-top: 1.1rem;
219
+ border: 1px solid var(--n-line);
220
+ border-radius: .95rem;
221
+ overflow: hidden;
222
+ }
223
+
224
+ .gll-stage-svg,
225
+ .gll-chart-svg {
226
+ display: block;
227
+ width: 100%;
228
+ height: auto;
229
+ }
230
+
231
+ .gll-paper {
232
+ fill: var(--n-paper);
233
+ }
234
+
235
+ .gll-base-line,
236
+ .gll-grid-line,
237
+ .gll-zero-line {
238
+ stroke: var(--n-line);
239
+ stroke-width: 1.5;
240
+ }
241
+
242
+ .gll-frame-bar {
243
+ fill: var(--n-line);
244
+ }
245
+
246
+ .gll-spike-bar {
247
+ fill: var(--n-variable);
248
+ }
249
+
250
+ .gll-plot-title,
251
+ .gll-plot-label,
252
+ .gll-plot-axis {
253
+ fill: var(--n-muted);
254
+ font-size: 11px;
255
+ }
256
+
257
+ .gll-plot-title,
258
+ .gll-plot-label {
259
+ font-weight: 800;
260
+ }
261
+
262
+ .gll-difference-line {
263
+ fill: none;
264
+ stroke: var(--n-accent);
265
+ stroke-width: 3;
266
+ stroke-linecap: round;
267
+ stroke-linejoin: round;
268
+ }
269
+
270
+ .gll-divider,
271
+ .gll-track {
272
+ fill: none;
273
+ stroke: var(--n-line);
274
+ stroke-width: 1.5;
275
+ }
276
+
277
+ .gll-variable-token {
278
+ fill: var(--n-variable);
279
+ stroke: var(--n-paper);
280
+ stroke-width: 4;
281
+ }
282
+
283
+ .gll-fixed-token {
284
+ fill: var(--n-fixed);
285
+ stroke: var(--n-paper);
286
+ stroke-width: 4;
287
+ }
288
+
289
+ .gll-lane-label,
290
+ .gll-marker-value,
291
+ .gll-stage-axis,
292
+ .gll-chart-label {
293
+ fill: var(--n-muted);
294
+ font-size: 11px;
295
+ }
296
+
297
+ .gll-marker-value {
298
+ fill: var(--n-ink);
299
+ font-weight: 800;
300
+ }
301
+
302
+ .gll-summary-grid {
303
+ display: grid;
304
+ grid-template-columns: repeat(3, minmax(0, 1fr));
305
+ gap: .65rem;
306
+ margin-top: 1rem;
307
+ }
308
+
309
+ .gll-summary-grid div {
310
+ padding: .72rem;
311
+ border-top: 2px solid var(--n-line);
312
+ }
313
+
314
+ .gll-summary-grid span {
315
+ display: block;
316
+ color: var(--n-muted);
317
+ font-size: .75rem;
318
+ line-height: 1.3;
319
+ }
320
+
321
+ .gll-summary-grid strong {
322
+ display: block;
323
+ margin-top: .25rem;
324
+ font-size: 1rem;
325
+ font-variant-numeric: tabular-nums;
326
+ }
327
+
328
+ .gll-diagnostic {
329
+ margin: .9rem 0 0;
330
+ padding: .65rem .8rem;
331
+ border-left: 3px solid var(--n-success);
332
+ background-color: var(--n-paper);
333
+ color: var(--n-ink);
334
+ font-size: .84rem;
335
+ line-height: 1.45;
336
+ }
337
+
338
+ .gll-diagnostic-notice {
339
+ border-color: var(--n-accent);
340
+ }
341
+
342
+ .gll-diagnostic-warning {
343
+ border-color: var(--n-warning);
344
+ }
345
+
346
+ .gll-timeline-heading {
347
+ margin-top: 1.35rem;
348
+ }
349
+
350
+ .gll-timeline-heading h3 {
351
+ margin: 0;
352
+ font-size: 1.05rem;
353
+ }
354
+
355
+ .gll-timeline-heading p {
356
+ margin: .3rem 0 .6rem;
357
+ font-size: .84rem;
358
+ }
359
+
360
+ .gll-chart-axis {
361
+ stroke: var(--n-line);
362
+ stroke-width: 1.5;
363
+ }
364
+
365
+ .gll-variable-line,
366
+ .gll-fixed-line {
367
+ fill: none;
368
+ stroke-width: 3;
369
+ stroke-linecap: round;
370
+ stroke-linejoin: round;
371
+ }
372
+
373
+ .gll-variable-line {
374
+ stroke: var(--n-variable);
375
+ }
376
+
377
+ .gll-fixed-line {
378
+ stroke: var(--n-fixed);
379
+ stroke-dasharray: 7 5;
380
+ }
381
+
382
+ .gll-legend {
383
+ display: flex;
384
+ flex-wrap: wrap;
385
+ gap: 1rem;
386
+ color: var(--n-muted);
387
+ font-size: .78rem;
388
+ }
389
+
390
+ .gll-legend span {
391
+ display: inline-flex;
392
+ align-items: center;
393
+ gap: .4rem;
394
+ }
395
+
396
+ .gll-legend-dot {
397
+ width: .65rem;
398
+ height: .65rem;
399
+ border-radius: 50%;
400
+ }
401
+
402
+ .gll-variable-dot {
403
+ background-color: var(--n-variable);
404
+ }
405
+
406
+ .gll-fixed-dot {
407
+ background-color: var(--n-fixed);
408
+ }
409
+
410
+ .gll-table {
411
+ margin-top: 1.1rem;
412
+ overflow-x: auto;
413
+ }
414
+
415
+ .gll-table h3 {
416
+ margin: 0 0 .55rem;
417
+ font-size: .95rem;
418
+ }
419
+
420
+ .gll-table table {
421
+ width: 100%;
422
+ border-collapse: collapse;
423
+ font-size: .76rem;
424
+ }
425
+
426
+ .gll-table th,
427
+ .gll-table td {
428
+ padding: .48rem .55rem;
429
+ border-bottom: 1px solid var(--n-line);
430
+ text-align: right;
431
+ white-space: nowrap;
432
+ }
433
+
434
+ .gll-table th {
435
+ color: var(--n-muted);
436
+ font-size: .75rem;
437
+ text-align: right;
438
+ }
439
+
440
+ .gll-table th:first-child,
441
+ .gll-table td:first-child {
442
+ text-align: left;
443
+ }
444
+
445
+ .gll-model-note,
446
+ .gll-privacy {
447
+ margin: 1rem 0 0;
448
+ font-size: .78rem;
449
+ }
450
+
451
+ .gll-privacy {
452
+ padding-top: .8rem;
453
+ border-top: 1px solid var(--n-line);
454
+ }
455
+
456
+ @media (max-width: 760px) {
457
+ .gll-shell {
458
+ grid-template-columns: 1fr;
459
+ }
460
+
461
+ .gll-controls {
462
+ border-bottom: 1px solid var(--n-line);
463
+ }
464
+
465
+ .gll-summary-grid {
466
+ grid-template-columns: repeat(2, minmax(0, 1fr));
467
+ }
468
+ }
469
+
470
+ @media (max-width: 480px) {
471
+ .gll-controls,
472
+ .gll-stage {
473
+ padding: 1rem;
474
+ }
475
+
476
+ .gll-stage-heading {
477
+ display: block;
478
+ }
479
+
480
+ .gll-live-badge {
481
+ margin-top: .75rem;
482
+ }
483
+
484
+ .gll-summary-grid {
485
+ gap: .45rem;
486
+ }
487
+
488
+ .gll-summary-grid div {
489
+ padding: .55rem;
490
+ }
491
+ }
@@ -0,0 +1,56 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { GameDeltaTimeFixedTimestepLabUI } from '../ui';
4
+ import { bibliographyEntries } from '../bibliography';
5
+
6
+ const faq = [
7
+ { question: 'Was zeigt dieses Labor für feste Zeitschritte?', answer: 'Es führt dasselbe bewegte Objekt durch eine Schleife mit variablem Delta und durch einen Akkumulator mit festem Zeitschritt. Wiederholbare Frame-Spitzen machen Simulationszeit, Positionsdrift und Nachholschritte sichtbar, ohne eine Game-Engine auszuführen.' },
8
+ { question: 'Was ist der Unterschied zwischen variablem und festem Delta?', answer: 'Das variable Modell aktualisiert einmal pro gerendertem Frame und verwendet dessen Dauer. Das feste Modell arbeitet in gleich großen Simulationsschritten und verarbeitet per Akkumulator so viele Schritte, wie die vergangene Echtzeit verlangt. Dadurch hängt das feste Modell weniger von der Bildrate ab, kann nach einem langen Frame aber zusätzliche Arbeit benötigen.' },
9
+ { question: 'Was bedeutet die Eingabe für eine Frame-Spitze?', answer: 'Sie addiert zusätzliche Millisekunden zu jedem ausgewählten Spitzen-Frame. Mit dem Abstand legst du fest, wie oft die Spitze erscheint. So entsteht ein wiederholbares Stottermuster statt einer zufälligen Messung.' },
10
+ { question: 'Was ändert die Delta-Begrenzung?', answer: 'Die Begrenzung beschränkt die Dauer, die das variable Modell bei einem langen Frame verwendet. Das kann einen zu großen Sprung verhindern, lässt diese Simulationsuhr aber hinter der Echtzeit zurückfallen. Der feste Akkumulator berücksichtigt weiterhin die gesamte Frame-Dauer.' },
11
+ { question: 'Ist dies ein Profiler oder ein Ersatz für Spieltests?', answer: 'Nein. Es ist ein deterministisches Lehr- und Designlabor mit vorgegebenen Zahlen. Es misst kein Gerät, diagnostiziert keine Engine, modelliert keine Renderkosten und beweist nicht, dass eine Schleife für jedes Spiel richtig ist.' },
12
+ ];
13
+
14
+ const howTo = [
15
+ { name: 'Den Frame-Rhythmus wählen', text: 'Lege eine Zielbildrate fest und füge eine wiederholbare Spitze in Millisekunden hinzu. Mit dem Abstand bestimmst du, in welchem Frame sie erscheint.' },
16
+ { name: 'Den Simulationsschritt festlegen', text: 'Wähle die Dauer des festen Zeitschritts. Ein Wert nahe 16,667 Millisekunden entspricht ungefähr 60 Simulationsupdates pro Sekunde.' },
17
+ { name: 'Beiden Welten dieselbe Bewegung geben', text: 'Setze Geschwindigkeit und Laufzeit. Beide Modelle bewegen dasselbe Objekt, daher entsteht der Positionsunterschied durch den Umgang mit der verstrichenen Zeit.' },
18
+ { name: 'Eine Begrenzung bewusst testen', text: 'Aktiviere die Delta-Begrenzung und vergleiche die Simulationszeit des variablen Modells mit dem festen Akkumulator. Eine Begrenzung ist eine Entscheidung mit Kosten, keine kostenlose Korrektur.' },
19
+ { name: 'Die Hinweise lesen', text: 'Prüfe die beiden Spuren, die Drift-Zusammenfassung, die Zeitleiste, das Nachholverhalten und die zugängliche Frame-Tabelle. Ändere beim Erklären oder Debuggen jeweils nur eine Eingabe.' },
20
+ ];
21
+
22
+ const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Labor für Game Delta-Zeit und feste Zeitschritte', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
23
+ const faqPage: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
24
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Variable Delta-Zeit mit einem festen Zeitschritt vergleichen', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
25
+
26
+ export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
27
+ slug: 'delta-zeit-und-fester-timestep-spielelabor',
28
+ title: 'Game Delta Zeit und festes Zeitschritt Labor',
29
+ description: 'Vergleiche variable und feste Game-Loops mit wiederholbaren Frame-Spitzen, Bewegung, Nachholschritten und sichtbarer Zeitdrift.',
30
+ ui: {
31
+ controlsTitle: 'Frame-Muster erstellen', fpsLabel: 'Zielbildrate', fpsHint: 'Gerenderte Frames pro Sekunde', spikeLabel: 'Frame-Spitze', spikeHint: 'Zusätzliche Zeit für Spitzen-Frames', spikeEveryLabel: 'Spitze alle Frames', fixedDtLabel: 'Fester Zeitschritt', fixedDtHint: 'Gleich große Simulationsschritte', velocityLabel: 'Objektgeschwindigkeit', durationLabel: 'Laufzeit', clampLabel: 'Delta-Obergrenze', clampToggle: 'Variables Delta begrenzen', runLabel: 'Experiment starten', resetLabel: 'Werte zurücksetzen', stageKicker: 'Das Versuchsfeld', stageTitle: 'Zwei Uhren, ein bewegtes Objekt', stageCaption: 'Balken zeigen die Ruckler, Linien verfolgen beide Positionen und die untere Spur isoliert die Drift, die Endwerte verbergen können.', frameTraceLabel: 'Frame-Dauern', positionPlotLabel: 'Objektposition', differencePlotLabel: 'Positionsunterschied zum festen Pfad', variableLane: 'Variables Delta', fixedLane: 'Fester Akkumulator', frameCountLabel: 'Gerenderte Frames', wallTimeLabel: 'Echtzeit', variableTimeLabel: 'Variable Simulationszeit', fixedTimeLabel: 'Feste Simulationszeit', divergenceLabel: 'Positionsunterschied', stepsLabel: 'Feste Simulationsschritte', diagnosticsTitle: 'Frame-Lesung', stableStatus: 'Die beiden Pfade bleiben bei diesem Muster ausgerichtet.', variableStatus: 'Der variable Pfad läuft voraus, weil seine Frame-Dauern die Spitzen enthalten.', fixedStatus: 'Der feste Pfad holt nach einem langen Frame mit mehreren Simulationsschritten auf.', clampStatus: 'Die Begrenzung verkleinert den Sprung des variablen Pfads und lässt ihn hinter der verstrichenen Echtzeit zurück.', timelineTitle: 'Das Bewegungsprotokoll', timelineCaption: 'Position über der verstrichenen Echtzeit. Eine gestrichelte Linie markiert gleiche feste Schritte.', frameAxis: 'Verstrichene Echtzeit', positionAxis: 'Objektposition', legendVariable: 'Variabler Delta-Pfad', legendFixed: 'Pfad mit festem Zeitschritt', legendSpike: 'Frame-Spitze', tableTitle: 'Zugängliches Frame-Protokoll', tableFrame: 'Frame', tableWall: 'Echtzeit', tableVariable: 'Variable Position', tableFixed: 'Feste Position', tableDelta: 'Unterschied', modelNote: 'Das variable Modell verwendet die aktuelle Frame-Dauer einmal mit der Geschwindigkeit. Das feste Modell sammelt die gesamte Echtzeit und bewegt sich in gleichen Schritten. Kein Pfad ist eine Leistungsmessung.', privacyDisclosure: 'Deine Werte werden nur in diesem Browser gespeichert, damit das Experiment bei der Rückkehr bereitsteht. Es werden keine Spieldaten oder Telemetrie gesendet.', unitMs: 'ms', unitSeconds: 's', unitPixels: 'Einheiten', statusReady: 'Experiment aktualisiert',
32
+ },
33
+ seo: [
34
+ { type: 'title', level: 2, text: 'Die Uhr im Game-Loop verstehen' },
35
+ { type: 'paragraph', html: 'Jeder gerenderte Frame liefert dem Spiel eine neue verstrichene Zeit. In einer Schleife mit variablem Delta wird Bewegung oft als <code>position += velocity × frameTime</code> aktualisiert. Bei gleichmäßigen Frames bleibt die Durchschnittsgeschwindigkeit passend, aber ein einzelnes Update wird so groß wie der Frame, der es ausgelöst hat. Ein Hitch ist deshalb nicht nur eine kurze Bildpause: Er verändert die Spielzeit dieses Updates.' },
36
+ { type: 'title', level: 2, text: 'Variable Integration mit einem festen Akkumulator vergleichen' },
37
+ { type: 'paragraph', html: 'Eine Schleife mit festem Zeitschritt addiert jede Frame-Dauer zu einem Akkumulator und verbraucht dann wiederholt einen gewählten Schritt wie <code>16,667 ms</code>. Die Simulation sieht gleich große Updates, während der Renderer in einem anderen Rhythmus arbeiten darf. Der Rest bleibt im Akkumulator für den nächsten Frame. Bei einem langen Frame führt die feste Schleife mehrere kleine Updates aus, um die verstrichene Zeit abzubilden. Die Schrittgröße bleibt stabil, aber es kann ein Nachholstoß entstehen.' },
38
+ { type: 'table', headers: ['Situation', 'Variabler Delta-Pfad', 'Pfad mit festem Zeitschritt', 'Zu treffende Entscheidung'], rows: [['Gleichmäßiges Rendering', 'Ein Update verwendet jede normale Frame-Dauer', 'Gleiche Schritte verbrauchen die angesammelte Zeit', 'Beide Pfade sollten der Bewegung eng folgen.'], ['Langer Frame', 'Ein großes Update kann ein Objekt zu weit bewegen', 'Mehrere feste Updates holen die Echtzeit ein', 'Drift und Nachholzahl gemeinsam prüfen.'], ['Andere Bildrate', 'Die Updategröße ändert sich mit der Bildrate', 'Der Simulationsschritt bleibt gleich', 'Feste Schritte verringern die Abhängigkeit vom Rendering.'], ['Delta-Obergrenze', 'Zeit über der Grenze wird ignoriert', 'Der Akkumulator erhält weiterhin die volle Frame-Dauer', 'Nur begrenzen, wenn verlorene Zeit akzeptabel ist.']] },
39
+ { type: 'title', level: 2, text: 'Erkennen, was eine Frame-Spitze bewirkt' },
40
+ { type: 'paragraph', html: 'Bei 60 Frames pro Sekunde dauert ein regelmäßiger Frame etwa <code>16,667 ms</code>. Mit einer zusätzlichen Spitze von <code>80 ms</code> wird ein Frame ungefähr <code>96,667 ms</code> lang. Das variable Modell verarbeitet diese Dauer in einem einzigen Update. Das feste Modell verarbeitet stattdessen ungefähr sechs Schritte zu je <code>16,667 ms</code>. Die gesamte verstrichene Zeit kann gleich sein, während der Weg durch die Simulation anders verläuft.' },
41
+ { type: 'title', level: 2, text: 'Positionsdrift und Nachholen gemeinsam lesen' },
42
+ { type: 'paragraph', html: 'Der Positionsunterschied ist der variable Pfad minus der feste Pfad. Er zeigt, wie weit sich die beiden Integrationsregeln trennen, nicht automatisch, welche Regel richtig ist. Die Nachholzahl zeigt, wie viel feste Arbeit in einem gerenderten Frame nötig war. Ein großer Unterschied deutet auf sichtbare Bewegungsabweichung hin; eine große Nachholzahl deutet auf ein mögliches CPU-Budgetproblem hin. Beides hängt zusammen, ist aber nicht dieselbe Diagnose.' },
43
+ { type: 'title', level: 2, text: 'Delta-Begrenzung als Richtlinie behandeln' },
44
+ { type: 'paragraph', html: 'Eine Begrenzung kann verhindern, dass ein pausierter Tab, ein Breakpoint oder ein starker Hitch eine Figur teleportiert oder einen Physikkörper durch Geometrie schickt. Der Preis ist, dass die variable Uhr hinter der echten verstrichenen Zeit zurückbleibt. Wenn das Spiel Zeit bewahren muss, eignet sich ein fester Akkumulator oder eine andere Wiederherstellungsregel. Wenn das Spiel Sprünge begrenzen und reaktionsfähig bleiben muss, kann ein Clamp passen, aber die verlorene Zeit muss beabsichtigt sein.' },
45
+ { type: 'title', level: 2, text: 'Die Schleife nach der Aufgabe wählen' },
46
+ { type: 'table', headers: ['Aufgabe im Game-Loop', 'Sinnvoller Standard', 'Grund'], rows: [['Physik, Kollisionen oder deterministisches Gameplay', 'Fester Zeitschritt', 'Gleiche Schritte machen die Simulation weniger abhängig vom Render-Rhythmus.'], ['Einfache visuelle Bewegung ohne Zustandsaufbau', 'Variables Delta', 'Das Update ist klein und braucht meist keine Nachholwarteschlange.'], ['Gameplay-Simulation mit glatter Darstellung', 'Fester Update-Schritt mit Interpolation', 'Die Simulation behält ihren Schritt, während die Darstellung Zwischenbewegung verbirgt.'], ['Erholung nach einem schweren Stillstand', 'Begrenzte Nachholregel', 'Ein schlechter Frame soll keine unbegrenzte Simulationsarbeit erzeugen.']] },
47
+ { type: 'title', level: 2, text: 'Einen wiederholbaren Tuning-Ablauf nutzen' },
48
+ { type: 'paragraph', html: 'Beginne ohne Spitzen und prüfe, ob beide Pfade übereinstimmen. Füge einen wiederholbaren Hitch hinzu und ändere danach seinen Abstand, um zu sehen, ob der sichtbare Fehler anwächst oder sich beruhigt. Passe den festen Schritt an und beobachte die Nachholzahl, bevor du die Geschwindigkeit änderst. Aktiviere zuletzt die Begrenzung und vergleiche die variable Simulationszeit mit der Echtzeit. So trennst du die Ursache einer Abweichung von der Regel, die sie begrenzen soll.' },
49
+ { type: 'tip', title: 'Was dieses Experiment nicht beweisen kann', html: 'Das Labor verwendet festgelegte Frame-Dauern und eine konstante Geschwindigkeit. Es erklärt daher Timing-Verhalten, misst aber kein Gerät und bestätigt keine Engine. Echte Physik, Eingabeabtastung, Netzwerk, Interpolation und ein begrenztes Frame-Budget können die beste Lösung verändern. Nutze das Ergebnis als Hypothese und überprüfe sie im tatsächlichen Spiel.' },
50
+ ],
51
+ faq,
52
+ bibliographyTitle: 'Referenzen zu Game-Loops',
53
+ bibliography: bibliographyEntries,
54
+ howTo,
55
+ schemas: [softwareApplication, faqPage, howToSchema],
56
+ };