storyboard::UI now adapts the camera to fit the story beats, but really story should do that.

This commit is contained in:
Zed A. Shaw 2026-02-22 12:14:42 -05:00
parent d56b4bd335
commit 024d0cfae7
9 changed files with 123 additions and 19 deletions

View file

@ -8,7 +8,7 @@
"sequences": { "sequences": {
"idle": {"frames": [0], "durations": [47] }, "idle": {"frames": [0], "durations": [47] },
"hurt": {"frames": [0, 1], "durations": [5, 57] }, "hurt": {"frames": [0, 1], "durations": [5, 57] },
"attack": {"frames": [0, 1, 0, 1], "durations": [35, 15, 5, 50] } "attack": {"frames": [0, 1], "durations": [30, 15] }
}, },
"transforms": { "transforms": {
"rushing": { "rushing": {
@ -58,7 +58,7 @@
}, },
"sounds": { "sounds": {
"idle": [], "idle": [],
"attack": [[0, "Sword_Hit_1"], [1, "Marmot_Scream_1"]], "attack": [[0, "Sword_Hit_1"], [1, "Sword_Hit_1"]],
"hurt": [[1, "Marmot_Scream_1"]] "hurt": [[1, "Marmot_Scream_1"]]
} }
}, },

View file

@ -1,5 +1,5 @@
{ {
"cameras": { "scene": {
"sheet": { "sheet": {
"frames": 1, "frames": 1,
"frame_width": 1024, "frame_width": 1024,
@ -80,5 +80,84 @@
"shake": [], "shake": [],
"bounce": [] "bounce": []
} }
},
"story": {
"sheet": {
"frames": 1,
"frame_width": 1024,
"frame_height": 768
},
"sequences": {},
"transforms": {
"pan": {
"min_x": 0.0,
"min_y": 0.0,
"max_x": 0.0,
"max_y": 0.0,
"flipped": false,
"scaled": false,
"toggled": false,
"looped": false,
"easing": "linear",
"relative": false,
"motion": "move_slide"
},
"shake": {
"min_x": -10.0,
"min_y": -10.0,
"max_x": 10.0,
"max_y": 10.0,
"flipped": false,
"scaled": false,
"toggled": false,
"looped": false,
"relative": true,
"easing": "normal_dist",
"motion": "move_shake"
},
"dolly": {
"min_x": 0.8,
"min_y": 0.8,
"max_x": 1.0,
"max_y": 1.0,
"flipped": false,
"scaled": false,
"toggled": false,
"looped": false,
"easing": "sine",
"relative": true,
"motion": "move_rush"
},
"bounce": {
"min_x": 0,
"min_y": -20,
"max_x": 0,
"max_y": 0,
"flipped": false,
"scaled": false,
"toggled": false,
"looped": false,
"relative": true,
"easing": "in_out_back",
"motion": "move_bounce"
},
"pause": {
"min_x": 0,
"min_y": 0,
"max_x": 0,
"max_y": 0,
"flipped": false,
"scaled": false,
"toggled": false,
"looped": false,
"relative": false,
"easing": "none",
"motion": "move_none"
}
},
"forms": {},
"sounds": {
"idle": []
}
} }
} }

View file

@ -10,8 +10,8 @@
], ],
"beats": [ "beats": [
["00:00", "a","pan"], ["00:00", "a","pan"],
["00:01", "a","pan"], ["00:01", "a","shake"],
["00:02", "b","pan"], ["00:02", "a","pause"],
["00:03", "g","pan"], ["00:03", "g","pan"],
["00:04", "h","pan"], ["00:04", "h","pan"],
["00:05", "h","bounce"], ["00:05", "h","bounce"],

View file

@ -17,15 +17,20 @@ namespace cinematic {
void init() { void init() {
if(!initialized) { if(!initialized) {
// BUG: it should be that you give a camera to load by name, not just one for all cameras
auto data = settings::get("cameras"); auto data = settings::get("cameras");
auto anim = components::convert<Animate2>(data["cameras"]);
MGR.animations.try_emplace("main", anim); for(auto [key, value] : data.json().items()) {
auto anim = components::convert<Animate2>(value);
MGR.animations.try_emplace(key, anim);
}
initialized = true; initialized = true;
} }
} }
Camera::Camera(sf::Vector2f size) : Camera::Camera(sf::Vector2f size, const std::string &name) :
anim(MGR.animations.at("main")), anim(MGR.animations.at(name)),
size(size), size(size),
base_size(size), base_size(size),
aimed_at{size.x/2, size.y/2}, aimed_at{size.x/2, size.y/2},
@ -33,6 +38,8 @@ namespace cinematic {
camera_bounds{{0,0}, size}, camera_bounds{{0,0}, size},
view{aimed_at, size} view{aimed_at, size}
{ {
anim.sheet.frame_width = base_size.x;
anim.sheet.frame_height = base_size.y;
} }
void Camera::update_camera_bounds(sf::Vector2f size) { void Camera::update_camera_bounds(sf::Vector2f size) {

View file

@ -13,7 +13,7 @@ namespace cinematic {
sf::FloatRect camera_bounds{{0,0},{SCREEN_WIDTH, SCREEN_HEIGHT}}; sf::FloatRect camera_bounds{{0,0},{SCREEN_WIDTH, SCREEN_HEIGHT}};
sf::View view; sf::View view;
Camera(sf::Vector2f base_size); Camera(sf::Vector2f size, const std::string &name);
void resize(float width); void resize(float width);
void scale(float ratio); void scale(float ratio);

View file

@ -13,6 +13,7 @@
#include "easings.hpp" #include "easings.hpp"
#include "json_mods.hpp" #include "json_mods.hpp"
#include "goap.hpp" #include "goap.hpp"
#include <array>
namespace combat { namespace combat {
enum class BattleHostState; enum class BattleHostState;
@ -106,7 +107,7 @@ namespace components {
std::string image; std::string image;
std::string audio; std::string audio;
std::vector<std::string> layout; std::vector<std::string> layout;
json beats; std::vector<std::array<std::string, 3>> beats;
}; };
struct Combat { struct Combat {

View file

@ -35,7 +35,7 @@ namespace scene {
std::unordered_map<std::string, int> $actor_name_ids; std::unordered_map<std::string, int> $actor_name_ids;
std::vector<Element> $fixtures; std::vector<Element> $fixtures;
std::vector<Element> $actors; std::vector<Element> $actors;
cinematic::Camera $camera{{BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT}}; cinematic::Camera $camera{{BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT}, "scene"};
Engine(components::AnimatedScene& scene); Engine(components::AnimatedScene& scene);

View file

@ -17,6 +17,20 @@ namespace storyboard {
auto config = settings::get("stories"); auto config = settings::get("stories");
$story = components::convert<components::Storyboard>(config[story_name]); $story = components::convert<components::Storyboard>(config[story_name]);
$audio = sound::get_sound_pair($story.audio).sound; $audio = sound::get_sound_pair($story.audio).sound;
config_camera($camera);
}
void UI::config_camera(cinematic::Camera &camera) {
camera.anim.sequences.clear();
camera.anim.forms.clear();
for(auto& [timecode, cell, transform] : $story.beats) {
animate2::Sequence seq{.frames={0}, .durations={60}};
camera.anim.sequences.try_emplace(timecode, seq);
animate2::Form form{timecode, transform};
camera.anim.forms.try_emplace(timecode, form);
}
} }
void UI::init() { void UI::init() {
@ -66,20 +80,22 @@ namespace storyboard {
} }
void UI::track_audio() { void UI::track_audio() {
auto& beat = $story.beats[cur_beat % $story.beats.size()]; auto& [timecode, cell_name, form] = $story.beats[cur_beat % $story.beats.size()];
auto track_head = $audio->getPlayingOffset(); auto track_head = $audio->getPlayingOffset();
auto next_beat = parse_time_code(beat[0]);
auto next_beat = parse_time_code(timecode);
if(track_head >= next_beat) { if(track_head >= next_beat) {
if($moving) return; if($moving) return;
$moving = true; // prevent motion until next tick $moving = true; // prevent motion until next tick
// get the original zoom target as from // get the original zoom target as from
auto& cell = $ui.cell_for($zoom_target); auto& from_cell = $ui.cell_for($zoom_target);
$camera.position(cell.mid_x, cell.mid_y); $camera.position(from_cell.mid_x, from_cell.mid_y);
$zoom_target = cell_name;
$camera.style(timecode);
$zoom_target = beat[1];
$camera.style(beat[2]);
// get the new target from the cell names // get the new target from the cell names
zoom($zoom_target); zoom($zoom_target);
$camera.play(); $camera.play();

View file

@ -11,7 +11,7 @@ namespace storyboard {
guecs::UI $ui; guecs::UI $ui;
sf::RenderTexture $view_texture; sf::RenderTexture $view_texture;
sf::Sprite $view_sprite; sf::Sprite $view_sprite;
cinematic::Camera $camera{{SCREEN_WIDTH, SCREEN_HEIGHT}}; cinematic::Camera $camera{{SCREEN_WIDTH, SCREEN_HEIGHT}, "story"};
std::shared_ptr<sf::Sound> $audio; std::shared_ptr<sf::Sound> $audio;
std::string $zoom_target = "a"; std::string $zoom_target = "a";
bool $moving = false; bool $moving = false;
@ -29,6 +29,7 @@ namespace storyboard {
void reset(); void reset();
void track_audio(); void track_audio();
bool playing(); bool playing();
void config_camera(cinematic::Camera &camera);
}; };
} }