Bug fix and UX improve #3
@@ -7,25 +7,80 @@
|
||||
"animations": [
|
||||
{
|
||||
"name": "idle",
|
||||
"fps": 6,
|
||||
"frames": [0, 1]
|
||||
"fps": 8,
|
||||
"frames": [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "happy",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "love",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "excited",
|
||||
"fps": 8,
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23]
|
||||
},
|
||||
{
|
||||
"name": "celebrate",
|
||||
"fps": 10,
|
||||
"frames": [1, 0]
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23],
|
||||
"one_shot": true
|
||||
},
|
||||
{
|
||||
"name": "success",
|
||||
"fps": 10,
|
||||
"frames": [0, 1, 0],
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23],
|
||||
"one_shot": true
|
||||
},
|
||||
{
|
||||
"name": "sleepy",
|
||||
"fps": 8,
|
||||
"frames": [24, 25, 26, 27, 28, 29, 30, 31]
|
||||
},
|
||||
{
|
||||
"name": "snoring",
|
||||
"fps": 8,
|
||||
"frames": [24, 25, 26, 27, 28, 29, 30, 31]
|
||||
},
|
||||
{
|
||||
"name": "working",
|
||||
"fps": 8,
|
||||
"frames": [32, 33, 34, 35, 36, 37, 38, 39]
|
||||
},
|
||||
{
|
||||
"name": "angry",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "surprised",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "shy",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"fps": 8,
|
||||
"frames": [1, 0, 1],
|
||||
"one_shot": true
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "dragging",
|
||||
"fps": 8,
|
||||
"frames": [48, 49, 50, 51, 52, 53, 54, 55]
|
||||
}
|
||||
],
|
||||
"anchor": {
|
||||
|
||||
@@ -7,25 +7,80 @@
|
||||
"animations": [
|
||||
{
|
||||
"name": "idle",
|
||||
"fps": 6,
|
||||
"frames": [0, 1]
|
||||
"fps": 8,
|
||||
"frames": [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "happy",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "love",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "excited",
|
||||
"fps": 8,
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23]
|
||||
},
|
||||
{
|
||||
"name": "celebrate",
|
||||
"fps": 10,
|
||||
"frames": [1, 0]
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23],
|
||||
"one_shot": true
|
||||
},
|
||||
{
|
||||
"name": "success",
|
||||
"fps": 10,
|
||||
"frames": [0, 1, 0],
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23],
|
||||
"one_shot": true
|
||||
},
|
||||
{
|
||||
"name": "sleepy",
|
||||
"fps": 8,
|
||||
"frames": [24, 25, 26, 27, 28, 29, 30, 31]
|
||||
},
|
||||
{
|
||||
"name": "snoring",
|
||||
"fps": 8,
|
||||
"frames": [24, 25, 26, 27, 28, 29, 30, 31]
|
||||
},
|
||||
{
|
||||
"name": "working",
|
||||
"fps": 8,
|
||||
"frames": [32, 33, 34, 35, 36, 37, 38, 39]
|
||||
},
|
||||
{
|
||||
"name": "angry",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "surprised",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "shy",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"fps": 8,
|
||||
"frames": [1, 0, 1],
|
||||
"one_shot": true
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "dragging",
|
||||
"fps": 8,
|
||||
"frames": [48, 49, 50, 51, 52, 53, 54, 55]
|
||||
}
|
||||
],
|
||||
"anchor": {
|
||||
|
||||
@@ -7,25 +7,80 @@
|
||||
"animations": [
|
||||
{
|
||||
"name": "idle",
|
||||
"fps": 6,
|
||||
"frames": [0, 1]
|
||||
"fps": 8,
|
||||
"frames": [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "happy",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "love",
|
||||
"fps": 8,
|
||||
"frames": [8, 9, 10, 11, 12, 13, 14, 15]
|
||||
},
|
||||
{
|
||||
"name": "excited",
|
||||
"fps": 8,
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23]
|
||||
},
|
||||
{
|
||||
"name": "celebrate",
|
||||
"fps": 10,
|
||||
"frames": [1, 0]
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23],
|
||||
"one_shot": true
|
||||
},
|
||||
{
|
||||
"name": "success",
|
||||
"fps": 10,
|
||||
"frames": [0, 1, 0],
|
||||
"frames": [16, 17, 18, 19, 20, 21, 22, 23],
|
||||
"one_shot": true
|
||||
},
|
||||
{
|
||||
"name": "sleepy",
|
||||
"fps": 8,
|
||||
"frames": [24, 25, 26, 27, 28, 29, 30, 31]
|
||||
},
|
||||
{
|
||||
"name": "snoring",
|
||||
"fps": 8,
|
||||
"frames": [24, 25, 26, 27, 28, 29, 30, 31]
|
||||
},
|
||||
{
|
||||
"name": "working",
|
||||
"fps": 8,
|
||||
"frames": [32, 33, 34, 35, 36, 37, 38, 39]
|
||||
},
|
||||
{
|
||||
"name": "angry",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "surprised",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "shy",
|
||||
"fps": 8,
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"fps": 8,
|
||||
"frames": [1, 0, 1],
|
||||
"one_shot": true
|
||||
"frames": [40, 41, 42, 43, 44, 45, 46, 47]
|
||||
},
|
||||
{
|
||||
"name": "dragging",
|
||||
"fps": 8,
|
||||
"frames": [48, 49, 50, 51, 52, 53, 54, 55]
|
||||
}
|
||||
],
|
||||
"anchor": {
|
||||
|
||||
@@ -1034,7 +1034,7 @@ fn default_animation_for_state(state: FrontendState) -> &'static str {
|
||||
FrontendState::Active => "active",
|
||||
FrontendState::Success => "success",
|
||||
FrontendState::Error => "error",
|
||||
FrontendState::Dragging => "idle",
|
||||
FrontendState::Dragging => "dragging",
|
||||
FrontendState::Hidden => "idle",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ fn default_animation_for_state(state: sprimo_protocol::v1::FrontendState) -> &'s
|
||||
sprimo_protocol::v1::FrontendState::Active => "active",
|
||||
sprimo_protocol::v1::FrontendState::Success => "success",
|
||||
sprimo_protocol::v1::FrontendState::Error => "error",
|
||||
sprimo_protocol::v1::FrontendState::Dragging => "idle",
|
||||
sprimo_protocol::v1::FrontendState::Dragging => "dragging",
|
||||
sprimo_protocol::v1::FrontendState::Hidden => "idle",
|
||||
}
|
||||
}
|
||||
@@ -284,6 +284,22 @@ mod tests {
|
||||
assert_eq!(snapshot.current_animation, "active");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dragging_state_maps_to_dragging_animation() {
|
||||
let temp = TempDir::new().expect("tempdir");
|
||||
let path = temp.path().join("config.toml");
|
||||
let core = RuntimeCore::new_with_config(path, AppConfig::default(), CapabilityFlags::default())
|
||||
.expect("core init");
|
||||
core.apply_command(&FrontendCommand::SetState {
|
||||
state: FrontendState::Dragging,
|
||||
ttl_ms: None,
|
||||
})
|
||||
.expect("apply");
|
||||
let snapshot = core.snapshot().read().expect("snapshot lock").clone();
|
||||
assert_eq!(snapshot.state, FrontendState::Dragging);
|
||||
assert_eq!(snapshot.current_animation, "dragging");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn click_through_flag_is_ignored_and_forced_false() {
|
||||
let temp = TempDir::new().expect("tempdir");
|
||||
|
||||
@@ -75,3 +75,20 @@ For `sprimo-tauri`, when manifest `image` is exactly `sprite.png`:
|
||||
- `frame_height = image_height / 7`
|
||||
- manifest `frame_width` and `frame_height` are ignored for this case.
|
||||
- animation frame indices are validated against the fixed grid frame count (`56`).
|
||||
|
||||
## Recommended 7x8 Row Semantics
|
||||
|
||||
For `sprite.png` packs using the fixed `8x7` topology, the project convention is:
|
||||
|
||||
- row 1 (`0..7`): `idle`
|
||||
- row 2 (`8..15`): `happy`, `love`, compatibility alias `active`
|
||||
- row 3 (`16..23`): `excited`, `celebrate`, compatibility alias `success`
|
||||
- row 4 (`24..31`): `sleepy`, `snoring`
|
||||
- row 5 (`32..39`): `working`
|
||||
- row 6 (`40..47`): `angry`, `surprised`, `shy`, compatibility alias `error`
|
||||
- row 7 (`48..55`): `dragging`
|
||||
|
||||
Default one-shot policy:
|
||||
|
||||
- `celebrate` and `success` are one-shot.
|
||||
- other row animations loop by default.
|
||||
|
||||
@@ -60,6 +60,14 @@ Frontend:
|
||||
from runtime snapshot events.
|
||||
- For `sprite.png` packs in tauri runtime, frame size is now derived from atlas dimensions with a
|
||||
fixed `8x7` grid topology to keep splitting stable across packaged asset resolutions.
|
||||
- `sprite.png` animation naming now follows row semantics with backward-compatible aliases:
|
||||
- row1: `idle`
|
||||
- row2: `happy`/`love` + alias `active`
|
||||
- row3: `excited`/`celebrate` + alias `success`
|
||||
- row4: `sleepy`/`snoring`
|
||||
- row5: `working`
|
||||
- row6: `angry`/`surprised`/`shy` + alias `error`
|
||||
- row7: `dragging`
|
||||
- React/Vite frontend now supports two window modes:
|
||||
- `main`: transparent overlay sprite renderer
|
||||
- `settings`: pop-out settings window for character and window controls
|
||||
|
||||
57
issues/issue6.md
Normal file
57
issues/issue6.md
Normal file
@@ -0,0 +1,57 @@
|
||||
## Title
|
||||
|
||||
Adopt row-based 7x8 sprite animation semantics with backward-compatible state aliases.
|
||||
|
||||
## Severity
|
||||
|
||||
P2
|
||||
|
||||
## Summary
|
||||
|
||||
Standardize `sprite.png` packs so each of the 7 rows maps to semantic animation groups, while
|
||||
keeping runtime compatibility with existing state names (`active`, `success`, `error`).
|
||||
|
||||
## Scope
|
||||
|
||||
- `assets/sprite-packs/{default,ferris,demogorgon}/manifest.json`
|
||||
- `crates/sprimo-runtime-core/src/lib.rs`
|
||||
- `crates/sprimo-app/src/main.rs`
|
||||
- docs:
|
||||
- `docs/SPRITE_PACK_SCHEMA.md`
|
||||
- `docs/TAURI_FRONTEND_DESIGN.md`
|
||||
|
||||
## Row Mapping Contract
|
||||
|
||||
- row 1 (`0..7`): `idle`
|
||||
- row 2 (`8..15`): `happy`, `love`, alias `active`
|
||||
- row 3 (`16..23`): `excited`, `celebrate`, alias `success`
|
||||
- row 4 (`24..31`): `sleepy`, `snoring`
|
||||
- row 5 (`32..39`): `working`
|
||||
- row 6 (`40..47`): `angry`, `surprised`, `shy`, alias `error`
|
||||
- row 7 (`48..55`): `dragging`
|
||||
|
||||
One-shot defaults:
|
||||
|
||||
- `celebrate` and `success`: one-shot
|
||||
- all others: loop
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
1. Runtime state mapping updated:
|
||||
- `Dragging` now maps to `"dragging"` instead of `"idle"` in runtime-core and Bevy frontend.
|
||||
2. All bundled sprite-pack manifests now expose row-based names and compatibility aliases.
|
||||
3. Added runtime-core unit test to confirm `SetState::Dragging` selects `"dragging"`.
|
||||
4. Updated schema/design docs to formalize the row convention.
|
||||
|
||||
## Verification
|
||||
|
||||
### Commands Run
|
||||
|
||||
- [x] `cargo test -p sprimo-runtime-core`
|
||||
- [x] `cargo check -p sprimo-tauri`
|
||||
- [x] `cargo check -p sprimo-app`
|
||||
|
||||
### Result
|
||||
|
||||
- Status: `Fix Implemented`
|
||||
- Notes: packaged runtime visual verification pending.
|
||||
Reference in New Issue
Block a user