diff --git a/docs/gyms/cross-timeline-interactions/gym_main.dtl b/docs/gyms/cross-timeline-interactions/gym_main.dtl index 58d8988..557998a 100644 --- a/docs/gyms/cross-timeline-interactions/gym_main.dtl +++ b/docs/gyms/cross-timeline-interactions/gym_main.dtl @@ -69,5 +69,10 @@ Miko (neutral): More patterns to explore: Miko (neutral): Press the button 5 times to return. jump gym09_intro/ +- 10. Character & Portrait Gym + Miko (joy): Positions, emotions, transforms, and spine animations! + Advisor (pl5): The visual half of the visual novel. + jump gym10_main/ + - Back to first page jump menu diff --git a/docs/gyms/gym10_character_portraits/README.md b/docs/gyms/gym10_character_portraits/README.md new file mode 100644 index 0000000..de607fe --- /dev/null +++ b/docs/gyms/gym10_character_portraits/README.md @@ -0,0 +1,25 @@ +# Gym 10 — Character & Portrait Gym + +Tests every aspect of Dialogic's character/portrait system. + +## Demos + +| # | Timeline | What it covers | +|---|----------|---------------| +| 1 | `gym10_positions` | Preset positions (left/center/right/leftmost/rightmost), manual pos=, z-index stacking | +| 2 | `gym10_portrait_switch` | `[portrait=name]` mid-text switching, emotional arcs, variable-driven portraits | +| 3 | `gym10_multichar` | 3+ characters on screen, z-index layering, dynamic leave/rejoin flow | +| 4 | `gym10_transforms` | mirror, scale, offset, rotation, smooth move transitions | +| 5 | `gym10_spine` | Raptor's spine-animated portraits (walk/jump/roar) vs static PNG portraits | + +## Characters Used + +- **Miko** — portraits: neutral, smile, joy, anger, shock +- **Advisor** — portraits: pl5, blink, doubt, surprise +- **Merchant** — portraits: happy, blink, surprise, confusion +- **Raptor** — spine-animated portraits: walk, jump, roar + +## How to Run + +- **From hub:** Pick option 10 in the main gym hub +- **Standalone:** Open `gym10_main.tscn` → F6 diff --git a/docs/gyms/gym10_character_portraits/gym10_controller.gd b/docs/gyms/gym10_character_portraits/gym10_controller.gd new file mode 100644 index 0000000..a89ae3d --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_controller.gd @@ -0,0 +1,11 @@ +# ============================================================ +# Gym 10 — Character & Portrait Gym Controller +# ============================================================ +extends Node2D + +@export var start_timeline: DialogicTimeline + + +func _ready() -> void: + Dialogic.start(start_timeline) + print("[Gym10] Character & Portrait Gym ready.") diff --git a/docs/gyms/gym10_character_portraits/gym10_controller.gd.uid b/docs/gyms/gym10_character_portraits/gym10_controller.gd.uid new file mode 100644 index 0000000..8c86382 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_controller.gd.uid @@ -0,0 +1 @@ +uid://pdl00kv4n5q1 diff --git a/docs/gyms/gym10_character_portraits/gym10_main.dtl b/docs/gyms/gym10_character_portraits/gym10_main.dtl new file mode 100644 index 0000000..0a2bd7a --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_main.dtl @@ -0,0 +1,48 @@ +# ============================================================ +# Gym 10 — Character & Portrait Gym +# ============================================================ +# Systematic exploration of Dialogic's character/portrait system: +# positions, mid-text switching, multi-character scenes, +# transforms (mirror/scale/offset), and spine animations. +# ============================================================ + +join Miko (joy) center [animation="Bounce In"] + +Miko (joy): Welcome to the Character & Portrait Gym! + +[wait time="0.5"] + +join Advisor (pl5) right [animation="Fade In"] + +Advisor (pl5): This is the VISUAL half of the visual novel. +Miko (smile): Characters, portraits, expressions — all the things\ +that bring a VN to life! + +[wait time="1.0"] + +label menu + +Miko (neutral): What would you like to explore? + +- 1. Positions & Presets + Miko (smile): Left, center, right, manual positioning, and z-index! + jump gym10_positions/ + +- 2. Portrait Switching (mid-text emotions) + Miko (joy): Watch my expression change in real-time as I speak! + jump gym10_portrait_switch/ + +- 3. Multi-Character Scenes + Miko (neutral): 3+ characters on screen — who's in front? + jump gym10_multichar/ + +- 4. Transforms (mirror, scale, offset) + Miko (smile): Flip, resize, and reposition characters. + jump gym10_transforms/ + +- 5. Spine-Animated Portraits (Raptor) + Miko (joy): The dinosaur with moving portraits! + jump gym10_spine/ + +- Back to main hub + jump gym_main/menu diff --git a/docs/gyms/gym10_character_portraits/gym10_main.dtl.uid b/docs/gyms/gym10_character_portraits/gym10_main.dtl.uid new file mode 100644 index 0000000..d5d14c8 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_main.dtl.uid @@ -0,0 +1 @@ +uid://br6emd84bsphi diff --git a/docs/gyms/gym10_character_portraits/gym10_main.tscn b/docs/gyms/gym10_character_portraits/gym10_main.tscn new file mode 100644 index 0000000..17a7aa2 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_main.tscn @@ -0,0 +1,8 @@ +[gd_scene format=3] + +[ext_resource type="Script" path="res://docs/gyms/gym10_character_portraits/gym10_controller.gd" id="1_ctrl"] +[ext_resource type="Resource" path="res://docs/gyms/gym10_character_portraits/gym10_main.dtl" id="2_main"] + +[node name="Gym10CharacterPortraits" type="Node2D"] +script = ExtResource("1_ctrl") +start_timeline = ExtResource("2_main") diff --git a/docs/gyms/gym10_character_portraits/gym10_multichar.dtl b/docs/gyms/gym10_character_portraits/gym10_multichar.dtl new file mode 100644 index 0000000..bef7366 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_multichar.dtl @@ -0,0 +1,70 @@ +# ============================================================ +# Gym 10.3 — Multi-Character Scenes +# ============================================================ +# Demonstrates managing 3+ characters on screen: +# join orchestration, z-index stacking, leave/rejoin flow. +# ============================================================ + +leave --All-- + +# ---- Build up a full scene ---- +join Miko (neutral) center [animation="Fade In"] +join Advisor (pl5) right [animation="Fade In" wait="true"] +join Merchant (happy) left [animation="Fade In" wait="true"] + +Miko (neutral): Three characters on screen! +Advisor (pl5): This is a full conversation scene. +Merchant (happy): I feel included! + +[wait time="1.0"] + +# ---- Demonstrate z-index with 3 overlapping chars ---- +leave --All-- + +join Miko (joy) center [animation="Fade In" z_index="2"] +Miko (joy): Center with z_index 2. + +join Advisor (pl5) pos=x0.45y0.5 size=y400px rot=0 [animation="Fade In" z_index="1"] +Advisor (pl5): Moved slightly left, behind Miko (z_index 1). + +join Merchant (surprise) pos=x0.55y0.5 size=y400px rot=0 [animation="Fade In" z_index="3"] +Merchant (surprise): Moved slightly right, with highest z_index (3)! + +[wait time="1.5"] + +Miko (smile): Notice the stacking — Merchant is on top, then me, then Advisor. +Advisor (doubt): This is how you control who appears in front. + +[wait time="1.5"] + +# ---- Leave and rejoin (dynamic cast) ---- +leave Merchant [animation="Fade Down"] + +Miko (neutral): The merchant left. Now it's just the two of us. + +[wait time="0.5"] + +join Merchant (happy) left [animation="Bounce In"] + +Merchant (happy): I'm back! And I moved to the left. + +[wait time="1.0"] + +Advisor (pl5): Characters can leave and rejoin at different positions. +Advisor (doubt): This is essential for scene direction — characters\ +enter and exit the conversation naturally. + +[wait time="1.5"] + +# ---- Leave all at once ---- +Miko (smile): And with one command... +leave --All-- [animation="Fade Down"] + +[wait time="1.0"] + +Advisor (pl5): `leave --All--` clears the stage instantly. +Miko (neutral): Useful for scene transitions! + +[wait time="1.0"] + +jump gym10_main/menu diff --git a/docs/gyms/gym10_character_portraits/gym10_multichar.dtl.uid b/docs/gyms/gym10_character_portraits/gym10_multichar.dtl.uid new file mode 100644 index 0000000..67a604d --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_multichar.dtl.uid @@ -0,0 +1 @@ +uid://bqy80a0et22t4 diff --git a/docs/gyms/gym10_character_portraits/gym10_portrait_switch.dtl b/docs/gyms/gym10_character_portraits/gym10_portrait_switch.dtl new file mode 100644 index 0000000..0fea40c --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_portrait_switch.dtl @@ -0,0 +1,64 @@ +# ============================================================ +# Gym 10.2 — Portrait Switching (Mid-Text Emotions) +# ============================================================ +# Demonstrates changing a character's portrait expression +# in the middle of a text event using [portrait=name]. +# Also: variable-driven portrait selection. +# ============================================================ + +leave --All-- + +join Miko (neutral) center [animation="Fade In"] + +Advisor (pl5): Portrait switching is what makes characters feel alive. +Advisor (pl5): Watch Miko's expression change as she speaks. + +[wait time="1.0"] + +# ---- Single emotion change ---- +Miko (neutral): I'll start calm...[portrait=joy] and suddenly I'm happy! + +[wait time="1.0"] + +# ---- Emotional arc in one line ---- +Miko (neutral): Watch this:[portrait=shock] Whoa![portrait=anger] That makes me mad!\ +[portrait=smile] But I'm a forgiving person.[portrait=joy] And now I'm happy! + +[wait time="1.5"] + +# ---- With pauses between emotions ---- +Miko (joy): Let me tell you a story.[pause=0.8][portrait=neutral] It starts normally.\ +[pause=0.8][portrait=shock] Then something shocking happens!\ +[pause=0.8][portrait=smile] But it has a happy ending. + +[wait time="2.0"] + +Advisor (pl5): The [portrait=name] tag switches the current speaker's portrait. +Advisor (doubt): It happens instantly when the text reveal reaches that point. + +# ---- Variable-driven portrait ---- +leave Miko +[wait time="0.5"] + +set {Gym.demo_flag} = true + +if {Gym.demo_flag}: + join Miko (joy) center [animation="Fade In"] + Miko (joy): The variable was TRUE, so I'm happy! +else: + join Miko (anger) center [animation="Fade In"] + Miko (anger): The variable was FALSE, so I'm angry! + +[wait time="1.0"] + +Miko (smile): You can also use variables directly in portrait expressions. +Advisor (pl5): In a character event, use curly braces and parentheses. +Advisor (doubt): But that needs to be set up in the character event, not in text. + +[wait time="1.0"] + +# ---- Clean up ---- +leave --All-- +[wait time="0.5"] + +jump gym10_main/menu diff --git a/docs/gyms/gym10_character_portraits/gym10_portrait_switch.dtl.uid b/docs/gyms/gym10_character_portraits/gym10_portrait_switch.dtl.uid new file mode 100644 index 0000000..c6fb8d8 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_portrait_switch.dtl.uid @@ -0,0 +1 @@ +uid://8sk057i7sjgh diff --git a/docs/gyms/gym10_character_portraits/gym10_positions.dtl b/docs/gyms/gym10_character_portraits/gym10_positions.dtl new file mode 100644 index 0000000..647cd6e --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_positions.dtl @@ -0,0 +1,81 @@ +# ============================================================ +# Gym 10.1 — Positions & Presets +# ============================================================ +# Demonstrates all preset positions and manual positioning. +# ============================================================ + +# ---- Clear the stage ---- +leave --All-- + +Advisor (pl5): Let's start with the basics — character positions. + +# ---- Join at each preset position ---- +join Miko (neutral) left [animation="Fade In"] +Miko (neutral): I'm on the left. + +join Advisor (pl5) center [animation="Fade In"] +Advisor (pl5): I'm in the center. + +join Merchant (happy) right [animation="Fade In"] +Merchant (happy): And I'm on the right! + +[wait time="1.0"] + +Advisor (pl5): Those are the three main presets: left, center, right. + +# ---- Show leftmost/rightmost ---- +leave --All-- + +join Miko (smile) leftmost [animation="Fade In"] +Miko (smile): Now I'm at leftmost — further to the edge! + +join Advisor (pl5) rightmost [animation="Fade In"] +Advisor (pl5): And I'm at rightmost — the far right edge. + +[wait time="1.5"] + +Advisor (pl5): Available presets: leftmost, left, center, right, rightmost. + +# ---- Manual positioning ---- +leave --All-- + +join Miko (joy) pos=x0.15y0.5 [animation="Fade In"] +Miko (joy): I'm manually positioned at x=0.15! + +join Advisor (pl5) pos=x0.85y0.5 [animation="Fade In"] +Advisor (pl5): And I'm at x=0.85 — no preset needed. + +[wait time="1.5"] + +Advisor (pl5): Use pos=xY for precise control over x/y coordinates. +Advisor (doubt): Values are relative to viewport: 0=left, 0.5=center, 1=right. + +# ---- Z-Index (stacking order) ---- +leave --All-- + +Miko (neutral): Next up — z-index controls who appears in front. + +join Miko (neutral) center [animation="Fade In" z_index="2"] +Miko (neutral): My z_index is 2 — I should be on top. + +join Advisor (pl5) center [animation="Fade In" z_index="1"] +Advisor (pl5): My z_index is 1 — I'm behind Miko. + +[wait time="1.5"] + +Advisor (doubt): Higher z_index = visually in front. Important when\ +characters overlap. + +join Merchant (happy) center [animation="Fade In" z_index="3"] +Merchant (happy): z_index 3! I'm on top of everyone! + +[wait time="1.5"] + +Merchant (happy): Z-index is Dialogic's own sorting — not Godot's native z_index. + +# ---- Clean up and return ---- +leave --All-- + +[wait time="0.5"] + +jump gym10_main/menu diff --git a/docs/gyms/gym10_character_portraits/gym10_positions.dtl.uid b/docs/gyms/gym10_character_portraits/gym10_positions.dtl.uid new file mode 100644 index 0000000..f23be9a --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_positions.dtl.uid @@ -0,0 +1 @@ +uid://145553b16tyc diff --git a/docs/gyms/gym10_character_portraits/gym10_spine.dtl b/docs/gyms/gym10_character_portraits/gym10_spine.dtl new file mode 100644 index 0000000..f0ebd32 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_spine.dtl @@ -0,0 +1,86 @@ +# ============================================================ +# Gym 10.5 — Spine-Animated Portraits (Raptor) +# ============================================================ +# Raptor uses spine animation portraits instead of static +# images: walk, jump, roar. Each plays a looping animation. +# ============================================================ + +leave --All-- + +# ---- Intro ---- +join Advisor (pl5) center +Advisor (pl5): Raptor uses SPINE-ANIMATED portraits. +Advisor (doubt): Unlike static PNG portraits, these play looping animations. + +[wait time="1.0"] + +# ---- Show the walk portrait ---- +leave Advisor +join Raptor (walk) center [animation="Fade In"] + +Raptor (walk): This is my WALK portrait. I'm animated! + +[wait time="3.0"] + +# ---- Switch to jump ---- +update Raptor (jump) center + +Raptor (jump): Now I'm JUMPING! Notice the animation change. + +[wait time="3.0"] + +# ---- Switch to roar ---- +update Raptor (roar) center + +Raptor (roar): ROOOAAR! My roar animation is fierce! + +[wait time="3.0"] + +# ---- Demonstrate fade transition ---- +update Raptor (walk) center + +Raptor (walk): You can also crossfade between spine portraits. + +[wait time="1.0"] + +# ---- Compare with static portraits ---- +leave Raptor + +join Miko (neutral) center [animation="Fade In"] +Miko (neutral): I use static PNG portraits — they don't move. +Miko (smile): Each of my expressions is a separate still image. + +[wait time="1.0"] + +join Advisor (pl5) left [animation="Fade In"] +Advisor (pl5): Spine portraits are great for characters that need\ +continuous motion — creatures, robots, or expressive characters. + +[wait time="1.5"] + +# ---- Bring Raptor back with both ---- +leave Miko +leave Advisor + +join Raptor (walk) center [animation="Fade In"] + +Raptor (walk): Spine portraits are configured in the character resource. +Raptor (walk): Each portrait points to a custom portrait scene\ +with a SpineSprite node instead of a TextureRect. + +[wait time="2.0"] + +# ---- Key difference ---- +join Advisor (pl5) right [animation="Fade In"] + +Advisor (pl5): Key difference: spine animations loop forever. +Advisor (pl5): Static portraits stay on one frame until you switch them. +Advisor (pl5): Both use the same join/update/leave commands! + +[wait time="2.0"] + +# ---- Clean up ---- +leave --All-- +[wait time="0.5"] + +jump gym10_main/menu diff --git a/docs/gyms/gym10_character_portraits/gym10_spine.dtl.uid b/docs/gyms/gym10_character_portraits/gym10_spine.dtl.uid new file mode 100644 index 0000000..4837557 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_spine.dtl.uid @@ -0,0 +1 @@ +uid://bh4bv8cb644be diff --git a/docs/gyms/gym10_character_portraits/gym10_transforms.dtl b/docs/gyms/gym10_character_portraits/gym10_transforms.dtl new file mode 100644 index 0000000..0178b9a --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_transforms.dtl @@ -0,0 +1,98 @@ +# ============================================================ +# Gym 10.4 — Transforms (Mirror, Scale, Offset) +# ============================================================ +# Demonstrates visual manipulation: mirroring (flip), +# scaling, offset, and manual positioning with size/rotation. +# ============================================================ + +leave --All-- + +# ---- Baseline ---- +Miko (neutral): Let's explore transforms — mirror, scale, and offset. +join Miko (neutral) center [animation="Fade In"] +Miko (neutral): Here I am at default: scale=1, no mirror, no offset. + +[wait time="1.5"] + +# ---- Mirror (flip horizontally) ---- +update Miko (neutral) center [animation="Tada" wait="true" mirrored="true"] +Miko (smile): And now I'm mirrored! I'm facing the other way. + +[wait time="1.0"] + +update Miko (neutral) center [animation="Tada" wait="true" mirrored="false"] +Miko (smile): And back to normal. Mirror is great for characters\ +facing each other. + +[wait time="1.5"] + +# ---- Scale ---- +leave Miko +join Miko (joy) center [animation="Fade In"] +Miko (joy): This is scale=1 — the default size. + +[wait time="1.0"] + +leave Miko +# Scale is set on the character resource, but we can demonstrate via update +# with manual size +join Miko (shock) pos=x0.5y0.5 size=x0.4y0.4 [animation="Bounce In"] +Miko (shock): Now I'm bigger! size=x0.4 y0.4 — 40% of viewport! + +[wait time="1.5"] + +join Advisor (pl5) pos=x0.2y0.5 size=x0.2y0.2 [animation="Fade In"] +Advisor (pl5): And I'm smaller — only 20% of the viewport. + +[wait time="1.5"] + +Miko (smile): Size differences create visual hierarchy. +Advisor (pl5): The main speaker can be larger; background characters smaller. + +[wait time="1.5"] + +# ---- Offset ---- +leave --All-- + +join Miko (neutral) center [animation="Fade In"] +Miko (neutral): Offset shifts a character from their position origin. + +[wait time="1.0"] + +# Offset is set on the character resource, not via timeline syntax +# But we can use manual pos for the same effect +update Miko (smile) pos=x0.5y0.7 [move_time="0.8" move_trans="Sine" move_ease="In Out"] +Miko (smile): I used pos= to shift downward smoothly! + +[wait time="1.0"] + +update Miko (joy) pos=x0.5y0.5 [move_time="0.5"] +Miko (joy): And back to center. The move_time made it animate smoothly. + +[wait time="1.5"] + +# ---- Rotation ---- +leave Miko +join Miko (joy) pos=x0.5y0.5 rot=15 [animation="Fade In"] +Miko (joy): I'm tilted! rot=15 degrees. + +[wait time="1.0"] + +update Miko (smile) pos=x0.5y0.5 rot=0 [move_time="0.5"] +Miko (smile): And back to straight. Rotate for dramatic effect! + +[wait time="1.5"] + +# ---- Summary ---- +leave --All-- + +Advisor (pl5): Quick reference: +Advisor (pl5): • mirroed=true/false — flip horizontally +Advisor (doubt): • size=x1 y1 — scale from viewport (0-1) +Advisor (doubt): • pos=x1 y1 — manual position +Advisor (doubt): • rot=degrees — rotation +Advisor (doubt): • move_time=X — smooth tween for position changes + +[wait time="2.0"] + +jump gym10_main/menu diff --git a/docs/gyms/gym10_character_portraits/gym10_transforms.dtl.uid b/docs/gyms/gym10_character_portraits/gym10_transforms.dtl.uid new file mode 100644 index 0000000..2a94436 --- /dev/null +++ b/docs/gyms/gym10_character_portraits/gym10_transforms.dtl.uid @@ -0,0 +1 @@ +uid://dcd782k6ho422 diff --git a/project.godot b/project.godot index 40b3260..97acc31 100644 --- a/project.godot +++ b/project.godot @@ -29,6 +29,12 @@ directories/dch_directory={ directories/dtl_directory={ "gym09_congrats": "res://docs/gyms/gym09_scene_switching/gym09_congrats.dtl", "gym09_intro": "res://docs/gyms/gym09_scene_switching/gym09_intro.dtl", +"gym10_main": "res://docs/gyms/gym10_character_portraits/gym10_main.dtl", +"gym10_multichar": "res://docs/gyms/gym10_character_portraits/gym10_multichar.dtl", +"gym10_portrait_switch": "res://docs/gyms/gym10_character_portraits/gym10_portrait_switch.dtl", +"gym10_positions": "res://docs/gyms/gym10_character_portraits/gym10_positions.dtl", +"gym10_spine": "res://docs/gyms/gym10_character_portraits/gym10_spine.dtl", +"gym10_transforms": "res://docs/gyms/gym10_character_portraits/gym10_transforms.dtl", "gym_main": "res://docs/gyms/cross-timeline-interactions/gym_main.dtl", "gym_pattern1_jump": "res://docs/gyms/cross-timeline-interactions/gym_pattern1_jump.dtl", "gym_pattern1_target": "res://docs/gyms/cross-timeline-interactions/gym_pattern1_target.dtl",