286 lines
9.9 KiB
Markdown
286 lines
9.9 KiB
Markdown
# MyGamePlayable — Project Instructions
|
|
|
|
## Context rules
|
|
|
|
This file contains persistent project rules and non-obvious constraints, not history.
|
|
|
|
**Keep this file under 200 lines.**
|
|
|
|
Add only:
|
|
|
|
* rules that affect future implementation decisions;
|
|
* non-obvious SDK/library/toolchain pitfalls;
|
|
* structural conventions;
|
|
* public SDK APIs that are not obvious from code.
|
|
|
|
Do not add:
|
|
|
|
* development history, dates, or completed tasks;
|
|
* test/build results;
|
|
* current tuning values;
|
|
* information obvious from nearby code;
|
|
* temporary project state.
|
|
|
|
If knowledge is specific to one subsystem, prefer `docs/<topic>.md`.
|
|
If it explains why a decision was made, put the detailed history in `docs/worklog/`.
|
|
|
|
When a pitfall is fixed by a utility, replace the explanation with one short rule
|
|
pointing to that utility.
|
|
|
|
Worklog files:
|
|
`docs/worklog/NN-short-english-name.md`
|
|
Update `docs/worklog/README.md` when adding one.
|
|
|
|
---
|
|
|
|
## Stack
|
|
|
|
* Three.js `0.185` via `@hitplay/playable_template` + `@hitplay/ads_common`.
|
|
* Physics: `cannon-es`.
|
|
* Tweens: `@tweenjs/tween.js` via SDK `TweenC`.
|
|
* VFX: `three.quarks`, designer `.json` prefabs.
|
|
* Audio: `howler`.
|
|
* Input: `nipplejs`.
|
|
* Debug UI: `lil-gui`.
|
|
* Analytics: `bytebrew-web-sdk`.
|
|
* Toolchain: Vite 6 + TypeScript 7.
|
|
|
|
### Runtime rules
|
|
|
|
* `UpdateController.Instance.onUpdate` is the only update event. `delta` is seconds.
|
|
* SDK calls `world.fixedStep()` every frame via `Physics_internal.update`.
|
|
Never call `step()` / `fixedStep()` manually.
|
|
* Physics initialization: `Physics_internal.init(...)` in `beforeResourcesLoadedCb.ts`.
|
|
* Call `TweenC.init()` exactly once. It is already called in
|
|
`beforeResourcesLoadedCb.ts`.
|
|
* Use `ThreeC.removeFromScene(obj)` to remove spawned meshes.
|
|
|
|
---
|
|
|
|
## Commands
|
|
|
|
| Command | Purpose |
|
|
| ---------------- | --------------------------------------------------------- |
|
|
| `npm start` | Vite dev server |
|
|
| `npm run export` | Single-file HTML → `export/output_export_browser_en.html` |
|
|
| `npm run build` | 24ADS platform zip → `zip/` |
|
|
| `npm run update` | Update HitPlay SDK packages |
|
|
|
|
Verify changes with `npx vite build`.
|
|
|
|
Use `stats.html` when investigating bundle-size growth.
|
|
|
|
---
|
|
|
|
## Structure
|
|
|
|
```text
|
|
src/index.ts entry point / Template.initConfig()
|
|
src/controllers/*C.ts one system per file, flat structure
|
|
src/configUIParams/ 24ADS config categories
|
|
src/templateConfig/*Cb.ts SDK lifecycle callbacks
|
|
src/utils/ shared utilities
|
|
src/resources/ meshes/images/fonts/VFX/sounds
|
|
src/css/ main.css, ui.css, health-bar.css
|
|
docs/worklog/ development history
|
|
```
|
|
|
|
Do not create controller subdirectories. The SDK uses a flat structure and
|
|
there is no benefit in mirroring artificial subfolders.
|
|
|
|
---
|
|
|
|
## Utilities — reuse, don't reimplement
|
|
|
|
* `perspectiveScale.ts` — perspective compensation for flight toward/away from camera.
|
|
* `screenWorldProjection.ts` — world↔screen projection.
|
|
* `tweenChain.ts` — safe tween-chain registration.
|
|
* `flashOverlayMaterial.ts` — additive hit-flash overlay.
|
|
* `pickupFlight.ts` — shared drop → scatter → bounce → fly-to-target animation,
|
|
driven by both `ResourceC` (wood) and `ZombiePartC` (zombie-part chunks).
|
|
Never write a second one.
|
|
|
|
`HealthBarC` is shared by crates, zombies, and player. Do not create another
|
|
health-bar implementation.
|
|
|
|
Two weapon progressions (bat level from wood, pistol level from zombie-part
|
|
chunks), each with its own fixed Pay Zone (`PayZoneC`, instance-based — no
|
|
relocation), and crate respawn: see `docs/progression.md`.
|
|
|
|
`HealthBarOptions.variant` controls colors via `health-bar.css`.
|
|
`follow` re-samples the anchor every frame for moving owners.
|
|
Health bars appear on first hit and hide after `HIDE_DELAY_S`; full-HP entities
|
|
do not show a bar.
|
|
|
|
---
|
|
|
|
## Dependency injection
|
|
|
|
Controllers must not directly import each other when that would create a cycle.
|
|
Inject getters/callbacks instead and wire them from `TestSceneC`.
|
|
|
|
Existing DI examples:
|
|
|
|
* `ResourceC.setFlyTarget()`
|
|
* `HudC.setBalanceGetter()`
|
|
* `CameraFollowC.setFacingTargetGetter()`
|
|
* `BreakablePropC.onHit / onDestroyed / onRespawned`
|
|
* `ZombieC.init(bounds, getPlayerPosition, damagePlayer, isPlayerAlive)`
|
|
* `ZombieInvasionHudC.setOnComplete()`
|
|
* `ZombiePartC.setFlyTarget()` / `PistolHudC.setLevelGetter()`
|
|
|
|
`ZombieInvasionHudC.setOnComplete()` must be called **before** `init()`;
|
|
`init()` immediately starts its countdown.
|
|
|
|
---
|
|
|
|
## Platform config
|
|
|
|
24ADS renders config categories from `src/configUIParams/` plus SDK categories.
|
|
|
|
| Platform tab | Source |
|
|
| --------------------------------------- | ------------------------------ |
|
|
| Analytics params | `analytics.ts` |
|
|
| Install Banner Settings | `installBanner.ts` |
|
|
| Scene settings | `globalSettings.ts` (`global`) |
|
|
| Gameplay tuning | `gameplay.ts` |
|
|
| Audio Settings | `sounds.ts` |
|
|
| Screen Orientation | `orientation.ts` |
|
|
| Filter scene / Redirect / Loader / Font | SDK |
|
|
|
|
Read runtime values with:
|
|
|
|
```ts
|
|
Template.getCategory("<categoryId>")["<parameterId>"]
|
|
```
|
|
|
|
### Config format
|
|
|
|
`configUIParams` array parameters use `[min, max, default]` per axis.
|
|
Runtime `Template.getCategory()` returns the actual `[x, y, z]` value.
|
|
|
|
Never interpret `[min, max, default]` as runtime coordinates.
|
|
|
|
### Camera ownership
|
|
|
|
There must be exactly one writer of `camera.rotation`: `CameraFollowC`.
|
|
|
|
* `CameraC.setCamera()` owns FOV only.
|
|
* `CameraFollowC.applyRigFromConfig()` owns camera position/rotation.
|
|
* `camera_position_*` is a player-relative offset, not an absolute position.
|
|
* `camera_rotation_*` is a fixed angle in degrees.
|
|
* `camera_fov_*` is FOV.
|
|
* Do not write camera position/rotation from `CameraC.setCamera()`.
|
|
`setCamera()` can run again when any `camera_*` config changes.
|
|
* `TestSceneC.CAMERA_OFFSET` is fallback only.
|
|
|
|
### Gameplay tuning
|
|
|
|
`gameplay.ts` is the source for platform tuning defaults.
|
|
`DebugConfigC` mirrors these values locally in lil-gui (`C` key, dev-only).
|
|
The debug panel copies defaults once at startup; it does not live-sync.
|
|
|
|
---
|
|
|
|
## Critical SDK / runtime pitfalls
|
|
|
|
* GLTF loader removes dots from node names:
|
|
`UI_Wood.001` → `UI_Wood001`.
|
|
Inspect runtime names with `scene.traverse()`, not GLB JSON.
|
|
|
|
* GLB materials are shared between nodes. Clone a material before modifying
|
|
one instance.
|
|
|
|
* `MeshBasicMaterial.color` cannot produce a visible white→white hit flash.
|
|
Use `src/utils/flashOverlayMaterial.ts`.
|
|
|
|
* Zone checks must use `new Box3().setFromObject(node)`, not visual/radius estimates.
|
|
|
|
* Perspective changes apparent object size. Use `perspectiveScale.ts` when compensating
|
|
for flight toward/away from the camera.
|
|
|
|
* Flight targets must be re-sampled every frame because the camera follows the player.
|
|
|
|
* Do not rely solely on tween `onComplete` for critical cleanup.
|
|
Use a timeout fallback with a small safety margin.
|
|
`removeFromScene()` is safe to call twice.
|
|
|
|
* screen↔world projection must use
|
|
`renderer.domElement.getBoundingClientRect()`, never `window.innerWidth/Height`.
|
|
SDK uses a letterboxed 9:16 viewport.
|
|
|
|
* `nipplejs.force` can exceed the visual joystick radius.
|
|
Clamp it when storing input, not inside every calculation.
|
|
|
|
* `Math.atan2` and CSS `conic-gradient` use different zero directions.
|
|
Account for the 90° offset.
|
|
|
|
* Do not stack independent CSS animations by changing `animation-name` on the
|
|
same element. Use nested elements.
|
|
|
|
* To fade to a base color, animate RGB rather than alpha.
|
|
|
|
* Use `--lvh` / `--lvw`; avoid raw `vh` / `vw` / `vmin`.
|
|
|
|
---
|
|
|
|
## Export / CSS / fonts
|
|
|
|
* Export HTML is gzip+base64, not searchable source.
|
|
Never grep `export/*.html` to verify bundled content.
|
|
Inspect the decompressed payload instead.
|
|
|
|
* `.bannerButton` is purged from the release export.
|
|
Do not add CSS targeting it.
|
|
`.bannerLogo` and `.bannerButtonText` remain.
|
|
|
|
* `Template.setUpFont()` works only when `!release`.
|
|
It is for platform preview.
|
|
Release uses our own `@font-face` from `main.css`.
|
|
|
|
* Release images must use:
|
|
`ConvertToBase64WhenRelease("./file.webp")`
|
|
from the same directory as the asset.
|
|
|
|
* CSS fonts are inlined automatically by the configured `assetsInlineLimit`.
|
|
|
|
---
|
|
|
|
## Asset rules
|
|
|
|
`ZombiePunk_Map.glb`:
|
|
|
|
* `Lootable` contains approximately 18 `Wooden_Box_XXX` crates.
|
|
* Crates may start at S2/S3. `BreakablePropC` must derive initial HP from
|
|
actual `_SN` nodes; never assume three stages.
|
|
* `UI_Wood` is the resource icon and must be cloned per spawn.
|
|
* `UI_Interactive_Zone_02` is the pistol's Pay Zone marker (halved in scale);
|
|
the bat's own Pay Zone is a runtime clone of it, not a second authored node
|
|
— see `TestSceneC.createPayZoneNodes`.
|
|
* `UI_Tool_Zone`, `UI_Wood001`, and `UI` are hidden decorative nodes.
|
|
|
|
`ZombiePunk_Character.glb`:
|
|
|
|
* Existing clips include `Idle`, `SlowWalk`, `Loot`, `WeaponTakeOut`,
|
|
`PistolShoot`, `WeaponPutDown`, `Death`,
|
|
`PistolBack/Forward/Left/Right`.
|
|
* Do not invent character states before checking existing clips.
|
|
* `Character_Pistol` is a skinned mesh (`skin:0`), not a bone prop.
|
|
Skeleton animation moves it with the hand.
|
|
* Bat uses `Tool_1` / `Tool_2` visibility switching.
|
|
* `Bullet` is a separate hidden rig.
|
|
|
|
---
|
|
|
|
## Change discipline
|
|
|
|
* Prefer minimal, focused changes.
|
|
* Do not refactor unrelated code.
|
|
* Reuse existing utilities and controllers before creating new ones.
|
|
* Preserve the existing DI pattern.
|
|
* Before adding a new SDK workaround, check `src/utils/` and this file.
|
|
* When a new persistent rule is discovered, update this file only if it affects
|
|
future decisions; otherwise document it in the appropriate source file or
|
|
`docs/worklog/`.
|
|
|