Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ converted binaries (`.ssab`) instead of parsing `.sspj` at runtime. See the
same conversion for CI/CD.
- **Resource classes**: `SSABResource` (animation binary) and `SSQBResource` (sequence
binary). `.ssab` is loaded zero-copy, so playback starts without a parse step.
- **Per-part Override Layer API**: override a part's color, cell and visibility at runtime,
addressed by part name or part index, each with a matching `clear_*` call.
- **Per-part Override Layer API**: override a part's color (single or per-corner gradient),
cell and visibility at runtime, addressed by part name or part index, each with a matching
`clear_*` call.
- **CellMap overrides**: swap an animation's textures at runtime for equipment changes and
color variants.
color variants. Cell map / cell names are enumerable from the player and the resource.
- **`SpriteStudioPartAttachment2D` node**: mirrors one part's pose onto a `Node2D` so Godot
content can be pinned to a part. Modeled on `RemoteTransform2D`.
- **Signals**: timeline `user_data` and `signal_emitted` events, audio events, animation
Expand Down
6 changes: 0 additions & 6 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,6 @@ SSPlayerForGodot leverages Godot's `CanvasItem` API and `Node2D` paradigms. Feat
- **Blocked on**: SDK Phase 3 **Instance Lifecycle → Animation Instancing** (Shared evaluation context).
- **Task**: Once `ssruntime` supports computing `FrameData` once and rendering it N times, create a node (e.g. `SpriteStudioReplicate2D`) that binds to an original player's context and simply submits the evaluated batches with a different root `Transform2D`, saving Godot CPU time.

### 🕒 Animation blending / Crossfade (⛔ SDK Phase 3)

- **Goal**: Blend multiple animations or crossfade between them.
- **Blocked on**: SDK Phase 3 **Animation Mixing/Blending** and **State machine implementation**.
- **Task**: Surface the blending/crossfade FFI capabilities to `SpriteStudioPlayer2D`, allowing users to smoothly transition between animations or manage layered blending.

### 🕒 Dynamic instance swap (⛔ SDK Phase 3)

- **Goal**: Replace the animation mounted on an Instance part at runtime (e.g., for equipment or character variations).
Expand Down
2 changes: 2 additions & 0 deletions docs/en/api/player.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func _ready() -> void:
* `set_frame_skip_enabled(enabled: bool)` / `is_frame_skip_enabled() -> bool` (default: `true`)
* `set_sub_frame_enabled(enabled: bool)` / `is_sub_frame_enabled() -> bool` (default: `false`)
* `set_cellmap_texture(cellmap_name: String, texture: Texture2D)` / `get_cellmap_texture(cellmap_name: String) -> Texture2D`
* `get_cellmap_names() -> PackedStringArray` / `get_cell_names(cellmap_name: String) -> PackedStringArray`: Names read from the assigned `SSABResource` (empty when none is assigned) — the discovery half of `set_part_cell_override()`. Also available on [`SSABResource`](resource.md) itself for an `.ssab` that is not on a player.

### Arguments for `set_playback_direction`

Expand All @@ -67,6 +68,7 @@ See [Scripting and Event-Driven Control → Part Tracking](../workflow/usage_scr
Override a single part's color / cell / visibility so that it wins over the keyframes. Every method returns `true` on success, or `false` when the part is unknown or the runtime rejects the call. See [Scripting and Event-Driven Control → Part Overrides](../workflow/usage_scripting.md) for the details and caveats.

* `set_part_color_override(part_name: String, color: Color, blend_op: int = 0, priority: int = 1) -> bool`
* `set_part_color_override_corners(part_name: String, left_top: Color, right_top: Color, left_bottom: Color, right_bottom: Color, blend_op: int = 0, priority: int = 1) -> bool`: Four-corner (per-vertex) colour, for a gradient across the part. Shares one override slot with `set_part_color_override` — the last call wins, and `clear_part_color_override` clears either kind.
* `set_part_cell_override(part_name: String, cellmap_name: String, cell_name: String, priority: int = 1) -> bool`
* `set_part_visibility_override(part_name: String, force_hidden: bool, cascade: bool = false) -> bool`
* `clear_part_color_override(part_name: String) -> bool` / `clear_part_cell_override(part_name: String) -> bool` / `clear_part_visibility_override(part_name: String) -> bool`
Expand Down
16 changes: 11 additions & 5 deletions docs/en/workflow/usage_scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ func _ready():
# Tint a part red (multiply). Applies to normal (image) parts.
ss_player.set_part_color_override("body", Color.RED, 1) # 1 = Mul

# Or give each of the four corners its own color, for a gradient.
ss_player.set_part_color_override_corners(
"body", Color.RED, Color.RED, Color.BLUE, Color.BLUE, 0)

# Make a part draw a different cell (cell map name is written without ".ssce").
ss_player.set_part_cell_override("body", "Ringo", "effect3")

Expand All @@ -203,22 +207,24 @@ func _ready():
|---|---|
| `get_part_index(part_name)` | Part index, or `-1` if the part is not in the asset |
| `set_part_color_override(part_name, color, blend_op = 0, priority = 1)` | Color override (single color) |
| `set_part_color_override_corners(part_name, left_top, right_top, left_bottom, right_bottom, blend_op = 0, priority = 1)` | Color override with a distinct color per corner (gradient) |
| `set_part_cell_override(part_name, cellmap_name, cell_name, priority = 1)` | Draw a different cell |
| `set_part_visibility_override(part_name, force_hidden, cascade = false)` | Force-hide (`force_hidden = false` reverts to the animation) |
| `clear_part_color_override` / `clear_part_cell_override` / `clear_part_visibility_override` | Clear one override on one part |
| `clear_all_part_overrides()` | Clear every override on the player |
| `*_by_index(part_index, ...)` | Part-index variant of each method above (skips the name lookup) |

Every method returns `false` when the part is unknown or the runtime rejects the call.
Every method returns `false` when the part is unknown or the runtime rejects the call. A single color and a four-corner color share one override slot per part, so the last call wins and `clear_part_color_override()` clears either kind.

The cell map / cell names you can pass to a cell override are enumerated from the resource:
The cell map / cell names you can pass to a cell override are enumerated from the player:

```gdscript
var ssab := ss_player.get_ssab_resource()
print(ssab.get_cellmap_names()) # -> ["Ringo", ...]
print(ssab.get_cell_names("Ringo")) # -> ["effect3", ...]
print(ss_player.get_cellmap_names()) # -> ["Ringo", ...]
print(ss_player.get_cell_names("Ringo")) # -> ["effect3", ...]
```

Both read the bound `SSABResource` and return an empty array when none is assigned. The same two methods are also available on the resource itself (`ss_player.get_ssab_resource().get_cellmap_names()`), which is the way to enumerate an `.ssab` you have not put on a player yet.

> **On choosing between a texture swap and a cell override**: `set_cellmap_texture()` in the previous section replaces a **whole cell map (texture)** at once, affecting every part that uses it. This feature instead replaces the cell that a **single part** draws. Pick whichever matches your intent.

> **On using part indices**: Part indices are stable within one asset (the same `.ssab`), so if you set overrides frequently, resolve the name once with `get_part_index()` and reuse that index with the `*_by_index()` variants.
Expand Down
2 changes: 2 additions & 0 deletions docs/ja/api/player.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func _ready() -> void:
* `set_frame_skip_enabled(enabled: bool)` / `is_frame_skip_enabled() -> bool` (デフォルト: `true`)
* `set_sub_frame_enabled(enabled: bool)` / `is_sub_frame_enabled() -> bool` (デフォルト: `false`)
* `set_cellmap_texture(cellmap_name: String, texture: Texture2D)` / `get_cellmap_texture(cellmap_name: String) -> Texture2D`
* `get_cellmap_names() -> PackedStringArray` / `get_cell_names(cellmap_name: String) -> PackedStringArray`: 割り当て済みの `SSABResource` から読んだ名前一覧(未割り当てなら空)。`set_part_cell_override()` に渡す名前を調べる用途です。まだプレーヤに載せていない `.ssab` を調べたい場合は [`SSABResource`](resource.md) 自身にも同じメソッドがあります。

### `set_playback_direction` の引数

Expand All @@ -67,6 +68,7 @@ func _ready() -> void:
パーツ単位で、カラー / セル / 表示指定をキーフレームより優先して上書きします。各メソッドは成功時に `true`、パーツが不明な場合やランタイムが受け付けなかった場合に `false` を返します。詳細と注意点は [スクリプト制御とイベント → パーツオーバーライド](../workflow/usage_scripting.md) を参照してください。

* `set_part_color_override(part_name: String, color: Color, blend_op: int = 0, priority: int = 1) -> bool`
* `set_part_color_override_corners(part_name: String, left_top: Color, right_top: Color, left_bottom: Color, right_bottom: Color, blend_op: int = 0, priority: int = 1) -> bool`: 4 頂点それぞれに色を指定して、パーツ内をグラデーションにします。`set_part_color_override` と同じオーバーライド枠を共有するので、後から呼んだ方が有効になり、`clear_part_color_override` はどちらも解除します。
* `set_part_cell_override(part_name: String, cellmap_name: String, cell_name: String, priority: int = 1) -> bool`
* `set_part_visibility_override(part_name: String, force_hidden: bool, cascade: bool = false) -> bool`
* `clear_part_color_override(part_name: String) -> bool` / `clear_part_cell_override(part_name: String) -> bool` / `clear_part_visibility_override(part_name: String) -> bool`
Expand Down
16 changes: 11 additions & 5 deletions docs/ja/workflow/usage_scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ func _ready():
# パーツを赤く着色(乗算)。通常(画像)パーツに適用されます。
ss_player.set_part_color_override("body", Color.RED, 1) # 1 = Mul

# 4 頂点それぞれに色を指定してグラデーションにすることもできます。
ss_player.set_part_color_override_corners(
"body", Color.RED, Color.RED, Color.BLUE, Color.BLUE, 0)

# 別のセルを描画させる(セルマップ名は ".ssce" を付けずに指定)。
ss_player.set_part_cell_override("body", "Ringo", "effect3")

Expand All @@ -203,22 +207,24 @@ func _ready():
|---|---|
| `get_part_index(part_name)` | パーツインデックス。アセットに無ければ `-1` |
| `set_part_color_override(part_name, color, blend_op = 0, priority = 1)` | パーツカラーオーバーライド(単色) |
| `set_part_color_override_corners(part_name, left_top, right_top, left_bottom, right_bottom, blend_op = 0, priority = 1)` | パーツカラーオーバーライド(4 頂点それぞれに色を指定=グラデーション) |
| `set_part_cell_override(part_name, cellmap_name, cell_name, priority = 1)` | 別のセルで描画する |
| `set_part_visibility_override(part_name, force_hidden, cascade = false)` | 強制非表示(`force_hidden = false` でアニメーションに戻す) |
| `clear_part_color_override` / `clear_part_cell_override` / `clear_part_visibility_override` | 1 パーツの 1 オーバーライドを解除 |
| `clear_all_part_overrides()` | そのプレーヤの全オーバーライドを解除 |
| `*_by_index(part_index, ...)` | 上記各メソッドのパーツインデックス指定版(パーツ名の解決を省略) |

各メソッドは、パーツが不明な場合やランタイムが受け付けなかった場合に `false` を返します。
各メソッドは、パーツが不明な場合やランタイムが受け付けなかった場合に `false` を返します。なお単色と 4 頂点色は 1 パーツにつき同じオーバーライド枠を共有するので、後から呼んだ方が有効になり、`clear_part_color_override()` はどちらも解除します。

セルオーバーライドに指定できるセルマップ名 / セル名は、リソース側から列挙できます。
セルオーバーライドに指定できるセルマップ名 / セル名は、プレーヤから列挙できます。

```gdscript
var ssab := ss_player.get_ssab_resource()
print(ssab.get_cellmap_names()) # → ["Ringo", ...]
print(ssab.get_cell_names("Ringo")) # → ["effect3", ...]
print(ss_player.get_cellmap_names()) # → ["Ringo", ...]
print(ss_player.get_cell_names("Ringo")) # → ["effect3", ...]
```

どちらも割り当て済みの `SSABResource` を読むので、未割り当てなら空の配列を返します。同じ 2 つのメソッドはリソース自身にもあり(`ss_player.get_ssab_resource().get_cellmap_names()`)、まだプレーヤに載せていない `.ssab` を列挙したい場合はそちらを使います。

> **テクスチャとセルの差し替えの使い分けについて**: 前節の `set_cellmap_texture()` は**セルマップ(テクスチャ)まるごと**の差し替えで、そのセルマップを使う全パーツにまとめて効きます。こちらは**パーツ 1 つ単位**で、描画するセルそのものを差し替える機能です。目的に応じて使い分けてください。

> **パーツインデックスの使い方について**: パーツインデックスは同一アセット(同じ `.ssab`)内では安定しているので、頻繁にオーバーライドするなら `get_part_index()` で一度パーツ名をパーツインデックスに解決して、`*_by_index()` にそのインデックスを使い回すことを推奨します。
Expand Down
27 changes: 18 additions & 9 deletions examples/Override_Ringo/override_demo.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ extends SpriteStudioPlayer2D
##
## API used (all on SpriteStudioPlayer2D):
## set_part_color_override(part, color, blend_op=0, priority=1)
## set_part_color_override_corners(part, lt, rt, lb, rb, blend_op=0, priority=1)
## set_part_visibility_override(part, force_hidden, cascade=false)
## set_part_cell_override(part, cellmap, cell, priority=1)
## clear_all_part_overrides()
## Cell names can be discovered from the resource:
## ssab.get_cellmap_names() / ssab.get_cell_names(cellmap)
## Cell names can be discovered from the player (or from the resource):
## get_cellmap_names() / get_cell_names(cellmap)

var _label: Label

Expand Down Expand Up @@ -47,9 +48,9 @@ func _step(text: String, secs: float) -> void:
func _run_demo() -> void:
# Discover what the loaded SSAB offers (also handy as a reference in Output).
# print("parts: ", get_part_names())
var cellmap: String = ssab.get_cellmap_names()[0] # "Ringo"
var cellmap: String = get_cellmap_names()[0] # "Ringo"
# print("cellmap: ", cellmap)
# print("cells: ", ssab.get_cell_names(cellmap))
# print("cells: ", get_cell_names(cellmap))

# Since the same part is accessed repeatedly, identify it by part-ID
# rather than its name (accessing by ID is slightly faster).
Expand All @@ -66,19 +67,27 @@ func _run_demo() -> void:
set_part_color_override_by_index(part_id, Color(0.0, 0.2, 1.0, 0.75)) # Access by id.
await _step("1) Color override: body -> red", 2.0)

# 2) Visibility — force-hide the 'apple' and cascade to its children (the whole face).
# 2) Color (4 corners) — one colour per vertex gives a gradient across the part.
# Corner order is left-top, right-top, left-bottom, right-bottom. This shares
# the single colour-override slot, so it replaces step 1's flat tint.
set_part_color_override_corners_by_index(part_id,
Color(1.0, 0.1, 0.1, 1.0), Color(1.0, 0.1, 0.1, 1.0),
Color(0.1, 0.3, 1.0, 1.0), Color(0.1, 0.3, 1.0, 1.0))
await _step("2) Corner colour override: body -> red-to-blue gradient", 2.0)

# 3) Visibility — force-hide the 'apple' and cascade to its children (the whole face).
clear_all_part_overrides()
# set_part_visibility_override("apple", true, true) # Access by name.
set_part_visibility_override_by_index(part_id, true, false) # Access by id.
set_part_visibility_override("heta", true, false)
await _step("2) Visibility override: hide 'apple' with cascade ('heta' disappears)", 2.0)
await _step("3) Visibility override: hide 'apple' with cascade ('heta' disappears)", 2.0)

# 3) Cell — swap the 'apple' sprite to the 'effect3' cell in the Ringo cellmap.
# 4) Cell — swap the 'apple' sprite to the 'effect3' cell in the Ringo cellmap.
clear_all_part_overrides()
# set_part_cell_override("apple", cellmap, "effect3") # Access by name.
set_part_cell_override_by_index(part_id, cellmap, "effect3") # Access by id.
set_part_visibility_override("heta", true, false)
await _step("3) Cell override: body sprite -> 'effect3' cell", 2.0)
await _step("4) Cell override: body sprite -> 'effect3' cell", 2.0)

clear_all_part_overrides()
await _step("4) No override (Restored)", 2.0)
await _step("5) No override (Restored)", 2.0)
2 changes: 1 addition & 1 deletion ss_player/SpriteStudio-SDK
27 changes: 27 additions & 0 deletions ss_player/doc_classes/SpriteStudioPlayer2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,33 @@
Overrides the texture used for the named cell map. Pass an invalid texture to clear the override.
</description>
</method>
<method name="get_cellmap_names" qualifiers="const">
<return type="PackedStringArray" />
<description>
Names of the cell maps in the assigned [SSABResource] (without the [code].ssce[/code] extension), or an empty array when no resource is assigned. Pass one of these to [method get_cell_names] or [method set_part_cell_override].
</description>
</method>
<method name="get_cell_names" qualifiers="const">
<return type="PackedStringArray" />
<param index="0" name="cellmap_name" type="String" />
<description>
Names of the cells inside [param cellmap_name], or an empty array when no resource is assigned or the cell map is unknown.
</description>
</method>
<method name="set_part_color_override_corners">
<return type="bool" />
<param index="0" name="part_name" type="String" />
<param index="1" name="left_top" type="Color" />
<param index="2" name="right_top" type="Color" />
<param index="3" name="left_bottom" type="Color" />
<param index="4" name="right_bottom" type="Color" />
<param index="5" name="blend_op" type="int" default="0" />
<param index="6" name="priority" type="int" default="1" />
<description>
Overrides a part's color with a distinct color per corner, producing a gradient across the part. [param blend_op] is [code]0[/code] = Mix, [code]1[/code] = Mul, [code]2[/code] = Add, [code]3[/code] = Sub; [param priority] is [code]0[/code] = until the animation updates the attribute, [code]1[/code] = until the next animation is set up, [code]2[/code] = permanent.
Shares one override slot with [method set_part_color_override], so the last call wins and [method clear_part_color_override] clears either kind. Returns [code]false[/code] when the part is unknown or the runtime rejects the call.
</description>
</method>
</methods>
<members>
<member name="ssab" type="SSABResource">
Expand Down
Loading
Loading