@jjlmoya/utils-games-development 1.64.0 → 1.66.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/tests/bibliography_wellformed_export.test.ts +46 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/seo_parity.test.ts +2 -2
- package/src/tests/seo_translation_completeness.test.ts +69 -0
- package/src/tests/seo_wellformed_export.test.ts +65 -0
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tests/translation_copy.test.ts +21 -18
- package/src/tool/audioLoopPointFinder/i18n/de.ts +13 -0
- package/src/tool/audioLoopPointFinder/i18n/fr.ts +26 -0
- package/src/tool/audioLoopPointFinder/i18n/id.ts +4 -0
- package/src/tool/audioLoopPointFinder/i18n/it.ts +3 -0
- package/src/tool/audioLoopPointFinder/i18n/nl.ts +4 -0
- package/src/tool/audioLoopPointFinder/i18n/pl.ts +4 -0
- package/src/tool/audioLoopPointFinder/i18n/pt.ts +3 -0
- package/src/tool/audioLoopPointFinder/i18n/ru.ts +4 -0
- package/src/tool/audioLoopPointFinder/i18n/sv.ts +4 -0
- package/src/tool/audioLoopPointFinder/i18n/tr.ts +4 -0
- package/src/tool/audioLoopPointFinder/i18n/zh.ts +4 -0
- package/src/tool/damageFormulaLab/bibliography.astro +3 -21
- package/src/tool/damageFormulaLab/i18n/de.ts +13 -0
- package/src/tool/damageFormulaLab/i18n/fr.ts +13 -0
- package/src/tool/damageFormulaLab/i18n/id.ts +13 -0
- package/src/tool/damageFormulaLab/i18n/it.ts +1 -0
- package/src/tool/damageFormulaLab/i18n/ja.ts +1 -0
- package/src/tool/damageFormulaLab/i18n/ko.ts +1 -0
- package/src/tool/damageFormulaLab/i18n/nl.ts +1 -0
- package/src/tool/damageFormulaLab/i18n/pl.ts +1 -0
- package/src/tool/damageFormulaLab/i18n/pt.ts +1 -0
- package/src/tool/damageFormulaLab/i18n/ru.ts +2 -0
- package/src/tool/damageFormulaLab/i18n/sv.ts +2 -0
- package/src/tool/damageFormulaLab/i18n/tr.ts +2 -0
- package/src/tool/damageFormulaLab/i18n/zh.ts +2 -0
- package/src/tool/damageFormulaLab/seo.astro +7 -5
- package/src/tool/gameDeltaTimeFixedTimestepLab/bibliography.astro +6 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/bibliography.ts +10 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/component.astro +51 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/controller.ts +79 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/dom-views.ts +88 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/entry.ts +27 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/evaluator.ts +21 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/game-delta-time-fixed-timestep-lab.css +491 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/de.ts +56 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/en.ts +180 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/es.ts +56 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/fr.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/id.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/it.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/ja.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/ko.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/nl.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/pl.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/pt.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/ru.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/sv.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/tr.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/i18n/zh.ts +46 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/index.ts +11 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/logic.test.ts +35 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/logic.ts +140 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/seo.astro +15 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/seo_contract.test.ts +25 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/storage.ts +23 -0
- package/src/tool/gameDeltaTimeFixedTimestepLab/ui.ts +54 -0
- package/src/tool/gameUIAccessibilityTester/i18n/de.ts +13 -0
- package/src/tool/gameUIAccessibilityTester/i18n/fr.ts +13 -0
- package/src/tool/gameUIAccessibilityTester/i18n/id.ts +13 -0
- package/src/tool/gameUIAccessibilityTester/i18n/it.ts +1 -0
- package/src/tool/gameUIAccessibilityTester/i18n/nl.ts +5 -0
- package/src/tool/gameUIAccessibilityTester/i18n/pl.ts +5 -0
- package/src/tool/gameUIAccessibilityTester/i18n/pt.ts +2 -0
- package/src/tool/gameUIAccessibilityTester/i18n/ru.ts +1 -0
- package/src/tool/gameUIAccessibilityTester/i18n/sv.ts +1 -0
- package/src/tool/gameUIAccessibilityTester/i18n/tr.ts +2 -0
- package/src/tool/gameUIAccessibilityTester/i18n/zh.ts +1 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/de.ts +14 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/fr.ts +13 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/id.ts +13 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/it.ts +1 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/nl.ts +1 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/pl.ts +2 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/pt.ts +2 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/ru.ts +2 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/sv.ts +1 -0
- package/src/tool/hitboxHurtboxAnimator/i18n/tr.ts +2 -0
- package/src/tool/hitboxHurtboxAnimator/seo.astro +7 -5
- package/src/tool/itchioGameTester/i18n/de.ts +13 -0
- package/src/tool/itchioGameTester/i18n/es.ts +2 -2
- package/src/tool/itchioGameTester/i18n/fr.ts +26 -0
- package/src/tool/itchioGameTester/i18n/id.ts +3 -0
- package/src/tool/itchioGameTester/i18n/it.ts +3 -0
- package/src/tool/itchioGameTester/i18n/ja.ts +3 -0
- package/src/tool/itchioGameTester/i18n/ko.ts +3 -0
- package/src/tool/itchioGameTester/i18n/nl.ts +3 -0
- package/src/tool/itchioGameTester/i18n/pl.ts +3 -0
- package/src/tool/itchioGameTester/i18n/pt.ts +3 -0
- package/src/tool/itchioGameTester/i18n/ru.ts +3 -0
- package/src/tool/itchioGameTester/i18n/sv.ts +3 -0
- package/src/tool/itchioGameTester/i18n/tr.ts +3 -0
- package/src/tool/itchioGameTester/i18n/zh.ts +3 -0
- package/src/tool/localizationSanitizer/i18n/de.ts +13 -0
- package/src/tool/localizationSanitizer/i18n/es.ts +26 -0
- package/src/tool/localizationSanitizer/i18n/fr.ts +4 -0
- package/src/tool/localizationSanitizer/i18n/id.ts +4 -0
- package/src/tool/localizationSanitizer/i18n/it.ts +4 -0
- package/src/tool/localizationSanitizer/i18n/ja.ts +4 -0
- package/src/tool/localizationSanitizer/i18n/ko.ts +4 -0
- package/src/tool/localizationSanitizer/i18n/nl.ts +5 -0
- package/src/tool/localizationSanitizer/i18n/pl.ts +5 -0
- package/src/tool/localizationSanitizer/i18n/pt.ts +5 -0
- package/src/tool/localizationSanitizer/i18n/ru.ts +5 -0
- package/src/tool/localizationSanitizer/i18n/sv.ts +5 -0
- package/src/tool/localizationSanitizer/i18n/tr.ts +5 -0
- package/src/tool/localizationSanitizer/i18n/zh.ts +6 -0
- package/src/tool/pixelArtPaletteSwapper/i18n/ja.ts +13 -0
- package/src/tool/pixelArtPaletteSwapper/i18n/ko.ts +13 -0
- package/src/tool/pixelArtPaletteSwapper/i18n/zh.ts +18 -0
- package/src/tool/retroSfxGenerator/i18n/de.ts +13 -0
- package/src/tool/retroSfxGenerator/i18n/fr.ts +13 -0
- package/src/tool/retroSfxGenerator/i18n/id.ts +13 -0
- package/src/tool/retroSfxGenerator/i18n/it.ts +1 -0
- package/src/tool/retroSfxGenerator/i18n/nl.ts +1 -0
- package/src/tool/retroSfxGenerator/i18n/pl.ts +1 -0
- package/src/tool/retroSfxGenerator/i18n/pt.ts +1 -0
- package/src/tool/retroSfxGenerator/i18n/ru.ts +1 -0
- package/src/tool/retroSfxGenerator/i18n/sv.ts +2 -0
- package/src/tool/retroSfxGenerator/i18n/tr.ts +1 -0
- package/src/tool/retroSfxGenerator/i18n/zh.ts +1 -0
- package/src/tool/spriteSheetPacker/i18n/de.ts +13 -0
- package/src/tool/spriteSheetPacker/i18n/es.ts +2 -2
- package/src/tool/spriteSheetPacker/i18n/fr.ts +26 -0
- package/src/tool/spriteSheetPacker/i18n/id.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/it.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/ja.ts +3 -0
- package/src/tool/spriteSheetPacker/i18n/ko.ts +3 -0
- package/src/tool/spriteSheetPacker/i18n/nl.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/pl.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/pt.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/ru.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/sv.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/tr.ts +4 -0
- package/src/tool/spriteSheetPacker/i18n/zh.ts +4 -0
- package/src/tool/steamBbcodeTranslator/i18n/de.ts +22 -0
- package/src/tool/steamBbcodeTranslator/i18n/es.ts +23 -1
- package/src/tool/steamBbcodeTranslator/i18n/fr.ts +6 -1
- package/src/tool/steamBbcodeTranslator/i18n/id.ts +7 -1
- package/src/tool/steamBbcodeTranslator/i18n/it.ts +6 -1
- package/src/tool/steamBbcodeTranslator/i18n/ja.ts +5 -1
- package/src/tool/steamBbcodeTranslator/i18n/ko.ts +5 -1
- package/src/tool/steamBbcodeTranslator/i18n/nl.ts +7 -1
- package/src/tool/steamBbcodeTranslator/i18n/pl.ts +7 -1
- package/src/tool/steamBbcodeTranslator/i18n/pt.ts +7 -1
- package/src/tool/steamBbcodeTranslator/i18n/ru.ts +7 -1
- package/src/tool/steamBbcodeTranslator/i18n/sv.ts +7 -1
- package/src/tool/steamBbcodeTranslator/i18n/tr.ts +7 -1
- package/src/tool/steamBbcodeTranslator/i18n/zh.ts +5 -1
- package/src/tool/steamCapsuleGenerator/i18n/de.ts +2 -2
- package/src/tool/steamCapsuleGenerator/i18n/es.ts +2 -2
- package/src/tool/steamCapsuleGenerator/i18n/fr.ts +2 -2
- package/src/tool/steamCapsuleGenerator/i18n/pl.ts +13 -0
- package/src/tool/steamCapsuleGenerator/i18n/ru.ts +26 -0
- package/src/tool/steamCapsuleGenerator/i18n/sv.ts +1 -0
- package/src/tool/steamCapsuleGenerator/i18n/tr.ts +1 -0
- package/src/tools.ts +2 -0
|
@@ -0,0 +1,46 @@
|
|
|
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: '이 고정 타임스텝 실험실은 무엇을 보여 주나요?', answer: '같은 움직이는 오브젝트를 가변 델타 시간 루프와 고정 타임스텝 누산기로 실행합니다. 반복 가능한 프레임 스파이크를 넣으면 게임 엔진 없이 시뮬레이션 시간, 위치 차이, 따라잡기 스텝을 확인할 수 있습니다.' },
|
|
8
|
+
{ question: '가변 델타와 고정 타임스텝의 차이는 무엇인가요?', answer: '가변 모델은 렌더링된 각 프레임의 시간으로 한 번 업데이트합니다. 고정 모델은 같은 크기의 시뮬레이션 스텝을 사용하고 누산기에 쌓인 실제 경과 시간만큼 스텝을 처리합니다. 고정 스텝은 렌더링 속도 의존성을 줄이지만 긴 프레임에서는 추가 따라잡기 작업이 필요할 수 있습니다.' },
|
|
9
|
+
{ question: '프레임 스파이크 입력은 무엇을 의미하나요?', answer: '선택한 스파이크 프레임마다 추가할 밀리초입니다. 빈도 값으로 몇 프레임마다 스파이크를 넣을지 정하므로 무작위 성능 기록 대신 반복 가능한 hitch를 만들 수 있습니다.' },
|
|
10
|
+
{ question: '델타 상한은 무엇을 바꾸나요?', answer: '프레임이 선택한 상한보다 길 때 가변 모델이 사용하는 델타를 줄입니다. 큰 순간 이동은 막을 수 있지만 해당 모델의 시뮬레이션 시계는 실제 시간보다 뒤처집니다. 고정 누산기는 프레임 전체 시간을 계속 계산합니다.' },
|
|
11
|
+
{ question: '프로파일러인가요, 플레이테스트를 대신하나요?', answer: '아닙니다. 지정된 숫자로 시간 처리 방식을 배우고 설계하는 결정론적 실험실입니다. 기기 성능을 측정하거나 엔진을 진단하거나 렌더링 비용을 모델링하거나 모든 게임에 맞는 루프를 증명하지는 않습니다.' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: '프레임 리듬 고르기', text: '목표 초당 프레임을 설정하고 밀리초 단위의 반복 가능한 스파이크를 추가합니다. 빈도로 스파이크가 나타날 프레임 간격을 정합니다.' },
|
|
15
|
+
{ name: '시뮬레이션 스텝 설정하기', text: '고정 타임스텝을 밀리초로 고릅니다. 16.667밀리초 근처의 값은 초당 약 60회의 시뮬레이션 업데이트를 뜻합니다.' },
|
|
16
|
+
{ name: '두 모델에 같은 움직임 규칙 주기', text: '속도와 실행 시간을 설정합니다. 두 모델이 같은 오브젝트를 움직이므로 위치 차이는 경과 시간을 소비하는 방식에서 생깁니다.' },
|
|
17
|
+
{ name: '상한을 의도적으로 테스트하기', text: '델타 상한을 켜고 가변 모델의 시뮬레이션 시간과 고정 누산기를 비교합니다. 상한은 비용이 있는 정책이지 무료 수정이 아닙니다.' },
|
|
18
|
+
{ name: '결과 읽기', text: '두 경로, 차이 요약, 타임라인, 따라잡기 동작, 접근 가능한 프레임 표를 확인합니다. 설명이나 디버깅에서는 한 번에 하나의 입력만 바꿉니다.' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: '게임 델타 시간과 고정 타임스텝 실험실', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: '가변 델타 시간과 고정 타임스텝 비교하기', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'game-delta-time-fixed-timestep-lab', title: '게임 델타 시간과 고정 타임스텝 실험실', description: '반복 가능한 프레임 스파이크, 움직임 시뮬레이션, 따라잡기 스텝과 보이는 시간 차이로 가변 및 고정 게임 루프를 비교합니다.',
|
|
26
|
+
ui: { controlsTitle: '프레임 패턴 만들기', fpsLabel: '목표 프레임 속도', fpsHint: '초당 렌더링 프레임', spikeLabel: '프레임 스파이크', spikeHint: '스파이크 프레임에 더할 시간', spikeEveryLabel: '스파이크 간격', fixedDtLabel: '고정 타임스텝', fixedDtHint: '같은 크기의 시뮬레이션 스텝', velocityLabel: '오브젝트 속도', durationLabel: '실행 시간', clampLabel: '델타 상한', clampToggle: '가변 델타 제한', runLabel: '실험 실행', resetLabel: '값 초기화', stageKicker: '실험 공간', stageTitle: '두 개의 시계, 하나의 움직이는 오브젝트', stageCaption: '막대는 hitch를 보여 주고 선은 두 위치를 추적합니다. 아래 선은 최종값에 가려질 수 있는 차이를 분리합니다.', frameTraceLabel: '프레임 시간', positionPlotLabel: '오브젝트 위치', differencePlotLabel: '고정 경로와의 위치 차이', variableLane: '가변 델타', fixedLane: '고정 누산기', frameCountLabel: '렌더링된 프레임', wallTimeLabel: '실제 시간', variableTimeLabel: '가변 시뮬레이션 시간', fixedTimeLabel: '고정 시뮬레이션 시간', divergenceLabel: '위치 차이', stepsLabel: '고정 시뮬레이션 스텝', diagnosticsTitle: '프레임 읽기', stableStatus: '이 패턴에서는 두 경로가 계속 일치합니다.', variableStatus: '프레임 시간에 스파이크가 포함되므로 가변 경로가 앞섭니다.', fixedStatus: '긴 프레임 뒤 고정 경로는 여러 시뮬레이션 스텝으로 따라잡습니다.', clampStatus: '상한은 가변 점프를 줄이고 해당 모델을 경과한 실제 시간보다 늦게 만듭니다.', timelineTitle: '움직임 기록', timelineCaption: '경과한 실제 시간에 따른 위치입니다. 점선은 같은 고정 스텝을 표시합니다.', frameAxis: '경과한 실제 시간', positionAxis: '오브젝트 위치', legendVariable: '가변 델타 경로', legendFixed: '고정 타임스텝 경로', legendSpike: '프레임 스파이크', tableTitle: '접근 가능한 프레임 기록', tableFrame: '프레임', tableWall: '실제 시간', tableVariable: '가변 위치', tableFixed: '고정 위치', tableDelta: '차이', modelNote: '가변 모델은 현재 프레임 시간으로 속도를 한 번 적용합니다. 고정 모델은 전체 실제 시간을 누적하고 같은 크기의 스텝으로 진행합니다. 어느 경로도 성능 측정값은 아닙니다.', privacyDisclosure: '값은 이 브라우저에만 저장되어 다음에 돌아왔을 때 실험을 이어갈 수 있습니다. 게임 데이터나 텔레메트리는 전송되지 않습니다.', unitMs: 'ms', unitSeconds: '초', unitPixels: '단위', statusReady: '실험 업데이트됨' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: '게임 루프 안의 시계를 이해하기' },
|
|
29
|
+
{ type: 'paragraph', html: '렌더링된 각 프레임은 게임에 새로운 경과 시간을 전달합니다. 가변 델타 루프에서는 보통 <code>position += velocity × frameTime</code>처럼 움직임을 업데이트합니다. 프레임이 안정적이면 평균 속도는 의도한 값에 가깝지만, 한 번의 업데이트가 그 업데이트를 만든 프레임만큼 커질 수 있습니다. 따라서 hitch는 단순한 화면 정지가 아니라 그 업데이트가 소비하는 게임 시간을 바꾸는 사건입니다.' },
|
|
30
|
+
{ type: 'title', level: 2, text: '가변 적분과 고정 누산기 비교하기' },
|
|
31
|
+
{ type: 'paragraph', html: '고정 타임스텝 루프는 각 프레임 시간을 누산기에 더한 뒤 <code>16.667 ms</code> 같은 선택된 스텝을 반복해서 소비합니다. 시뮬레이션은 같은 크기의 업데이트를 받고 렌더러는 다른 리듬으로 표시할 수 있습니다. 남은 소수 시간은 다음 프레임까지 누산기에 남습니다. 긴 프레임이 오면 고정 루프는 경과 시간을 반영하기 위해 여러 작은 업데이트를 수행합니다. 스텝 크기는 안정적이지만 따라잡기 작업이 순간적으로 늘 수 있습니다.' },
|
|
32
|
+
{ type: 'table', headers: ['상황', '가변 델타 경로', '고정 타임스텝 경로', '판단할 내용'], rows: [['안정적인 렌더링', '일반 프레임 시간으로 한 번 업데이트', '같은 스텝으로 누적 시간을 소비', '두 경로가 같은 움직임을 따르는지 확인합니다.'], ['긴 프레임', '큰 업데이트가 오브젝트를 너무 멀리 이동', '여러 고정 업데이트가 시간을 따라잡음', '위치 차이와 따라잡기 수를 함께 읽습니다.'], ['다른 렌더링 속도', '업데이트 크기가 프레임 속도에 따라 변함', '시뮬레이션 스텝은 동일함', '고정 스텝은 렌더링 의존성을 줄입니다.'], ['델타 상한', '상한을 넘는 시간을 무시', '누산기는 전체 프레임 시간을 받음', '시간 손실을 허용할 때만 제한합니다.']] },
|
|
33
|
+
{ type: 'title', level: 2, text: '프레임 스파이크가 실제로 하는 일 보기' },
|
|
34
|
+
{ type: 'paragraph', html: '초당 60프레임에서는 일반 프레임이 약 <code>16.667 ms</code>입니다. 여기에 <code>80 ms</code> 스파이크가 추가되면 한 프레임은 약 <code>96.667 ms</code>가 됩니다. 가변 모델은 이 시간을 한 번의 업데이트로 소비합니다. 고정 모델은 대신 약 여섯 번의 <code>16.667 ms</code> 스텝을 소비합니다. 전체 경과 시간은 같을 수 있지만 시뮬레이션을 통과하는 경로는 달라집니다.' },
|
|
35
|
+
{ type: 'title', level: 2, text: '위치 차이와 따라잡기를 함께 읽기' },
|
|
36
|
+
{ type: 'paragraph', html: '위치 차이는 가변 경로에서 고정 경로를 뺀 값입니다. 두 적분 정책이 얼마나 벌어졌는지를 보여 주지만 어느 쪽이 자동으로 옳은지는 말해 주지 않습니다. 따라잡기 수는 한 번의 렌더링 프레임에서 필요한 고정 작업량을 보여 줍니다. 큰 차이는 보이는 움직임의 불일치를, 많은 스텝은 CPU 예산 문제 가능성을 나타냅니다. 서로 관련 있지만 같은 진단은 아닙니다.' },
|
|
37
|
+
{ type: 'title', level: 2, text: '델타 제한을 정책으로 다루기' },
|
|
38
|
+
{ type: 'paragraph', html: '상한은 일시 정지된 탭, 브레이크포인트 또는 심각한 hitch가 캐릭터를 순간 이동시키거나 물리 오브젝트를 지오메트리 너머로 보내는 일을 막을 수 있습니다. 대신 가변 시계는 실제 경과 시간보다 뒤처집니다. 게임이 시간을 보존해야 한다면 고정 누산기나 다른 복구 정책을 사용합니다. 점프를 제한하고 반응성을 유지해야 한다면 상한이 유용할 수 있지만 잃는 시간을 의도적으로 선택해야 합니다.' },
|
|
39
|
+
{ type: 'title', level: 2, text: '보호해야 할 작업에 맞춰 루프 선택하기' },
|
|
40
|
+
{ type: 'table', headers: ['게임 루프 작업', '유용한 기본값', '이유'], rows: [['물리, 충돌 또는 결정론적 게임플레이', '고정 타임스텝', '같은 스텝이 렌더링 리듬에 대한 의존성을 줄입니다.'], ['상태를 누적하지 않는 단순한 시각 효과', '가변 델타', '업데이트가 작고 보통 따라잡기 큐가 필요하지 않습니다.'], ['부드러운 표시가 필요한 게임플레이 시뮬레이션', '보간이 있는 고정 업데이트', '시뮬레이션 스텝은 유지하고 표시에서 소수 진행을 숨깁니다.'], ['심각한 정지 뒤 복구', '제한된 따라잡기 정책', '나쁜 프레임 하나가 무제한 시뮬레이션 작업을 만들지 않게 합니다.']] },
|
|
41
|
+
{ type: 'title', level: 2, text: '반복 가능한 조정 절차 사용하기' },
|
|
42
|
+
{ type: 'paragraph', html: '먼저 스파이크 없이 두 경로가 일치하는지 확인합니다. 반복 가능한 hitch를 추가하고 빈도를 바꿔 보이는 오류가 누적되는지 안정되는지 확인합니다. 속도를 바꾸기 전에 고정 스텝을 조정하고 따라잡기 수를 살핍니다. 마지막으로 상한을 켜고 가변 시뮬레이션 시간과 실제 시간을 비교합니다. 이 순서로 차이의 원인과 이를 제한하는 정책을 분리할 수 있습니다.' },
|
|
43
|
+
{ type: 'tip', title: '이 실험으로 증명할 수 없는 것', html: '이 실험실은 지정된 프레임 시간과 일정한 속도를 사용하므로 시간 동작을 설명할 뿐 기기를 측정하거나 엔진을 검증하지 않습니다. 실제 물리, 입력 샘플링, 네트워크, 보간, 프레임 예산에 따라 최선의 설계는 달라질 수 있습니다. 결과를 가설로 삼고 실제 게임에서 확인하세요.' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: '게임 루프 참고 자료', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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: 'Wat laat dit laboratorium voor een vaste timestep zien?', answer: 'Het voert hetzelfde bewegende object door een lus met variabele delta en door een accumulator met vaste timestep. Herhaalbare framepieken maken gesimuleerde tijd, positiedrift en inhaalstappen zichtbaar zonder een game-engine te starten.' },
|
|
8
|
+
{ question: 'Wat is het verschil tussen variabele delta en vaste timestep?', answer: 'Het variabele model werkt eenmaal per gerenderd frame met de duur van dat frame. Het vaste model werkt met gelijke simulatiestappen en gebruikt een accumulator om de stappen te verwerken die de verstreken echte tijd vereist. Vaste stappen verminderen de afhankelijkheid van de renderfrequentie, maar een lang frame kan extra inhaalwerk veroorzaken.' },
|
|
9
|
+
{ question: 'Wat stelt de invoer voor een framepiek voor?', answer: 'De invoer voegt extra milliseconden toe aan elk geselecteerd piekframe. Met het interval bepaal je na hoeveel frames de piek verschijnt, zodat je een herhaalbaar hitchpatroon kunt onderzoeken.' },
|
|
10
|
+
{ question: 'Wat verandert delta begrenzen?', answer: 'De bovengrens beperkt de delta die het variabele model gebruikt wanneer een frame langer is dan de gekozen grens. Dat kan een te grote sprong voorkomen, maar de gesimuleerde klok loopt dan achter op de echte tijd. De vaste accumulator blijft de volledige frameduur verwerken.' },
|
|
11
|
+
{ question: 'Is dit een profiler of vervangt het playtesten?', answer: 'Nee. Het is een deterministisch leer- en ontwerplaboratorium met opgegeven getallen. Het meet geen apparaat, diagnosticeert geen engine, modelleert geen renderkosten en bewijst niet dat één lus voor elk spel geschikt is.' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: 'Kies het frameritme', text: 'Stel de gewenste frames per seconde in en voeg een herhaalbare piek in milliseconden toe. Gebruik het interval om de piek elke gekozen hoeveelheid frames te plaatsen.' },
|
|
15
|
+
{ name: 'Stel de simulatiestap in', text: 'Kies de vaste timestep in milliseconden. Een waarde rond 16,667 milliseconden staat voor ongeveer 60 simulatie-updates per seconde.' },
|
|
16
|
+
{ name: 'Geef beide werelden dezelfde bewegingsregel', text: 'Stel snelheid en duur in. Beide modellen bewegen hetzelfde object, dus het positieverschil ontstaat door hun omgang met verstreken tijd.' },
|
|
17
|
+
{ name: 'Test een begrenzing bewust', text: 'Schakel de deltabegrenzing in en vergelijk de variabele simulatietijd met de vaste accumulator. Een begrenzing is een keuze met zichtbare kosten, geen gratis correctie.' },
|
|
18
|
+
{ name: 'Lees het bewijs', text: 'Bekijk de twee paden, de divergentiesamenvatting, de tijdlijn, het inhaalgedrag en de toegankelijke frametabel. Verander tijdens uitleg of foutzoeken één invoer tegelijk.' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Game delta time en vaste timestep laboratorium', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Variabele delta time vergelijken met vaste timestep', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'game-delta-time-en-vaste-timestep-laboratorium', title: 'Game Delta Time en Vaste Timestep Laboratorium', description: 'Vergelijk game-lussen met variabele delta en vaste timestep met herhaalbare framepieken, gesimuleerde beweging, inhaalstappen en zichtbare tijdsdrift.',
|
|
26
|
+
ui: { controlsTitle: 'Bouw het framemotief', fpsLabel: 'Doelframerate', fpsHint: 'Gerenderde frames per seconde', spikeLabel: 'Framepiek', spikeHint: 'Extra tijd voor piekframes', spikeEveryLabel: 'Piek elke frames', fixedDtLabel: 'Vaste timestep', fixedDtHint: 'Gelijke grootte van de simulatiestap', velocityLabel: 'Snelheid van object', durationLabel: 'Duur van experiment', clampLabel: 'Delta-plafond', clampToggle: 'Variabele delta begrenzen', runLabel: 'Experiment uitvoeren', resetLabel: 'Waarden herstellen', stageKicker: 'Het proefveld', stageTitle: 'Twee klokken, één bewegend object', stageCaption: 'Balken tonen de hitches, lijnen volgen beide posities en het onderste spoor maakt de drift zichtbaar die eindwaarden kunnen verbergen.', frameTraceLabel: 'Frameduren', positionPlotLabel: 'Objectpositie', differencePlotLabel: 'Positieverschil met vast pad', variableLane: 'Variabele delta', fixedLane: 'Vaste accumulator', frameCountLabel: 'Gerenderde frames', wallTimeLabel: 'Echte tijd', variableTimeLabel: 'Variabele simulatietijd', fixedTimeLabel: 'Vaste simulatietijd', divergenceLabel: 'Positieverschil', stepsLabel: 'Vaste simulatiestappen', diagnosticsTitle: 'Framelezing', stableStatus: 'De twee paden blijven voor dit patroon gelijk lopen.', variableStatus: 'Het variabele pad loopt voor omdat de frameduren de pieken bevatten.', fixedStatus: 'Het vaste pad haalt na een lang frame in met meerdere simulatiestappen.', clampStatus: 'De begrenzing verkleint de variabele sprong en laat dit model achterlopen op de verstreken echte tijd.', timelineTitle: 'Het bewegingsregister', timelineCaption: 'Positie over verstreken echte tijd. Een stippellijn markeert gelijke vaste stappen.', frameAxis: 'Verstreken echte tijd', positionAxis: 'Objectpositie', legendVariable: 'Variabel deltapad', legendFixed: 'Pad met vaste timestep', legendSpike: 'Framepiek', tableTitle: 'Toegankelijk frameregister', tableFrame: 'Frame', tableWall: 'Echte tijd', tableVariable: 'Variabele positie', tableFixed: 'Vaste positie', tableDelta: 'Verschil', modelNote: 'Het variabele model gebruikt de huidige frameduur eenmaal met de snelheid. Het vaste model verzamelt alle echte tijd en beweegt in gelijke stappen. Geen van beide paden is een prestatiemeting.', privacyDisclosure: 'Je waarden worden alleen in deze browser opgeslagen, zodat het experiment klaarstaat wanneer je terugkomt. Er worden geen gamegegevens of telemetrie verzonden.', unitMs: 'ms', unitSeconds: 's', unitPixels: 'eenheden', statusReady: 'Experiment bijgewerkt' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: 'Begrijp de klok in een game-lus' },
|
|
29
|
+
{ type: 'paragraph', html: 'Elk gerenderd frame geeft de game nieuwe verstreken tijd. In een lus met variabele delta wordt beweging vaak bijgewerkt als <code>position += velocity × frameTime</code>. Bij stabiele frames blijft de gemiddelde snelheid dicht bij de bedoeling, maar één update kan zo groot worden als het frame dat haar veroorzaakte. Een hitch is daarom niet alleen een visuele pauze: hij verandert de hoeveelheid gametijd die die update verbruikt.' },
|
|
30
|
+
{ type: 'title', level: 2, text: 'Vergelijk variabele integratie met een vaste accumulator' },
|
|
31
|
+
{ type: 'paragraph', html: 'Een lus met vaste timestep telt elke frameduur op bij een accumulator en verbruikt daarna herhaaldelijk een gekozen stap zoals <code>16,667 ms</code>. De simulatie ziet gelijke updates, terwijl de renderer een ander ritme kan houden. De resterende fractie blijft in de accumulator voor het volgende frame. Bij een lang frame voert de vaste lus meerdere kleine updates uit om de verstreken tijd te verwerken. De stapgrootte blijft stabiel, maar er kan een inhaalpiek ontstaan.' },
|
|
32
|
+
{ type: 'table', headers: ['Situatie', 'Pad met variabele delta', 'Pad met vaste timestep', 'Beslissing om te overwegen'], rows: [['Stabiele rendering', 'Eén update gebruikt elke normale frameduur', 'Gelijke stappen verbruiken de verzamelde tijd', 'Beide paden zouden dezelfde beweging moeten volgen.'], ['Lang frame', 'Eén grote update kan een object te ver verplaatsen', 'Meerdere vaste updates halen de tijd in', 'Lees drift samen met het aantal inhaalstappen.'], ['Andere renderfrequentie', 'De updategrootte verandert met de framerate', 'De simulatiestap blijft gelijk', 'Vaste stappen verminderen renderafhankelijkheid.'], ['Delta-plafond', 'Tijd boven de grens wordt genegeerd', 'De accumulator ontvangt de volledige frameduur', 'Begrens alleen wanneer tijd verliezen aanvaardbaar is.']] },
|
|
33
|
+
{ type: 'title', level: 2, text: 'Zie wat een framepiek werkelijk doet' },
|
|
34
|
+
{ type: 'paragraph', html: 'Bij 60 frames per seconde duurt een normaal frame ongeveer <code>16,667 ms</code>. Met een extra piek van <code>80 ms</code> duurt één frame ongeveer <code>96,667 ms</code>. Het variabele model verwerkt die hele duur in één update. Het vaste model verwerkt in plaats daarvan ongeveer zes stappen van <code>16,667 ms</code>. De totale verstreken tijd kan gelijk zijn, terwijl het pad door de simulatie verschilt.' },
|
|
35
|
+
{ type: 'title', level: 2, text: 'Lees positiedrift samen met inhalen' },
|
|
36
|
+
{ type: 'paragraph', html: 'Positieverschil is het variabele pad min het vaste pad. Het toont hoe ver de twee integratiekeuzes uit elkaar zijn geraakt, niet automatisch welke keuze juist is. Het aantal inhaalstappen toont hoeveel vaste arbeid in één gerenderd frame nodig was. Een groot verschil wijst op zichtbare bewegingsafwijking; veel inhaalstappen wijzen op een mogelijk CPU-budgetprobleem. De signalen hangen samen, maar zijn niet dezelfde diagnose.' },
|
|
37
|
+
{ type: 'title', level: 2, text: 'Behandel delta begrenzen als een beleid' },
|
|
38
|
+
{ type: 'paragraph', html: 'Een begrenzing kan voorkomen dat een gepauzeerd tabblad, breakpoint of zware hitch een personage teleporteert of een physics-object door geometrie laat gaan. De prijs is dat de variabele klok achterloopt op de echte verstreken tijd. Als de game timing moet behouden, gebruik dan een vaste accumulator of ander herstelbeleid. Als sprongen begrensd en de game responsief moet blijven, kan een begrenzing passend zijn, maar het verlies aan tijd moet opzettelijk zijn.' },
|
|
39
|
+
{ type: 'title', level: 2, text: 'Kies de lus voor de taak die ze beschermt' },
|
|
40
|
+
{ type: 'table', headers: ['Taak in de game-lus', 'Nuttige standaard', 'Reden'], rows: [['Physics, botsingen of deterministische gameplay', 'Vaste timestep', 'Gelijke stappen maken de simulatie minder afhankelijk van het render-ritme.'], ['Eenvoudige visuele beweging zonder opgebouwde toestand', 'Variabele delta', 'De update is klein en heeft meestal geen inhaalwachtrij nodig.'], ['Gameplay-simulatie met vloeiende rendering', 'Vaste update met interpolatie', 'De simulatie houdt zijn stap terwijl de presentatie de fractie verbergt.'], ['Herstel na een zware stilstand', 'Begrensd inhaalbeleid', 'Voorkom dat één slecht frame onbeperkt simulatiewerk veroorzaakt.']] },
|
|
41
|
+
{ type: 'title', level: 2, text: 'Gebruik een herhaalbare afstelmethode' },
|
|
42
|
+
{ type: 'paragraph', html: 'Begin zonder pieken en bevestig dat beide paden samenvallen. Voeg één herhaalbare hitch toe en verander daarna het interval om te zien of de zichtbare fout optelt of stabiliseert. Pas de vaste stap aan en bekijk het aantal inhaalstappen voordat je de snelheid verandert. Schakel ten slotte de begrenzing in en vergelijk variabele simulatietijd met echte tijd. Zo scheid je de oorzaak van drift van het beleid dat haar moet beperken.' },
|
|
43
|
+
{ type: 'tip', title: 'Wat dit experiment niet kan bewijzen', html: 'Het laboratorium gebruikt opgegeven frameduren en constante snelheid. Het verklaart timinggedrag, maar meet geen apparaat en valideert geen engine. Echte physics, inputmeting, netwerk, interpolatie en framebudget kunnen de beste keuze veranderen. Gebruik het resultaat als hypothese en controleer die daarna in de echte game.' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: 'Referenties over game-lussen', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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: 'Co pokazuje laboratorium stałego kroku czasu?', answer: 'Uruchamia ten sam poruszający się obiekt w pętli ze zmiennym delta time oraz w akumulatorze ze stałym timestepem. Powtarzalne skoki klatki pokazują czas symulacji, różnicę pozycji i kroki nadrabiania bez uruchamiania silnika gry.' },
|
|
8
|
+
{ question: 'Czym różni się zmienne delta od stałego timestepu?', answer: 'Model zmienny wykonuje krok raz na każdą wyrenderowaną klatkę i używa jej długości. Model stały działa w równych krokach symulacji, a akumulator przetwarza tyle kroków, ile wymaga upływ czasu rzeczywistego. Stałe kroki ograniczają zależność od częstotliwości renderowania, ale długa klatka może wymagać dodatkowej pracy.' },
|
|
9
|
+
{ question: 'Co oznacza wartość skoku klatki?', answer: 'Dodaje dodatkowe milisekundy do każdej wybranej klatki ze skokiem. Pole częstotliwości określa, co ile klatek pojawia się skok, więc można stworzyć powtarzalny hitch zamiast polegać na losowym pomiarze.' },
|
|
10
|
+
{ question: 'Co zmienia ograniczenie delta?', answer: 'Ograniczenie zmniejsza delta używane przez model zmienny, gdy klatka przekracza wybrany próg. Może zapobiec zbyt dużemu skokowi, ale zegar symulacji tego modelu pozostaje wtedy za czasem rzeczywistym. Stały akumulator nadal uwzględnia całą długość klatki.' },
|
|
11
|
+
{ question: 'Czy to profiler albo zamiennik testów gry?', answer: 'Nie. To deterministyczne laboratorium edukacyjne i projektowe korzystające z zadanych liczb. Nie mierzy urządzenia, nie diagnozuje silnika, nie modeluje kosztu renderowania i nie dowodzi, że jedna pętla pasuje do każdej gry.' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: 'Wybierz rytm klatek', text: 'Ustaw docelową liczbę klatek na sekundę i dodaj powtarzalny skok w milisekundach. Częstotliwość umieszcza go co wybraną liczbę klatek.' },
|
|
15
|
+
{ name: 'Ustaw krok symulacji', text: 'Wybierz stały timestep w milisekundach. Wartość bliska 16,667 milisekundy oznacza około 60 aktualizacji symulacji na sekundę.' },
|
|
16
|
+
{ name: 'Nadaj obu światom tę samą regułę ruchu', text: 'Ustaw prędkość i czas trwania. Oba modele poruszają ten sam obiekt, więc różnica pozycji wynika ze sposobu wykorzystania upływającego czasu.' },
|
|
17
|
+
{ name: 'Świadomie przetestuj ograniczenie', text: 'Włącz ograniczenie delta i porównaj czas symulacji modelu zmiennego ze stałym akumulatorem. Ograniczenie to decyzja z widocznym kosztem, a nie darmowa poprawka.' },
|
|
18
|
+
{ name: 'Odczytaj dowody', text: 'Sprawdź obie ścieżki, podsumowanie rozbieżności, oś czasu, nadrabianie oraz dostępną tabelę klatek. Podczas nauki lub debugowania zmieniaj jedną wartość naraz.' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Laboratorium game delta time i stałego timestepu', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Porównać zmienne delta time ze stałym timestepem', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'laboratorium-delta-time-i-stalego-timestepu-gier', title: 'Laboratorium Delta Time i Stałego Timestepu Gier', description: 'Porównaj pętle gier ze zmiennym delta i stałym timestepem dzięki powtarzalnym skokom klatek, symulacji ruchu, krokom nadrabiania i widocznej różnicy czasu.',
|
|
26
|
+
ui: { controlsTitle: 'Zbuduj wzorzec klatek', fpsLabel: 'Docelowa liczba klatek', fpsHint: 'Wyrenderowane klatki na sekundę', spikeLabel: 'Skok klatki', spikeHint: 'Dodatkowy czas dla klatek ze skokiem', spikeEveryLabel: 'Skok co ile klatek', fixedDtLabel: 'Stały timestep', fixedDtHint: 'Równa długość kroku symulacji', velocityLabel: 'Prędkość obiektu', durationLabel: 'Czas trwania', clampLabel: 'Limit delta', clampToggle: 'Ogranicz zmienne delta', runLabel: 'Uruchom eksperyment', resetLabel: 'Przywróć wartości', stageKicker: 'Pole doświadczalne', stageTitle: 'Dwa zegary, jeden poruszający się obiekt', stageCaption: 'Słupki pokazują hitches, linie śledzą obie pozycje, a dolna ścieżka izoluje różnicę ukrytą przez wartości końcowe.', frameTraceLabel: 'Długości klatek', positionPlotLabel: 'Pozycja obiektu', differencePlotLabel: 'Różnica pozycji względem stałej ścieżki', variableLane: 'Zmienna delta', fixedLane: 'Stały akumulator', frameCountLabel: 'Wyrenderowane klatki', wallTimeLabel: 'Czas rzeczywisty', variableTimeLabel: 'Zmienny czas symulacji', fixedTimeLabel: 'Stały czas symulacji', divergenceLabel: 'Różnica pozycji', stepsLabel: 'Stałe kroki symulacji', diagnosticsTitle: 'Odczyt klatek', stableStatus: 'Obie ścieżki pozostają zgodne dla tego wzorca.', variableStatus: 'Zmienna ścieżka wyprzedza, bo jej długości klatek zawierają skoki.', fixedStatus: 'Stała ścieżka nadrabia po długiej klatce kilkoma krokami symulacji.', clampStatus: 'Limit zmniejsza zmienny skok i pozostawia ten model za upływającym czasem rzeczywistym.', timelineTitle: 'Rejestr ruchu', timelineCaption: 'Pozycja względem upływającego czasu rzeczywistego. Linia przerywana oznacza równe stałe kroki.', frameAxis: 'Upływający czas rzeczywisty', positionAxis: 'Pozycja obiektu', legendVariable: 'Ścieżka zmiennej delty', legendFixed: 'Ścieżka stałego timestepu', legendSpike: 'Skok klatki', tableTitle: 'Dostępny rejestr klatek', tableFrame: 'Klatka', tableWall: 'Czas rzeczywisty', tableVariable: 'Pozycja zmienna', tableFixed: 'Pozycja stała', tableDelta: 'Różnica', modelNote: 'Model zmienny stosuje prędkość raz z bieżącą długością klatki. Model stały gromadzi cały czas rzeczywisty i porusza się równymi krokami. Żadna ścieżka nie jest pomiarem wydajności.', privacyDisclosure: 'Wartości są zapisywane tylko w tej przeglądarce, aby eksperyment był gotowy po powrocie. Żadne dane gry ani telemetria nie są wysyłane.', unitMs: 'ms', unitSeconds: 's', unitPixels: 'jedn.', statusReady: 'Eksperyment zaktualizowany' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: 'Zrozum zegar wewnątrz pętli gry' },
|
|
29
|
+
{ type: 'paragraph', html: 'Każda wyrenderowana klatka dostarcza grze nowy czas, który upłynął. W pętli ze zmiennym delta ruch jest często aktualizowany jako <code>position += velocity × frameTime</code>. Przy równych klatkach średnia prędkość pozostaje bliska zamierzonej, ale pojedyncza aktualizacja może być tak duża jak klatka, która ją wywołała. Hitch nie jest więc tylko pauzą obrazu: zmienia ilość czasu gry zużytą przez tę aktualizację.' },
|
|
30
|
+
{ type: 'title', level: 2, text: 'Porównaj zmienną integrację ze stałym akumulatorem' },
|
|
31
|
+
{ type: 'paragraph', html: 'Pętla ze stałym timestepem dodaje długość każdej klatki do akumulatora, a następnie wielokrotnie zużywa wybrany krok, na przykład <code>16,667 ms</code>. Symulacja dostaje równe aktualizacje, choć renderer może pracować w innym rytmie. Pozostały ułamek zostaje w akumulatorze na następną klatkę. Gdy pojawia się długa klatka, stała pętla wykonuje kilka małych aktualizacji, aby uwzględnić upływ czasu. Krok pozostaje stabilny, lecz może wystąpić skok pracy nadrabiania.' },
|
|
32
|
+
{ type: 'table', headers: ['Sytuacja', 'Ścieżka zmiennej delty', 'Ścieżka stałego timestepu', 'Decyzja do rozważenia'], rows: [['Stabilne renderowanie', 'Jedna aktualizacja używa każdej zwykłej długości klatki', 'Równe kroki zużywają zgromadzony czas', 'Obie ścieżki powinny podążać za tym samym ruchem.'], ['Długa klatka', 'Duża aktualizacja może przesunąć obiekt za daleko', 'Kilka stałych aktualizacji nadrabia czas', 'Odczytaj różnicę razem z liczbą kroków nadrabiania.'], ['Inna częstotliwość renderowania', 'Rozmiar aktualizacji zmienia się z frame rate', 'Krok symulacji pozostaje taki sam', 'Stałe kroki zmniejszają zależność od renderowania.'], ['Limit delta', 'Czas ponad limitem jest pomijany', 'Akumulator dostaje pełną długość klatki', 'Ograniczaj tylko wtedy, gdy utrata czasu jest akceptowalna.']] },
|
|
33
|
+
{ type: 'title', level: 2, text: 'Zobacz rzeczywisty skutek skoku klatki' },
|
|
34
|
+
{ type: 'paragraph', html: 'Przy 60 klatkach na sekundę zwykła klatka trwa około <code>16,667 ms</code>. Z dodatkowym skokiem <code>80 ms</code> jedna klatka trwa około <code>96,667 ms</code>. Model zmienny zużywa cały ten czas w jednej aktualizacji. Model stały zużywa zamiast tego około sześciu kroków po <code>16,667 ms</code>. Całkowity upływ czasu może być taki sam, choć droga przez symulację jest inna.' },
|
|
35
|
+
{ type: 'title', level: 2, text: 'Czytaj różnicę pozycji razem z nadrabianiem' },
|
|
36
|
+
{ type: 'paragraph', html: 'Różnica pozycji to ścieżka zmienna minus stała. Pokazuje, jak bardzo rozeszły się zasady integracji, ale nie mówi automatycznie, która jest poprawna. Liczba kroków nadrabiania pokazuje, ile stałej pracy wykonano w jednej wyrenderowanej klatce. Duża różnica wskazuje na widoczną rozbieżność ruchu; duża liczba kroków wskazuje na możliwy problem budżetu CPU. Sygnały są powiązane, lecz nie oznaczają tej samej diagnozy.' },
|
|
37
|
+
{ type: 'title', level: 2, text: 'Traktuj ograniczenie delta jak politykę' },
|
|
38
|
+
{ type: 'paragraph', html: 'Limit może zapobiec teleportacji postaci albo przenikaniu ciała fizycznego przez geometrię po wstrzymaniu karty, breakpointcie lub silnym hitchu. Ceną jest opóźnienie zmiennego zegara względem rzeczywistego czasu. Jeśli gra musi zachować czas, użyj stałego akumulatora albo innej polityki odzyskiwania. Jeśli musi ograniczać skoki i pozostać responsywna, limit może pasować, ale utracony czas powinien być zamierzony.' },
|
|
39
|
+
{ type: 'title', level: 2, text: 'Wybierz pętlę według zadania, które chroni' },
|
|
40
|
+
{ type: 'table', headers: ['Zadanie pętli gry', 'Przydatny wybór domyślny', 'Powód'], rows: [['Fizyka, kolizje lub deterministyczna rozgrywka', 'Stały timestep', 'Równe kroki ograniczają zależność symulacji od rytmu renderowania.'], ['Prosty ruch wizualny bez akumulowanego stanu', 'Zmienna delta', 'Aktualizacja jest mała i zwykle nie potrzebuje kolejki nadrabiania.'], ['Symulacja rozgrywki z płynnym renderingiem', 'Stała aktualizacja z interpolacją', 'Symulacja zachowuje krok, a prezentacja ukrywa ułamek postępu.'], ['Odzyskiwanie po poważnym zatrzymaniu', 'Ograniczona polityka nadrabiania', 'Jeden zły frame nie powinien tworzyć nieograniczonej pracy.']] },
|
|
41
|
+
{ type: 'title', level: 2, text: 'Zastosuj powtarzalny proces strojenia' },
|
|
42
|
+
{ type: 'paragraph', html: 'Zacznij bez skoków i potwierdź zgodność obu ścieżek. Dodaj powtarzalny hitch, a następnie zmień jego częstotliwość, aby sprawdzić, czy widoczny błąd narasta, czy się stabilizuje. Dostosuj stały krok i obserwuj liczbę kroków nadrabiania przed zmianą prędkości. Na końcu włącz limit i porównaj zmienny czas symulacji z czasem rzeczywistym. Ta kolejność oddziela przyczynę rozbieżności od polityki, która ma ją ograniczać.' },
|
|
43
|
+
{ type: 'tip', title: 'Czego ten eksperyment nie dowodzi', html: 'Laboratorium używa zadanych długości klatek i stałej prędkości, więc wyjaśnia zachowanie czasu, ale nie mierzy urządzenia i nie zatwierdza silnika. Rzeczywista fizyka, próbkowanie wejścia, sieć, interpolacja i budżet klatki mogą zmienić najlepszy projekt. Potraktuj wynik jako hipotezę i sprawdź ją w prawdziwej grze.' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: 'Materiały o pętlach gry', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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: 'O que este laboratório de timestep fixo demonstra?', answer: 'Ele executa o mesmo objeto em movimento em um loop de delta variável e em um acumulador de timestep fixo. Picos de frame repetíveis tornam visíveis o tempo simulado, o desvio de posição e os passos de recuperação sem executar uma engine de jogo.' },
|
|
8
|
+
{ question: 'Qual é a diferença entre delta variável e timestep fixo?', answer: 'O modelo variável avança uma vez por frame renderizado usando a duração desse frame. O modelo fixo avança em passos de simulação iguais e usa um acumulador para processar os passos exigidos pelo tempo real decorrido. Passos fixos reduzem a dependência da taxa de renderização, mas um frame longo pode exigir trabalho extra.' },
|
|
9
|
+
{ question: 'O que representa a entrada de pico de frame?', answer: 'Ela adiciona milissegundos extras a cada frame escolhido como pico. O campo de frequência define a cada quantos frames o pico aparece, permitindo criar um hitch repetível em vez de depender de uma captura aleatória.' },
|
|
10
|
+
{ question: 'O que muda ao limitar o delta?', answer: 'O limite reduz o delta usado pelo modelo variável quando um frame ultrapassa o teto escolhido. Isso pode impedir um salto grande, mas faz o relógio simulado desse modelo ficar atrasado em relação ao tempo real. O acumulador fixo continua contabilizando toda a duração do frame.' },
|
|
11
|
+
{ question: 'Isto é um profiler ou substitui os testes do jogo?', answer: 'Não. É um laboratório determinístico de aprendizagem e design com números declarados. Ele não mede o seu dispositivo, diagnostica uma engine, modela o custo de renderização nem prova que um loop seja correto para todos os jogos.' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: 'Escolher o ritmo dos frames', text: 'Defina os frames por segundo desejados e adicione um pico repetível em milissegundos. Use a frequência para posicionar esse pico a cada determinado número de frames.' },
|
|
15
|
+
{ name: 'Definir o passo de simulação', text: 'Escolha o timestep fixo em milissegundos. Um valor próximo de 16,667 milissegundos representa cerca de 60 atualizações de simulação por segundo.' },
|
|
16
|
+
{ name: 'Dar a mesma regra de movimento', text: 'Defina velocidade e duração. Os dois modelos movem o mesmo objeto, então a diferença de posição vem da forma como cada um consome o tempo decorrido.' },
|
|
17
|
+
{ name: 'Testar um limite conscientemente', text: 'Ative o limite de delta e compare o tempo simulado do modelo variável com o acumulador fixo. Um limite é uma escolha com um custo visível, não uma correção gratuita.' },
|
|
18
|
+
{ name: 'Ler as evidências', text: 'Observe as duas trajetórias, o resumo da divergência, a linha do tempo, a recuperação e a tabela acessível de frames. Altere uma entrada por vez ao ensinar ou depurar o loop.' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Laboratório de delta time e timestep fixo para jogos', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Comparar delta time variável com timestep fixo', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'laboratorio-delta-time-e-timestep-fixo-jogos', title: 'Laboratório de Delta Time e Timestep Fixo para Jogos', description: 'Compare loops de jogo com delta variável e timestep fixo usando picos de frame repetíveis, movimento simulado, passos de recuperação e desvio temporal visível.',
|
|
26
|
+
ui: { controlsTitle: 'Monte o padrão de frames', fpsLabel: 'Taxa de frames alvo', fpsHint: 'Frames renderizados por segundo', spikeLabel: 'Pico de frame', spikeHint: 'Tempo extra adicionado aos frames de pico', spikeEveryLabel: 'Pico a cada frames', fixedDtLabel: 'Timestep fixo', fixedDtHint: 'Tamanho igual do passo de simulação', velocityLabel: 'Velocidade do objeto', durationLabel: 'Duração do experimento', clampLabel: 'Teto do delta', clampToggle: 'Limitar o delta variável', runLabel: 'Executar experimento', resetLabel: 'Redefinir valores', stageKicker: 'O campo de testes', stageTitle: 'Dois relógios, um objeto em movimento', stageCaption: 'As barras mostram os hitches, as linhas acompanham as duas posições e o traço inferior isola o desvio que os valores finais podem esconder.', frameTraceLabel: 'Durações dos frames', positionPlotLabel: 'Posição do objeto', differencePlotLabel: 'Diferença de posição em relação ao caminho fixo', variableLane: 'Delta variável', fixedLane: 'Acumulador fixo', frameCountLabel: 'Frames renderizados', wallTimeLabel: 'Tempo real', variableTimeLabel: 'Tempo simulado variável', fixedTimeLabel: 'Tempo simulado fixo', divergenceLabel: 'Diferença de posição', stepsLabel: 'Passos de simulação fixos', diagnosticsTitle: 'Leitura dos frames', stableStatus: 'Os dois caminhos permanecem alinhados neste padrão.', variableStatus: 'O caminho variável avança porque as suas durações incluem os picos.', fixedStatus: 'O caminho fixo recupera o tempo com vários passos de simulação depois de um frame longo.', clampStatus: 'O limite reduz o salto variável e deixa esse modelo atrás do tempo real decorrido.', timelineTitle: 'O registro do movimento', timelineCaption: 'Posição ao longo do tempo real decorrido. Uma linha tracejada marca passos fixos iguais.', frameAxis: 'Tempo real decorrido', positionAxis: 'Posição do objeto', legendVariable: 'Caminho de delta variável', legendFixed: 'Caminho de timestep fixo', legendSpike: 'Pico de frame', tableTitle: 'Registro acessível de frames', tableFrame: 'Frame', tableWall: 'Tempo real', tableVariable: 'Posição variável', tableFixed: 'Posição fixa', tableDelta: 'Diferença', modelNote: 'O modelo variável aplica a velocidade uma vez com a duração do frame atual. O modelo fixo acumula todo o tempo real e avança em passos iguais. Nenhum caminho é uma medição de desempenho.', privacyDisclosure: 'Os seus valores são guardados apenas neste navegador para que o experimento esteja pronto quando voltar. Nenhum dado do jogo ou telemetria é enviado.', unitMs: 'ms', unitSeconds: 's', unitPixels: 'unidades', statusReady: 'Experimento atualizado' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: 'Entenda o relógio dentro de um loop de jogo' },
|
|
29
|
+
{ type: 'paragraph', html: 'Cada frame renderizado entrega ao jogo um novo tempo decorrido. Em um loop de delta variável, o movimento costuma ser atualizado como <code>position += velocity × frameTime</code>. Quando os frames são estáveis, a velocidade média fica próxima da desejada, mas uma atualização pode ter o mesmo tamanho do frame que a produziu. Por isso, um hitch não é apenas uma pausa visual: ele muda a quantidade de tempo de jogo consumida por essa atualização.' },
|
|
30
|
+
{ type: 'title', level: 2, text: 'Compare a integração variável com um acumulador fixo' },
|
|
31
|
+
{ type: 'paragraph', html: 'Um loop de timestep fixo soma a duração de cada frame a um acumulador e consome repetidamente um passo escolhido, como <code>16,667 ms</code>. A simulação recebe atualizações iguais enquanto o render pode manter outro ritmo. A fração restante fica no acumulador para o próximo frame. Quando chega um frame longo, o loop fixo executa várias atualizações pequenas para contabilizar o tempo decorrido. O tamanho do passo permanece estável, mas pode surgir um pico de trabalho de recuperação.' },
|
|
32
|
+
{ type: 'table', headers: ['Situação', 'Caminho de delta variável', 'Caminho de timestep fixo', 'Decisão a considerar'], rows: [['Renderização estável', 'Uma atualização usa cada duração normal', 'Passos iguais consomem o tempo acumulado', 'Os dois caminhos devem acompanhar o mesmo movimento.'], ['Frame longo', 'Uma atualização grande pode mover o objeto longe demais', 'Várias atualizações fixas recuperam o tempo', 'Observe o desvio junto com a contagem de recuperação.'], ['Taxa de renderização diferente', 'O tamanho da atualização muda com o frame rate', 'O passo de simulação permanece igual', 'Passos fixos reduzem a dependência do render.'], ['Teto do delta', 'O tempo acima do limite é ignorado', 'O acumulador recebe a duração completa', 'Limite apenas quando perder tempo for aceitável.']] },
|
|
33
|
+
{ type: 'title', level: 2, text: 'Veja o que um pico de frame realmente faz' },
|
|
34
|
+
{ type: 'paragraph', html: 'A 60 frames por segundo, um frame normal dura cerca de <code>16,667 ms</code>. Com um pico adicional de <code>80 ms</code>, um frame passa a durar cerca de <code>96,667 ms</code>. O modelo variável consome toda essa duração em uma atualização. O modelo fixo consome em vez disso cerca de seis passos de <code>16,667 ms</code>. O tempo total decorrido pode coincidir, embora o percurso dentro da simulação seja diferente.' },
|
|
35
|
+
{ type: 'title', level: 2, text: 'Leia o desvio de posição junto com a recuperação' },
|
|
36
|
+
{ type: 'paragraph', html: 'A diferença de posição é o caminho variável menos o caminho fixo. Ela mostra o quanto as duas políticas de integração se separaram, não qual delas está automaticamente correta. A contagem de recuperação mostra quanto trabalho fixo foi necessário em um único frame renderizado. Uma diferença grande aponta para uma divergência de movimento visível; muitos passos apontam para um possível problema de orçamento de CPU. Os sinais estão relacionados, mas não dão o mesmo diagnóstico.' },
|
|
37
|
+
{ type: 'title', level: 2, text: 'Trate o limite do delta como uma política' },
|
|
38
|
+
{ type: 'paragraph', html: 'Um limite pode impedir que uma aba pausada, um breakpoint ou um hitch grave teletransporte um personagem ou faça um corpo físico atravessar a geometria. O custo é que o relógio variável fica atrás do tempo real decorrido. Se o jogo precisa preservar o tempo, use um acumulador fixo ou outra política de recuperação. Se precisa manter os saltos limitados e continuar responsivo, um limite pode ser adequado, mas o tempo perdido deve ser intencional.' },
|
|
39
|
+
{ type: 'title', level: 2, text: 'Escolha o loop pelo trabalho que ele protege' },
|
|
40
|
+
{ type: 'table', headers: ['Trabalho do loop', 'Padrão útil', 'Motivo'], rows: [['Física, colisões ou gameplay determinístico', 'Timestep fixo', 'Passos iguais tornam a simulação menos dependente do ritmo de renderização.'], ['Movimento visual simples sem estado acumulado', 'Delta variável', 'A atualização é pequena e normalmente não precisa de uma fila de recuperação.'], ['Simulação de gameplay com renderização suave', 'Atualização fixa com interpolação', 'A simulação mantém o passo enquanto a apresentação esconde o progresso fracionário.'], ['Recuperação depois de uma pausa grave', 'Política de recuperação limitada', 'Evita transformar um frame ruim em trabalho de simulação ilimitado.']] },
|
|
41
|
+
{ type: 'title', level: 2, text: 'Use um fluxo de ajuste repetível' },
|
|
42
|
+
{ type: 'paragraph', html: 'Comece sem picos e confirme que os dois caminhos coincidem. Adicione um hitch repetível e mude a sua frequência para ver se o erro visível se acumula ou se estabiliza. Ajuste o passo fixo e observe a contagem de recuperação antes de alterar a velocidade. Por fim, ative o limite e compare o tempo simulado variável com o tempo real. Essa sequência separa a causa de uma divergência da política usada para contê-la.' },
|
|
43
|
+
{ type: 'tip', title: 'O que este experimento não pode provar', html: 'O laboratório usa durações de frame declaradas e velocidade constante, portanto explica o comportamento temporal, mas não mede um dispositivo nem valida uma engine. Física real, amostragem de entrada, rede, interpolação e orçamento de frame podem mudar o melhor design. Use o resultado para formular uma hipótese e depois verifique-a no jogo real.' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: 'Referências sobre loops de jogos', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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: 'Что показывает лаборатория фиксированного шага?', answer: 'Она прогоняет один и тот же движущийся объект через цикл с переменным delta time и через аккумулятор с фиксированным timestep. Повторяемые скачки длительности кадра показывают модельное время, расхождение позиций и шаги догоняющего расчёта без запуска игрового движка.' },
|
|
8
|
+
{ question: 'Чем отличаются переменный delta и фиксированный timestep?', answer: 'Переменная модель делает один шаг на каждый отрисованный кадр и использует его длительность. Фиксированная модель работает равными шагами симуляции, а аккумулятор обрабатывает столько шагов, сколько требует прошедшее реальное время. Фиксированный шаг уменьшает зависимость симуляции от частоты кадров, но длинный кадр может потребовать дополнительной работы.' },
|
|
9
|
+
{ question: 'Что означает ввод скачка кадра?', answer: 'Он добавляет миллисекунды к каждому выбранному кадру со скачком. Поле частоты задаёт, через сколько кадров появляется скачок, поэтому можно создать повторяемый hitch вместо случайного профиля производительности.' },
|
|
10
|
+
{ question: 'Что меняет ограничение delta?', answer: 'Ограничение уменьшает delta, которое использует переменная модель, если кадр длиннее выбранного порога. Это может предотвратить слишком большой рывок, но модельное время этих часов отстанет от реального. Фиксированный аккумулятор по-прежнему учитывает всю длительность кадра.' },
|
|
11
|
+
{ question: 'Это профайлер или замена игрового тестирования?', answer: 'Нет. Это детерминированная лаборатория для обучения и проектирования с заданными числами. Она не измеряет устройство, не диагностирует движок, не моделирует стоимость рендеринга и не доказывает, что один цикл подходит любой игре.' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: 'Выберите ритм кадров', text: 'Задайте целевую частоту кадров и добавьте повторяемый скачок в миллисекундах. Частота размещает его через выбранное число кадров.' },
|
|
15
|
+
{ name: 'Задайте шаг симуляции', text: 'Выберите фиксированный timestep в миллисекундах. Значение около 16,667 миллисекунды соответствует примерно 60 обновлениям симуляции в секунду.' },
|
|
16
|
+
{ name: 'Дайте обеим моделям одно правило движения', text: 'Задайте скорость и длительность. Обе модели двигают один объект, поэтому разница позиций возникает из-за разного расходования прошедшего времени.' },
|
|
17
|
+
{ name: 'Осознанно проверьте ограничение', text: 'Включите ограничение delta и сравните модельное время переменной модели с фиксированным аккумулятором. Ограничение имеет видимую цену и не является бесплатным исправлением.' },
|
|
18
|
+
{ name: 'Прочитайте результат', text: 'Изучите обе траектории, сводку расхождения, временную шкалу, догоняющие шаги и доступную таблицу кадров. При объяснении или отладке меняйте по одному параметру.' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Лаборатория delta time и фиксированного timestep для игр', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Сравнить переменный delta time и фиксированный timestep', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'laboratoriya-delta-time-i-fiksirovannogo-timestep-igr', title: 'Лаборатория Delta Time и фиксированного Timestep для игр', description: 'Сравните игровые циклы с переменным delta и фиксированным timestep с помощью повторяемых скачков кадров, симуляции движения, догоняющих шагов и видимого расхождения времени.',
|
|
26
|
+
ui: { controlsTitle: 'Создайте ритм кадров', fpsLabel: 'Целевая частота кадров', fpsHint: 'Отрисованные кадры в секунду', spikeLabel: 'Скачок кадра', spikeHint: 'Дополнительное время для кадров со скачком', spikeEveryLabel: 'Скачок через кадров', fixedDtLabel: 'Фиксированный timestep', fixedDtHint: 'Одинаковый размер шага симуляции', velocityLabel: 'Скорость объекта', durationLabel: 'Длительность запуска', clampLabel: 'Предел delta', clampToggle: 'Ограничить переменный delta', runLabel: 'Запустить эксперимент', resetLabel: 'Сбросить значения', stageKicker: 'Экспериментальная площадка', stageTitle: 'Двое часов, один движущийся объект', stageCaption: 'Столбики показывают hitch, линии отслеживают обе позиции, а нижняя линия выделяет расхождение, которое могут скрыть итоговые значения.', frameTraceLabel: 'Длительности кадров', positionPlotLabel: 'Позиция объекта', differencePlotLabel: 'Разница позиции относительно фиксированного пути', variableLane: 'Переменный delta', fixedLane: 'Фиксированный аккумулятор', frameCountLabel: 'Отрисованные кадры', wallTimeLabel: 'Реальное время', variableTimeLabel: 'Переменное модельное время', fixedTimeLabel: 'Фиксированное модельное время', divergenceLabel: 'Разница позиций', stepsLabel: 'Фиксированные шаги симуляции', diagnosticsTitle: 'Чтение кадров', stableStatus: 'Для этого шаблона оба пути остаются согласованными.', variableStatus: 'Переменный путь опережает, потому что длительности кадров включают скачки.', fixedStatus: 'После длинного кадра фиксированный путь догоняет время несколькими шагами симуляции.', clampStatus: 'Ограничение уменьшает переменный скачок и оставляет эту модель позади прошедшего реального времени.', timelineTitle: 'Журнал движения', timelineCaption: 'Позиция относительно прошедшего реального времени. Пунктир отмечает одинаковые фиксированные шаги.', frameAxis: 'Прошедшее реальное время', positionAxis: 'Позиция объекта', legendVariable: 'Путь переменного delta', legendFixed: 'Путь фиксированного timestep', legendSpike: 'Скачок кадра', tableTitle: 'Доступный журнал кадров', tableFrame: 'Кадр', tableWall: 'Реальное время', tableVariable: 'Переменная позиция', tableFixed: 'Фиксированная позиция', tableDelta: 'Разница', modelNote: 'Переменная модель применяет скорость один раз с текущей длительностью кадра. Фиксированная модель накапливает всё реальное время и движется равными шагами. Ни один путь не является измерением производительности.', privacyDisclosure: 'Значения сохраняются только в этом браузере, чтобы эксперимент был готов при следующем визите. Данные игры и телеметрия никуда не отправляются.', unitMs: 'мс', unitSeconds: 'с', unitPixels: 'ед.', statusReady: 'Эксперимент обновлён' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: 'Поймите часы внутри игрового цикла' },
|
|
29
|
+
{ type: 'paragraph', html: 'Каждый отрисованный кадр передаёт игре новое значение прошедшего времени. В цикле с переменным delta движение часто обновляется так: <code>position += velocity × frameTime</code>. При стабильных кадрах средняя скорость близка к заданной, но одна операция может стать такой же большой, как вызвавший её кадр. Поэтому hitch не просто задерживает изображение: он меняет количество игрового времени, потраченное этой операцией.' },
|
|
30
|
+
{ type: 'title', level: 2, text: 'Сравните переменную интеграцию с фиксированным аккумулятором' },
|
|
31
|
+
{ type: 'paragraph', html: 'Цикл с фиксированным timestep добавляет длительность каждого кадра в аккумулятор, а затем многократно расходует выбранный шаг, например <code>16,667 мс</code>. Симуляция получает одинаковые обновления, а рендер может идти в другом ритме. Оставшаяся дробная часть хранится до следующего кадра. Если кадр длинный, фиксированный цикл выполняет несколько маленьких обновлений, чтобы учесть прошедшее время. Размер шага остаётся стабильным, но возникает всплеск догоняющей работы.' },
|
|
32
|
+
{ type: 'table', headers: ['Ситуация', 'Путь переменного delta', 'Путь фиксированного timestep', 'Решение для рассмотрения'], rows: [['Стабильный рендер', 'Одна операция использует обычную длительность кадра', 'Равные шаги расходуют накопленное время', 'Оба пути должны близко следовать одному движению.'], ['Длинный кадр', 'Большая операция может слишком далеко сдвинуть объект', 'Несколько фиксированных операций догоняют время', 'Смотрите расхождение вместе с числом шагов.'], ['Другая частота рендера', 'Размер операции меняется вместе с частотой кадров', 'Шаг симуляции остаётся прежним', 'Фиксированные шаги уменьшают зависимость от рендера.'], ['Предел delta', 'Время выше предела игнорируется', 'Аккумулятор получает всю длительность кадра', 'Ограничивайте только при приемлемой потере времени.']] },
|
|
33
|
+
{ type: 'title', level: 2, text: 'Посмотрите на настоящий эффект скачка кадра' },
|
|
34
|
+
{ type: 'paragraph', html: 'При 60 кадрах в секунду обычный кадр длится около <code>16,667 мс</code>. При дополнительном скачке в <code>80 мс</code> один кадр длится примерно <code>96,667 мс</code>. Переменная модель расходует всё это время за одну операцию. Фиксированная модель вместо этого выполняет около шести шагов по <code>16,667 мс</code>. Общее прошедшее время может совпасть, хотя путь симуляции будет разным.' },
|
|
35
|
+
{ type: 'title', level: 2, text: 'Читайте расхождение позиции вместе с догоняющей работой' },
|
|
36
|
+
{ type: 'paragraph', html: 'Разница позиций равна переменному пути минус фиксированный путь. Она показывает, насколько разошлись правила интеграции, но не определяет автоматически правильный вариант. Число догоняющих шагов показывает объём фиксированной работы в одном отрисованном кадре. Большая разница указывает на видимое расхождение движения, а большое число шагов может указывать на проблему бюджета CPU. Это связанные, но разные диагнозы.' },
|
|
37
|
+
{ type: 'title', level: 2, text: 'Считайте ограничение delta политикой' },
|
|
38
|
+
{ type: 'paragraph', html: 'Предел может помешать паузе вкладки, breakpoint или тяжёлому hitch отправить персонажа телепортом или протолкнуть физическое тело сквозь геометрию. Цена состоит в том, что переменные часы отстанут от реального времени. Если игре важно сохранить время, используйте фиксированный аккумулятор или другую стратегию восстановления. Если важно ограничить скачок и сохранить отзывчивость, предел может подойти, но потеря времени должна быть осознанной.' },
|
|
39
|
+
{ type: 'title', level: 2, text: 'Выбирайте цикл по задаче, которую он защищает' },
|
|
40
|
+
{ type: 'table', headers: ['Задача игрового цикла', 'Полезный вариант по умолчанию', 'Причина'], rows: [['Физика, столкновения или детерминированный геймплей', 'Фиксированный timestep', 'Одинаковые шаги уменьшают зависимость симуляции от ритма рендера.'], ['Простое визуальное движение без накопления состояния', 'Переменный delta', 'Обновление мало и обычно не требует очереди догоняющих шагов.'], ['Симуляция геймплея с плавным рендером', 'Фиксированное обновление с интерполяцией', 'Симуляция сохраняет шаг, а представление скрывает дробный прогресс.'], ['Восстановление после сильной остановки', 'Ограниченная политика догоняющих шагов', 'Один плохой кадр не должен создавать бесконечную работу.']] },
|
|
41
|
+
{ type: 'title', level: 2, text: 'Используйте повторяемый процесс настройки' },
|
|
42
|
+
{ type: 'paragraph', html: 'Начните без скачков и убедитесь, что оба пути совпадают. Добавьте повторяемый hitch, затем измените его частоту и проверьте, накапливается ли видимая ошибка или стабилизируется. Настройте фиксированный шаг и посмотрите число догоняющих операций до изменения скорости. Наконец включите предел и сравните переменное модельное время с реальным. Так причина расхождения отделяется от политики, которая его ограничивает.' },
|
|
43
|
+
{ type: 'tip', title: 'Чего этот эксперимент не доказывает', html: 'Лаборатория использует заданные длительности кадров и постоянную скорость. Она объясняет поведение времени, но не измеряет устройство и не подтверждает игровой движок. Реальная физика, опрос ввода, сеть, интерполяция и бюджет кадра могут изменить лучший дизайн. Используйте результат как гипотезу и проверьте её в настоящей игре.' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: 'Материалы об игровых циклах', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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: 'Vad visar det här laboratoriet för fast timestep?', answer: 'Det kör samma rörliga objekt genom en loop med variabel delta-tid och en ackumulator med fast timestep. Upprepningsbara bildrute-spikar gör simulerad tid, positionsdrift och ikappsteg synliga utan att starta en spelmotor.' },
|
|
8
|
+
{ question: 'Vad är skillnaden mellan variabel delta och fast timestep?', answer: 'Den variabla modellen uppdateras en gång per renderad bildruta och använder bildrutans längd. Den fasta modellen använder lika stora simuleringssteg och en ackumulator som behandlar så många steg som den förflutna verkliga tiden kräver. Fasta steg minskar beroendet av renderingsfrekvensen, men en lång bildruta kan skapa extra ikapparbete.' },
|
|
9
|
+
{ question: 'Vad betyder inmatningen för en bildrute-spik?', answer: 'Den lägger till extra millisekunder på varje vald spikbildruta. Fältet för intervall bestämmer efter hur många bildrutor spiken kommer, så du kan skapa ett upprepningsbart hitchmönster.' },
|
|
10
|
+
{ question: 'Vad ändras när delta begränsas?', answer: 'Gränsen minskar den delta som den variabla modellen använder när en bildruta är längre än det valda taket. Det kan förhindra ett för stort hopp, men modellens simulerade klocka hamnar efter verklig tid. Den fasta ackumulatorn räknar fortfarande hela bildrutans längd.' },
|
|
11
|
+
{ question: 'Är detta en profiler eller ersätter det speltestning?', answer: 'Nej. Det är ett deterministiskt laboratorium för lärande och design med angivna tal. Det mäter inte en enhet, diagnostiserar ingen motor, modellerar inte renderingskostnad och bevisar inte att en loop passar alla spel.' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: 'Välj bildrutornas rytm', text: 'Ställ in önskat antal bildrutor per sekund och lägg till en upprepningsbar spik i millisekunder. Intervallet placerar spiken med det valda antalet bildrutor emellan.' },
|
|
15
|
+
{ name: 'Ställ in simuleringssteget', text: 'Välj fast timestep i millisekunder. Ett värde nära 16,667 millisekunder motsvarar ungefär 60 simuleringsuppdateringar per sekund.' },
|
|
16
|
+
{ name: 'Ge båda modellerna samma rörelseregel', text: 'Ange hastighet och varaktighet. Båda modellerna flyttar samma objekt, så positionsskillnaden beror på hur de använder den förflutna tiden.' },
|
|
17
|
+
{ name: 'Testa en gräns medvetet', text: 'Aktivera delta-gränsen och jämför variabel simulerad tid med den fasta ackumulatorn. En gräns är ett val med synlig kostnad, inte en gratis korrigering.' },
|
|
18
|
+
{ name: 'Läs bevisen', text: 'Granska de två banorna, sammanfattningen av avvikelsen, tidslinjen, ikappbeteendet och den tillgängliga bildrutetabellen. Ändra en inmatning i taget när du lär ut eller felsöker loopen.' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Laboratorium för game delta-tid och fast timestep', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Jämför variabel delta-tid med fast timestep', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'laboratorium-game-delta-tid-och-fast-timestep', title: 'Laboratorium för Game Delta tid och Fast Timestep', description: 'Jämför spel-loopar med variabel delta och fast timestep med upprepningsbara bildrute-spikar, simulerad rörelse, ikappsteg och synlig tidsdrift.',
|
|
26
|
+
ui: { controlsTitle: 'Bygg bildrutemönstret', fpsLabel: 'Målfrekvens', fpsHint: 'Renderade bildrutor per sekund', spikeLabel: 'Bildrute-spik', spikeHint: 'Extra tid på spikbildrutor', spikeEveryLabel: 'Spik var n:te bildruta', fixedDtLabel: 'Fast timestep', fixedDtHint: 'Lika stort simuleringssteg', velocityLabel: 'Objektets hastighet', durationLabel: 'Körningens längd', clampLabel: 'Delta-tak', clampToggle: 'Begränsa variabel delta-tid', runLabel: 'Kör experiment', resetLabel: 'Återställ värden', stageKicker: 'Testfältet', stageTitle: 'Två klockor, ett rörligt objekt', stageCaption: 'Staplarna visar hitchar, linjerna följer båda positionerna och det nedre spåret isolerar driften som slutvärden kan dölja.', frameTraceLabel: 'Bildrutors längd', positionPlotLabel: 'Objektets position', differencePlotLabel: 'Positionsskillnad från fast bana', variableLane: 'Variabel delta', fixedLane: 'Fast ackumulator', frameCountLabel: 'Renderade bildrutor', wallTimeLabel: 'Verklig tid', variableTimeLabel: 'Variabel simulerad tid', fixedTimeLabel: 'Fast simulerad tid', divergenceLabel: 'Positionsskillnad', stepsLabel: 'Fasta simuleringssteg', diagnosticsTitle: 'Bildruteläsning', stableStatus: 'De två banorna förblir samordnade för detta mönster.', variableStatus: 'Den variabla banan går före eftersom bildrutornas längder innehåller spikarna.', fixedStatus: 'Den fasta banan kommer ikapp med flera simuleringssteg efter en lång bildruta.', clampStatus: 'Gränsen minskar det variabla hoppet och lämnar modellen efter den förflutna verkliga tiden.', timelineTitle: 'Rörelseloggen', timelineCaption: 'Position över förfluten verklig tid. En streckad linje markerar lika fasta steg.', frameAxis: 'Förfluten verklig tid', positionAxis: 'Objektets position', legendVariable: 'Variabel delta-bana', legendFixed: 'Fast timestep-bana', legendSpike: 'Bildrute-spik', tableTitle: 'Tillgänglig bildruteloggen', tableFrame: 'Bildruta', tableWall: 'Verklig tid', tableVariable: 'Variabel position', tableFixed: 'Fast position', tableDelta: 'Skillnad', modelNote: 'Den variabla modellen använder aktuell bildrutelängd för att tillämpa hastigheten en gång. Den fasta modellen samlar all verklig tid och rör sig i lika steg. Ingen bana är en prestandamätning.', privacyDisclosure: 'Dina värden sparas bara i den här webbläsaren så att experimentet är redo när du återkommer. Inga speldata eller telemetri skickas.', unitMs: 'ms', unitSeconds: 's', unitPixels: 'enheter', statusReady: 'Experimentet uppdaterades' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: 'Förstå klockan i en spel-loop' },
|
|
29
|
+
{ type: 'paragraph', html: 'Varje renderad bildruta ger spelet en ny förfluten tid. I en loop med variabel delta uppdateras rörelse ofta som <code>position += velocity × frameTime</code>. När bildrutorna är stabila ligger medelhastigheten nära den avsedda, men en enskild uppdatering kan bli lika stor som bildrutan som skapade den. En hitch är därför inte bara en visuell paus: den ändrar hur mycket speltid uppdateringen förbrukar.' },
|
|
30
|
+
{ type: 'title', level: 2, text: 'Jämför variabel integration med en fast ackumulator' },
|
|
31
|
+
{ type: 'paragraph', html: 'En loop med fast timestep lägger varje bildrutelängd i en ackumulator och förbrukar sedan upprepade gånger ett valt steg som <code>16,667 ms</code>. Simuleringen får lika stora uppdateringar medan renderingen kan hålla en annan rytm. Den återstående bråkdelen ligger kvar till nästa bildruta. När en lång bildruta kommer utför den fasta loopen flera små uppdateringar för att täcka den förflutna tiden. Steget förblir stabilt, men ikapparbetet kan ge en belastningstopp.' },
|
|
32
|
+
{ type: 'table', headers: ['Situation', 'Variabel delta-bana', 'Fast timestep-bana', 'Beslut att överväga'], rows: [['Stabil rendering', 'En uppdatering använder varje normal bildrutelängd', 'Lika steg förbrukar den ackumulerade tiden', 'Båda bör följa samma rörelse nära.'], ['Lång bildruta', 'En stor uppdatering kan flytta objektet för långt', 'Flera fasta uppdateringar kommer ikapp tiden', 'Läs drift och antal ikappsteg tillsammans.'], ['Annan renderingsfrekvens', 'Uppdateringens storlek ändras med frekvensen', 'Simuleringssteget förblir samma', 'Fasta steg minskar beroendet av rendering.'], ['Delta-tak', 'Tid över taket ignoreras', 'Ackumulatorn får hela bildrutans längd', 'Begränsa bara när förlorad tid är acceptabel.']] },
|
|
33
|
+
{ type: 'title', level: 2, text: 'Se vad en bildrute-spik faktiskt gör' },
|
|
34
|
+
{ type: 'paragraph', html: 'Vid 60 bildrutor per sekund tar en normal bildruta ungefär <code>16,667 ms</code>. Med en extra spik på <code>80 ms</code> blir en bildruta ungefär <code>96,667 ms</code>. Den variabla modellen förbrukar hela längden i en uppdatering. Den fasta modellen förbrukar i stället ungefär sex steg på <code>16,667 ms</code>. Den totala förflutna tiden kan stämma, även om vägen genom simuleringen skiljer sig.' },
|
|
35
|
+
{ type: 'title', level: 2, text: 'Läs positionsdrift och ikappsteg tillsammans' },
|
|
36
|
+
{ type: 'paragraph', html: 'Positionsskillnaden är den variabla banan minus den fasta banan. Den visar hur långt de två integrationsreglerna har separerat, inte vilken som automatiskt är rätt. Antalet ikappsteg visar hur mycket fast arbete som krävdes i en enda renderad bildruta. En stor skillnad pekar på synlig rörelseavvikelse; många ikappsteg pekar på ett möjligt CPU-budgetproblem. Signalerna hänger ihop, men är inte samma diagnos.' },
|
|
37
|
+
{ type: 'title', level: 2, text: 'Behandla delta-begränsning som en policy' },
|
|
38
|
+
{ type: 'paragraph', html: 'En gräns kan hindra en pausad flik, breakpoint eller allvarlig hitch från att teleportera en figur eller skicka en fysikkropp genom geometri. Priset är att den variabla klockan hamnar efter verkligt förfluten tid. Om spelet måste bevara tid bör du använda en fast ackumulator eller en annan återhämtningspolicy. Om spelet måste begränsa hopp och förbli responsivt kan en gräns fungera, men den förlorade tiden ska vara avsiktlig.' },
|
|
39
|
+
{ type: 'title', level: 2, text: 'Välj loopen efter arbetet den skyddar' },
|
|
40
|
+
{ type: 'table', headers: ['Arbete i spel-loopen', 'Användbar standard', 'Varför'], rows: [['Fysik, kollisioner eller deterministiskt gameplay', 'Fast timestep', 'Lika steg gör simuleringen mindre beroende av renderingsrytmen.'], ['Enkel visuell rörelse utan ackumulerat tillstånd', 'Variabel delta', 'Uppdateringen är liten och behöver oftast ingen ikappkö.'], ['Gameplay-simulering med mjuk rendering', 'Fast uppdatering med interpolation', 'Simuleringen behåller steget medan presentationen döljer bråkdelen.'], ['Återhämtning efter ett kraftigt stopp', 'Begränsad ikappolicy', 'Ett dåligt frame ska inte skapa obegränsat simuleringsarbete.']] },
|
|
41
|
+
{ type: 'title', level: 2, text: 'Följ ett upprepningsbart justeringsflöde' },
|
|
42
|
+
{ type: 'paragraph', html: 'Börja utan spikar och kontrollera att banorna sammanfaller. Lägg till en upprepningsbar hitch och ändra sedan intervallet för att se om det synliga felet växer eller stabiliseras. Justera det fasta steget och kontrollera antalet ikappsteg innan du ändrar hastigheten. Aktivera till sist gränsen och jämför variabel simulerad tid med verklig tid. Ordningen skiljer orsaken till drift från policyn som ska begränsa den.' },
|
|
43
|
+
{ type: 'tip', title: 'Vad experimentet inte kan bevisa', html: 'Laboratoriet använder angivna bildrutelängder och konstant hastighet. Det förklarar tidsbeteende, men mäter ingen enhet och validerar ingen motor. Verklig fysik, inmatningssampling, nätverk, interpolation och bildrutebudget kan ändra bästa design. Använd resultatet som en hypotes och kontrollera den i det faktiska spelet.' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: 'Referenser om spel-loopar', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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: 'Bu sabit timestep laboratuvarı neyi gösterir?', answer: 'Aynı hareketli nesneyi değişken delta time döngüsünden ve sabit timestep akümülatöründen geçirir. Tekrarlanabilir frame sıçramaları, bir oyun motoru çalıştırmadan simülasyon zamanını, konum farkını ve yetişme adımlarını görünür kılar.' },
|
|
8
|
+
{ question: 'Değişken delta ile sabit timestep arasındaki fark nedir?', answer: 'Değişken model, oluşturulan her frame için frame süresini kullanarak bir kez ilerler. Sabit model eşit simülasyon adımlarıyla ilerler ve geçen gerçek zamanın gerektirdiği kadar adımı bir akümülatör üzerinden işler. Sabit adımlar render hızına bağımlılığı azaltır, ancak uzun bir frame ek yetişme işi oluşturabilir.' },
|
|
9
|
+
{ question: 'Frame sıçraması girdisi neyi temsil eder?', answer: 'Seçilen her sıçrama frame ine eklenecek milisaniyeleri temsil eder. Sıklık alanı sıçramanın kaç frame de bir görüleceğini belirler, böylece rastgele bir performans kaydı yerine tekrarlanabilir bir hitch deseni oluşturabilirsiniz.' },
|
|
10
|
+
{ question: 'Delta sınırı neyi değiştirir?', answer: 'Bir frame seçilen tavanı aştığında değişken modelin kullandığı delta yı sınırlar. Bu, aşırı büyük bir sıçramayı önleyebilir, fakat bu modelin simülasyon saati gerçek zamanın gerisinde kalır. Sabit akümülatör frame in tamamını hesaplamaya devam eder.' },
|
|
11
|
+
{ question: 'Bu bir profiler mı, oyun testinin yerini tutar mı?', answer: 'Hayır. Belirlenmiş sayılarla çalışan deterministik bir öğrenme ve tasarım laboratuvarıdır. Cihazınızı ölçmez, bir motoru teşhis etmez, render maliyetini modellemez ve tek bir döngünün her oyun için doğru olduğunu kanıtlamaz.' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: 'Frame ritmini seçin', text: 'Hedef saniyedeki frame sayısını ayarlayın ve milisaniye cinsinden tekrarlanabilir bir sıçrama ekleyin. Sıklık alanı sıçramayı seçilen frame aralığına yerleştirir.' },
|
|
15
|
+
{ name: 'Simülasyon adımını ayarlayın', text: 'Sabit timestep değerini milisaniye cinsinden seçin. 16,667 milisaniyeye yakın bir değer saniyede yaklaşık 60 simülasyon güncellemesini temsil eder.' },
|
|
16
|
+
{ name: 'İki modele aynı hareket kuralını verin', text: 'Hızı ve süreyi belirleyin. İki model de aynı nesneyi hareket ettirdiği için konum farkı geçen zamanı tüketme biçimlerinden doğar.' },
|
|
17
|
+
{ name: 'Bir sınırı bilinçli şekilde test edin', text: 'Delta sınırını açın ve değişken simülasyon zamanını sabit akümülatörle karşılaştırın. Sınır, maliyeti görünen bir politika tercihidir; ücretsiz bir düzeltme değildir.' },
|
|
18
|
+
{ name: 'Kanıtı okuyun', text: 'İki yolu, fark özetini, zaman çizelgesini, yetişme davranışını ve erişilebilir frame tablosunu inceleyin. Öğretirken veya hata ayıklarken her seferinde tek bir girdiyi değiştirin.' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Oyun delta time ve sabit timestep laboratuvarı', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Değişken delta time ile sabit timestep i karşılaştırma', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'oyun-delta-time-ve-sabit-timestep-laboratuvari', title: 'Oyun Delta Time ve Sabit Timestep Laboratuvarı', description: 'Tekrarlanabilir frame sıçramaları, simüle hareket, yetişme adımları ve görünür zaman farkıyla değişken ve sabit oyun döngülerini karşılaştırın.',
|
|
26
|
+
ui: { controlsTitle: 'Frame desenini oluşturun', fpsLabel: 'Hedef frame hızı', fpsHint: 'Saniyede oluşturulan frame', spikeLabel: 'Frame sıçraması', spikeHint: 'Sıçrama framelerine eklenen süre', spikeEveryLabel: 'Sıçrama aralığı', fixedDtLabel: 'Sabit timestep', fixedDtHint: 'Eşit simülasyon adımı boyutu', velocityLabel: 'Nesne hızı', durationLabel: 'Çalışma süresi', clampLabel: 'Delta tavanı', clampToggle: 'Değişken delta yı sınırla', runLabel: 'Deneyi çalıştır', resetLabel: 'Değerleri sıfırla', stageKicker: 'Deney alanı', stageTitle: 'İki saat, tek hareketli nesne', stageCaption: 'Çubuklar hitch leri, çizgiler iki konumu gösterir; alt iz, son değerlerin saklayabileceği farkı ayırır.', frameTraceLabel: 'Frame süreleri', positionPlotLabel: 'Nesne konumu', differencePlotLabel: 'Sabit yola göre konum farkı', variableLane: 'Değişken delta', fixedLane: 'Sabit akümülatör', frameCountLabel: 'Oluşturulan frame', wallTimeLabel: 'Gerçek zaman', variableTimeLabel: 'Değişken simülasyon zamanı', fixedTimeLabel: 'Sabit simülasyon zamanı', divergenceLabel: 'Konum farkı', stepsLabel: 'Sabit simülasyon adımları', diagnosticsTitle: 'Frame okuması', stableStatus: 'Bu desende iki yol hizalı kalıyor.', variableStatus: 'Frame süreleri sıçramaları içerdiği için değişken yol öne geçiyor.', fixedStatus: 'Uzun bir frame den sonra sabit yol birden fazla simülasyon adımıyla yetişiyor.', clampStatus: 'Sınır değişken sıçramayı azaltıyor ve bu modeli geçen gerçek zamanın gerisinde bırakıyor.', timelineTitle: 'Hareket kaydı', timelineCaption: 'Geçen gerçek zamana göre konum. Kesikli çizgi eşit sabit adımları gösterir.', frameAxis: 'Geçen gerçek zaman', positionAxis: 'Nesne konumu', legendVariable: 'Değişken delta yolu', legendFixed: 'Sabit timestep yolu', legendSpike: 'Frame sıçraması', tableTitle: 'Erişilebilir frame kaydı', tableFrame: 'Frame', tableWall: 'Gerçek zaman', tableVariable: 'Değişken konum', tableFixed: 'Sabit konum', tableDelta: 'Fark', modelNote: 'Değişken model, geçerli frame süresiyle hızı bir kez uygular. Sabit model tüm gerçek zamanı biriktirir ve eşit adımlarla ilerler. Hiçbir yol performans ölçümü değildir.', privacyDisclosure: 'Değerleriniz yalnızca bu tarayıcıda saklanır, böylece deney geri döndüğünüzde hazır olur. Oyun verisi veya telemetri gönderilmez.', unitMs: 'ms', unitSeconds: 'sn', unitPixels: 'birim', statusReady: 'Deney güncellendi' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: 'Bir oyun döngüsündeki saati anlayın' },
|
|
29
|
+
{ type: 'paragraph', html: 'Oluşturulan her frame oyuna yeni bir geçen zaman değeri verir. Değişken delta döngüsünde hareket genellikle <code>position += velocity × frameTime</code> şeklinde güncellenir. Frame ler düzenliyken ortalama hız hedefe yakın kalır, ancak tek bir güncelleme onu üreten frame kadar büyük olabilir. Bu yüzden hitch yalnızca görsel bir duraklama değildir: o güncellemenin tükettiği oyun zamanını değiştirir.' },
|
|
30
|
+
{ type: 'title', level: 2, text: 'Değişken entegrasyonu sabit akümülatörle karşılaştırın' },
|
|
31
|
+
{ type: 'paragraph', html: 'Sabit timestep döngüsü her frame süresini bir akümülatöre ekler, ardından <code>16,667 ms</code> gibi seçilen bir adımı tekrar tekrar tüketir. Simülasyon eşit güncellemeler alırken renderer farklı bir ritimde çalışabilir. Kalan kesir bir sonraki frame için akümülatörde kalır. Uzun bir frame geldiğinde sabit döngü, geçen zamanı işlemek için birkaç küçük güncelleme yapar. Adım boyutu sabit kalır, ancak yetişme işi bir yük artışı oluşturabilir.' },
|
|
32
|
+
{ type: 'table', headers: ['Durum', 'Değişken delta yolu', 'Sabit timestep yolu', 'Düşünülecek karar'], rows: [['Düzenli render', 'Bir güncelleme her normal frame süresini kullanır', 'Eşit adımlar biriken zamanı tüketir', 'İki yolun aynı hareketi izlediğini kontrol edin.'], ['Uzun frame', 'Büyük bir güncelleme nesneyi fazla ileri taşıyabilir', 'Birden çok sabit güncelleme zamanı yakalar', 'Farkı yetişme adımı sayısıyla birlikte okuyun.'], ['Farklı render hızı', 'Güncelleme boyutu frame hızıyla değişir', 'Simülasyon adımı aynı kalır', 'Sabit adımlar render bağımlılığını azaltır.'], ['Delta tavanı', 'Tavanın üzerindeki zaman yok sayılır', 'Akümülatör frame in tamamını alır', 'Sadece zaman kaybı kabul edilebilirse sınır koyun.']] },
|
|
33
|
+
{ type: 'title', level: 2, text: 'Bir frame sıçramasının gerçek etkisini görün' },
|
|
34
|
+
{ type: 'paragraph', html: 'Saniyede 60 frame hızında normal bir frame yaklaşık <code>16,667 ms</code> sürer. Buna <code>80 ms</code> eklenirse tek bir frame yaklaşık <code>96,667 ms</code> olur. Değişken model bu sürenin tamamını tek güncellemede tüketir. Sabit model ise yaklaşık altı adet <code>16,667 ms</code> adımı tüketir. Toplam geçen zaman aynı olabilir, fakat simülasyondaki yol farklıdır.' },
|
|
35
|
+
{ type: 'title', level: 2, text: 'Konum farkını yetişme işiyle birlikte okuyun' },
|
|
36
|
+
{ type: 'paragraph', html: 'Konum farkı değişken yol eksi sabit yoldur. İki entegrasyon politikasının ne kadar ayrıldığını gösterir, ancak otomatik olarak hangisinin doğru olduğunu söylemez. Yetişme sayısı tek bir render frame inde gereken sabit iş miktarını gösterir. Büyük fark görünür hareket sapmasına, çok sayıda adım ise olası CPU bütçesi sorununa işaret eder. İlişkilidirler, fakat aynı teşhis değildir.' },
|
|
37
|
+
{ type: 'title', level: 2, text: 'Delta sınırlamasını bir politika olarak ele alın' },
|
|
38
|
+
{ type: 'paragraph', html: 'Sınır, duraklatılmış bir sekmenin, breakpoint in veya ağır bir hitch in karakteri ışınlamasını ya da fizik gövdesini geometriden geçirmesini önleyebilir. Bedeli, değişken saatin geçen gerçek zamanın gerisinde kalmasıdır. Oyun zamanı korumalıysa sabit akümülatör veya başka bir kurtarma politikası kullanın. Sıçramalar sınırlı ve oyun duyarlı kalmalıysa sınır uygun olabilir, ancak kaybedilen zaman bilinçli bir seçim olmalıdır.' },
|
|
39
|
+
{ type: 'title', level: 2, text: 'Döngüyü koruduğu işe göre seçin' },
|
|
40
|
+
{ type: 'table', headers: ['Oyun döngüsü işi', 'Yararlı varsayılan', 'Nedeni'], rows: [['Fizik, çarpışma veya deterministik oynanış', 'Sabit timestep', 'Eşit adımlar simülasyonu render ritmine daha az bağımlı kılar.'], ['Birikmiş durumu olmayan basit görsel hareket', 'Değişken delta', 'Güncelleme küçüktür ve genellikle yetişme kuyruğu gerekmez.'], ['Akıcı görüntülü oynanış simülasyonu', 'Enterpolasyonlu sabit güncelleme', 'Simülasyon adımını korur, gösterim ise kesirli ilerlemeyi gizler.'], ['Ciddi duraklama sonrası toparlanma', 'Sınırlı yetişme politikası', 'Kötü bir frame in sınırsız simülasyon işi üretmesini önler.']] },
|
|
41
|
+
{ type: 'title', level: 2, text: 'Tekrarlanabilir bir ayar akışı kullanın' },
|
|
42
|
+
{ type: 'paragraph', html: 'Sıçrama olmadan başlayın ve iki yolun aynı olduğunu doğrulayın. Tekrarlanabilir bir hitch ekleyin, ardından görünür hatanın birikip birikmediğini veya dengelenip dengelenmediğini görmek için sıklığını değiştirin. Hızı değiştirmeden önce sabit adımı ayarlayın ve yetişme sayısını inceleyin. Son olarak sınırı açın ve değişken simülasyon zamanını gerçek zamanla karşılaştırın. Bu sıra, farkın nedenini onu sınırlayan politikadan ayırır.' },
|
|
43
|
+
{ type: 'tip', title: 'Bu deney neyi kanıtlayamaz?', html: 'Laboratuvar belirlenmiş frame süreleri ve sabit hız kullanır; zaman davranışını açıklar ama bir cihazı ölçmez ve bir motoru doğrulamaz. Gerçek fizik, girdi örnekleme, ağ, enterpolasyon ve frame bütçesi en iyi tasarımı değiştirebilir. Sonucu bir hipotez olarak kullanın ve gerçek oyunda doğrulayın.' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: 'Oyun döngüsü kaynakları', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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: '这个固定时间步实验室展示什么?', answer: '它让同一个移动物体分别通过可变 delta time 循环和固定 timestep 累加器。加入可重复的帧尖峰后,可以在不运行游戏引擎的情况下观察模拟时间、位置偏差和追赶步骤。' },
|
|
8
|
+
{ question: '可变 delta 和固定 timestep 有什么区别?', answer: '可变模型在每个渲染帧更新一次,并使用该帧的时长。固定模型使用大小相同的模拟步骤,通过累加器处理实际经过的时间所需要的步骤。固定步骤减少模拟对渲染帧率的依赖,但长帧可能造成额外的追赶工作。' },
|
|
9
|
+
{ question: '帧尖峰输入代表什么?', answer: '它会给每个选中的尖峰帧增加额外的毫秒数。频率字段决定每隔多少帧出现一次尖峰,因此可以创建可重复的卡顿模式,而不是依赖随机的性能记录。' },
|
|
10
|
+
{ question: '限制 delta 会改变什么?', answer: '当帧时间超过设定上限时,限制会减少可变模型使用的 delta。这可以避免一次过大的跳跃,但该模型的模拟时钟会落后于实际时间。固定累加器仍然会计算完整的帧时长。' },
|
|
11
|
+
{ question: '这是性能分析器或游戏测试的替代品吗?', answer: '不是。这是一个使用指定数字的确定性学习和设计实验室。它不会测量设备、诊断引擎、模拟渲染成本,也不能证明某种循环适用于所有游戏。' },
|
|
12
|
+
];
|
|
13
|
+
const howTo = [
|
|
14
|
+
{ name: '选择帧节奏', text: '设置目标每秒帧数,并添加可重复的毫秒级尖峰。使用频率决定每隔多少帧放置一次尖峰。' },
|
|
15
|
+
{ name: '设置模拟步骤', text: '选择固定 timestep 的毫秒数。接近 16.667 毫秒的值代表每秒约 60 次模拟更新。' },
|
|
16
|
+
{ name: '给两个模型相同的运动规则', text: '设置速度和时长。两个模型移动同一个物体,因此位置差异来自它们消耗经过时间的方式。' },
|
|
17
|
+
{ name: '有意识地测试上限', text: '启用 delta 上限,比较可变模型的模拟时间与固定累加器。上限是带有明显代价的策略,不是免费的修复。' },
|
|
18
|
+
{ name: '阅读证据', text: '查看两条路径、偏差摘要、时间线、追赶行为和无障碍帧表。教学或调试时,一次只改变一个输入。' },
|
|
19
|
+
];
|
|
20
|
+
const softwareApplication: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: '游戏 Delta Time 与固定 Timestep 实验室', applicationCategory: 'DeveloperApplication', operatingSystem: 'Any' };
|
|
21
|
+
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 } })) };
|
|
22
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: '比较可变 Delta Time 与固定 Timestep', step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
23
|
+
|
|
24
|
+
export const content: ToolLocaleContent<GameDeltaTimeFixedTimestepLabUI> = {
|
|
25
|
+
slug: 'game-delta-time-fixed-timestep-lab', title: '游戏 Delta Time 与固定 Timestep 实验室', description: '通过可重复的帧尖峰、移动模拟、追赶步骤和可见的时间偏差,比较可变与固定的游戏循环。',
|
|
26
|
+
ui: { controlsTitle: '构建帧模式', fpsLabel: '目标帧率', fpsHint: '每秒渲染帧数', spikeLabel: '帧尖峰', spikeHint: '尖峰帧增加的额外时间', spikeEveryLabel: '尖峰间隔帧数', fixedDtLabel: '固定时间步', fixedDtHint: '相等的模拟步骤大小', velocityLabel: '物体速度', durationLabel: '运行时长', clampLabel: 'Delta 上限', clampToggle: '限制可变 Delta', runLabel: '运行实验', resetLabel: '重置数值', stageKicker: '实验场', stageTitle: '两只时钟,一个移动物体', stageCaption: '柱形图显示卡顿,曲线跟踪两个位置,下方曲线单独显示最终数值可能隐藏的偏差。', frameTraceLabel: '帧时长', positionPlotLabel: '物体位置', differencePlotLabel: '相对固定路径的位置差', variableLane: '可变 Delta', fixedLane: '固定累加器', frameCountLabel: '渲染帧数', wallTimeLabel: '实际时间', variableTimeLabel: '可变模拟时间', fixedTimeLabel: '固定模拟时间', divergenceLabel: '位置差', stepsLabel: '固定模拟步骤', diagnosticsTitle: '帧读取', stableStatus: '在这个模式下两条路径保持一致。', variableStatus: '可变路径向前,因为它的帧时长包含尖峰。', fixedStatus: '长帧之后,固定路径通过多个模拟步骤追赶时间。', clampStatus: '上限减少可变跳跃,并使该模型落后于已经经过的实际时间。', timelineTitle: '运动记录', timelineCaption: '相对于经过的实际时间的位置。虚线表示相等的固定步骤。', frameAxis: '经过的实际时间', positionAxis: '物体位置', legendVariable: '可变 Delta 路径', legendFixed: '固定 Timestep 路径', legendSpike: '帧尖峰', tableTitle: '无障碍帧记录', tableFrame: '帧', tableWall: '实际时间', tableVariable: '可变位置', tableFixed: '固定位置', tableDelta: '差值', modelNote: '可变模型使用当前帧时长将速度应用一次。固定模型累加完整实际时间,并以相等步骤前进。两条路径都不是性能测量。', privacyDisclosure: '你的数值只保存在此浏览器中,方便下次继续实验。不会发送游戏数据或遥测信息。', unitMs: '毫秒', unitSeconds: '秒', unitPixels: '单位', statusReady: '实验已更新' },
|
|
27
|
+
seo: [
|
|
28
|
+
{ type: 'title', level: 2, text: '理解游戏循环里的时钟' },
|
|
29
|
+
{ type: 'paragraph', html: '每个渲染帧都会向游戏提供新的经过时间。在可变 delta 循环中,运动通常按 <code>position += velocity × frameTime</code> 更新。帧稳定时,平均速度接近目标值,但一次更新也可能和产生它的帧一样长。因此 hitch 不只是画面暂停:它会改变这次更新消耗的游戏时间。' },
|
|
30
|
+
{ type: 'title', level: 2, text: '比较可变积分与固定累加器' },
|
|
31
|
+
{ type: 'paragraph', html: '固定 timestep 循环把每个帧时长加入累加器,然后反复消耗选定的步骤,例如 <code>16.667 ms</code>。模拟得到大小相同的更新,而渲染器可以保持不同节奏。剩余的小数时间留在累加器中,供下一帧使用。长帧到来时,固定循环执行多个小更新来覆盖经过的时间。步骤大小保持稳定,但可能出现追赶工作峰值。' },
|
|
32
|
+
{ type: 'table', headers: ['情况', '可变 Delta 路径', '固定 Timestep 路径', '需要考虑的决定'], rows: [['稳定渲染', '一次更新使用每个正常帧时长', '相同步骤消耗累积时间', '确认两条路径是否跟随相同运动。'], ['长帧', '一次大更新可能让物体移动过远', '多个固定更新追赶经过的时间', '同时查看位置差与追赶步骤数量。'], ['不同渲染速度', '更新大小随帧率变化', '模拟步骤保持不变', '固定步骤减少对渲染节奏的依赖。'], ['Delta 上限', '忽略超过上限的时间', '累加器接收完整帧时长', '只有可以接受时间损失时才限制。']] },
|
|
33
|
+
{ type: 'title', level: 2, text: '查看帧尖峰的实际影响' },
|
|
34
|
+
{ type: 'paragraph', html: '每秒 60 帧时,普通帧约为 <code>16.667 ms</code>。增加 <code>80 ms</code> 尖峰后,一帧约为 <code>96.667 ms</code>。可变模型在一次更新中消耗全部时长。固定模型则大约消耗六个 <code>16.667 ms</code> 步骤。总经过时间可能相同,但模拟走过的路径不同。' },
|
|
35
|
+
{ type: 'title', level: 2, text: '结合位置偏差和追赶步骤阅读结果' },
|
|
36
|
+
{ type: 'paragraph', html: '位置差等于可变路径减去固定路径。它说明两种积分策略分离了多远,但不会自动说明哪一种正确。追赶数量说明一个渲染帧中需要多少固定工作。较大的位置差意味着可见的运动差异,较多的追赶步骤则可能意味着 CPU 预算问题。两者相关,却不是同一个诊断。' },
|
|
37
|
+
{ type: 'title', level: 2, text: '把 Delta 限制当作策略' },
|
|
38
|
+
{ type: 'paragraph', html: '上限可以防止暂停的标签页、断点或严重 hitch 让角色瞬移,或让物理物体穿过几何体。代价是可变时钟会落后于实际经过的时间。如果游戏必须保留时间,应使用固定累加器或其他恢复策略。如果游戏必须限制跳跃并保持响应,上限可能合适,但丢失的时间必须是有意选择的。' },
|
|
39
|
+
{ type: 'title', level: 2, text: '根据需要保护的工作选择循环' },
|
|
40
|
+
{ type: 'table', headers: ['游戏循环任务', '有用的默认选择', '原因'], rows: [['物理、碰撞或确定性游戏玩法', '固定 Timestep', '相同步骤让模拟更少依赖渲染节奏。'], ['不积累状态的简单视觉运动', '可变 Delta', '更新很小,通常不需要追赶队列。'], ['带平滑渲染的游戏模拟', '带插值的固定更新', '模拟保持步骤,显示层隐藏小数进度。'], ['严重停顿后的恢复', '有上限的追赶策略', '避免一次坏帧制造无限的模拟工作。']] },
|
|
41
|
+
{ type: 'title', level: 2, text: '使用可重复的调节流程' },
|
|
42
|
+
{ type: 'paragraph', html: '先关闭尖峰,确认两条路径一致。加入可重复的 hitch,再改变频率,观察可见误差是累积还是稳定。调整固定步骤,查看追赶数量,然后再改变速度。最后启用上限,比较可变模拟时间与实际时间。这个顺序可以把偏差原因和限制偏差的策略分开。' },
|
|
43
|
+
{ type: 'tip', title: '这个实验无法证明什么', html: '实验室使用指定的帧时长和恒定速度,因此解释的是时间行为,而不是测量设备或验证引擎。真实物理、输入采样、网络、插值和帧预算可能改变最佳设计。把结果当作假设,再在实际游戏中验证。' },
|
|
44
|
+
],
|
|
45
|
+
faq, bibliographyTitle: '游戏循环参考资料', bibliography: bibliographyEntries, howTo, schemas: [softwareApplication, faqPage, howToSchema],
|
|
46
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../../types';
|
|
2
|
+
import { gameDeltaTimeFixedTimestepLab } from './entry';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
|
|
6
|
+
export const GAME_DELTA_TIME_FIXED_TIMESTEP_LAB_TOOL: ToolDefinition = {
|
|
7
|
+
entry: gameDeltaTimeFixedTimestepLab,
|
|
8
|
+
Component: () => import('./component.astro'),
|
|
9
|
+
SEOComponent: () => import('./seo.astro'),
|
|
10
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
11
|
+
};
|