Now have a simple camera system that I can configure in json for different motion effects.
This commit is contained in:
parent
8345097e10
commit
5b57fb2033
11 changed files with 173 additions and 128 deletions
74
assets/cameras.json
Normal file
74
assets/cameras.json
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"pan": {
|
||||||
|
"_type": "Animation",
|
||||||
|
"easing": 2,
|
||||||
|
"motion": 7,
|
||||||
|
"ease_rate": 0.05,
|
||||||
|
"min_x": 0,
|
||||||
|
"min_y": 0,
|
||||||
|
"max_x": 150.0,
|
||||||
|
"max_y": 0.0,
|
||||||
|
"simple": true,
|
||||||
|
"frames": 1,
|
||||||
|
"speed": 0.01,
|
||||||
|
"scaled": false,
|
||||||
|
"stationary": true,
|
||||||
|
"toggled": false,
|
||||||
|
"flipped": false,
|
||||||
|
"looped": false
|
||||||
|
},
|
||||||
|
"shake": {
|
||||||
|
"_type": "Animation",
|
||||||
|
"easing": 6,
|
||||||
|
"motion": 1,
|
||||||
|
"ease_rate": 0.05,
|
||||||
|
"min_x": -10.0,
|
||||||
|
"min_y": -10.0,
|
||||||
|
"max_x": 10.0,
|
||||||
|
"max_y": 10.0,
|
||||||
|
"simple": true,
|
||||||
|
"frames": 1,
|
||||||
|
"speed": 0.01,
|
||||||
|
"scaled": false,
|
||||||
|
"stationary": true,
|
||||||
|
"toggled": false,
|
||||||
|
"flipped": false,
|
||||||
|
"looped": false
|
||||||
|
},
|
||||||
|
"dolly": {
|
||||||
|
"_type": "Animation",
|
||||||
|
"easing": 2,
|
||||||
|
"motion": 0,
|
||||||
|
"ease_rate": 0.05,
|
||||||
|
"min_x": 0.8,
|
||||||
|
"min_y": 0.8,
|
||||||
|
"max_x": 1.3,
|
||||||
|
"max_y": 1.3,
|
||||||
|
"simple": true,
|
||||||
|
"frames": 1,
|
||||||
|
"speed": 0.01,
|
||||||
|
"scaled": true,
|
||||||
|
"stationary": true,
|
||||||
|
"toggled": false,
|
||||||
|
"flipped": false,
|
||||||
|
"looped": false
|
||||||
|
},
|
||||||
|
"bounce": {
|
||||||
|
"_type": "Animation",
|
||||||
|
"easing": 3,
|
||||||
|
"motion": 2,
|
||||||
|
"ease_rate": 0.05,
|
||||||
|
"min_x": -10,
|
||||||
|
"min_y": -10,
|
||||||
|
"max_x": 10,
|
||||||
|
"max_y": 10,
|
||||||
|
"simple": true,
|
||||||
|
"frames": 1,
|
||||||
|
"speed": 0.01,
|
||||||
|
"scaled": false,
|
||||||
|
"stationary": true,
|
||||||
|
"toggled": false,
|
||||||
|
"flipped": false,
|
||||||
|
"looped": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -87,7 +87,7 @@ namespace boss {
|
||||||
const std::string& player_pos = run % 10 < 5 ? "player1" : "player2";
|
const std::string& player_pos = run % 10 < 5 ? "player1" : "player2";
|
||||||
$ui.move_actor("player", player_pos);
|
$ui.move_actor("player", player_pos);
|
||||||
$ui.zoom(player_pos);
|
$ui.zoom(player_pos);
|
||||||
$ui.$zoom_anim.play();
|
$ui.$camera.play();
|
||||||
int attack_id = std::any_cast<int>(data);
|
int attack_id = std::any_cast<int>(data);
|
||||||
boss::System::combat(attack_id);
|
boss::System::combat(attack_id);
|
||||||
state(State::PLAYER_TURN);
|
state(State::PLAYER_TURN);
|
||||||
|
|
|
||||||
16
boss/ui.cpp
16
boss/ui.cpp
|
|
@ -12,10 +12,10 @@ namespace boss {
|
||||||
$combat_ui(true),
|
$combat_ui(true),
|
||||||
$arena(scene),
|
$arena(scene),
|
||||||
$view_texture({BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT}),
|
$view_texture({BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT}),
|
||||||
$view_sprite($view_texture.getTexture()),
|
$view_sprite($view_texture.getTexture())
|
||||||
$zoom_anim(animation::load("test_zoom"))
|
|
||||||
{
|
{
|
||||||
$view_sprite.setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
|
$view_sprite.setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
|
||||||
|
$camera.style("bounce");
|
||||||
}
|
}
|
||||||
|
|
||||||
void UI::init() {
|
void UI::init() {
|
||||||
|
|
@ -45,7 +45,7 @@ namespace boss {
|
||||||
$actions.render(window);
|
$actions.render(window);
|
||||||
$combat_ui.render(window);
|
$combat_ui.render(window);
|
||||||
|
|
||||||
if($zoom_anim.playing) {
|
if($camera.playing()) {
|
||||||
zoom("player2");
|
zoom("player2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,12 +80,12 @@ namespace boss {
|
||||||
if(cell_name == "") {
|
if(cell_name == "") {
|
||||||
sf::View zoom{{BOSS_VIEW_WIDTH/2,BOSS_VIEW_HEIGHT/2}, {BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT}};
|
sf::View zoom{{BOSS_VIEW_WIDTH/2,BOSS_VIEW_HEIGHT/2}, {BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT}};
|
||||||
$view_texture.setView(zoom);
|
$view_texture.setView(zoom);
|
||||||
} else if($zoom_anim.playing) {
|
} else if($camera.playing()) {
|
||||||
auto& cell = $arena.$ui.cell_for(cell_name);
|
auto& cell = $arena.$ui.cell_for(cell_name);
|
||||||
sf::Vector2f pos{float(cell.x/2), float(cell.y/2)};
|
|
||||||
sf::View zoom;
|
$camera.resize({BOSS_VIEW_WIDTH/2, BOSS_VIEW_HEIGHT/2});
|
||||||
$zoom_anim.apply(zoom, pos, {BOSS_VIEW_WIDTH/2, BOSS_VIEW_HEIGHT/2});
|
$camera.move($view_texture,
|
||||||
$view_texture.setView(zoom);
|
{float(cell.x/2), float(cell.y/2)});
|
||||||
}
|
}
|
||||||
|
|
||||||
$view_sprite.setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
|
$view_sprite.setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <guecs/ui.hpp>
|
#include <guecs/ui.hpp>
|
||||||
#include "gui/combat_ui.hpp"
|
#include "gui/combat_ui.hpp"
|
||||||
#include "scene.hpp"
|
#include "scene.hpp"
|
||||||
|
#include "camera.hpp"
|
||||||
|
|
||||||
namespace components {
|
namespace components {
|
||||||
struct Animation;
|
struct Animation;
|
||||||
|
|
@ -18,7 +19,7 @@ namespace boss {
|
||||||
guecs::UI $actions;
|
guecs::UI $actions;
|
||||||
sf::RenderTexture $view_texture;
|
sf::RenderTexture $view_texture;
|
||||||
sf::Sprite $view_sprite;
|
sf::Sprite $view_sprite;
|
||||||
components::Animation $zoom_anim;
|
cinematic::Camera $camera;
|
||||||
|
|
||||||
UI(components::AnimatedScene &scene, DinkyECS::Entity boss_id);
|
UI(components::AnimatedScene &scene, DinkyECS::Entity boss_id);
|
||||||
|
|
||||||
|
|
|
||||||
56
camera.cpp
Normal file
56
camera.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "camera.hpp"
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include "animation.hpp"
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "components.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
|
namespace cinematic {
|
||||||
|
using components::Animation, std::string;
|
||||||
|
|
||||||
|
struct CameraManager {
|
||||||
|
std::unordered_map<string, Animation> animations;
|
||||||
|
};
|
||||||
|
|
||||||
|
static CameraManager MGR;
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
if(!initialized) {
|
||||||
|
auto cameras = settings::get("cameras");
|
||||||
|
for(auto &[name, data] : cameras.json().items()) {
|
||||||
|
auto anim = components::convert<Animation>(data);
|
||||||
|
MGR.animations.try_emplace(name, anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera::Camera() :
|
||||||
|
anim(MGR.animations.at("pan"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::resize(sf::Vector2f to) {
|
||||||
|
size = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::style(const std::string &name) {
|
||||||
|
anim = MGR.animations.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::move(sf::RenderTexture& target, sf::Vector2f pos) {
|
||||||
|
sf::View zoom;
|
||||||
|
anim.apply(zoom, pos, size);
|
||||||
|
target.setView(zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Camera::playing() {
|
||||||
|
return anim.playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::play() {
|
||||||
|
anim.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
camera.hpp
20
camera.hpp
|
|
@ -1 +1,21 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "components.hpp"
|
||||||
|
#include <SFML/Graphics/RenderTexture.hpp>
|
||||||
|
|
||||||
|
namespace cinematic {
|
||||||
|
struct Camera {
|
||||||
|
components::Animation anim;
|
||||||
|
sf::View view;
|
||||||
|
sf::Vector2f size;
|
||||||
|
|
||||||
|
Camera();
|
||||||
|
|
||||||
|
void resize(sf::Vector2f size);
|
||||||
|
void move(sf::RenderTexture& target, sf::Vector2f pos);
|
||||||
|
bool playing();
|
||||||
|
void play();
|
||||||
|
void style(const std::string &name);
|
||||||
|
};
|
||||||
|
|
||||||
|
void init();
|
||||||
|
}
|
||||||
|
|
|
||||||
2
main.cpp
2
main.cpp
|
|
@ -8,6 +8,7 @@
|
||||||
#include "shaders.hpp"
|
#include "shaders.hpp"
|
||||||
#include "backend.hpp"
|
#include "backend.hpp"
|
||||||
#include "game_level.hpp"
|
#include "game_level.hpp"
|
||||||
|
#include "camera.hpp"
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -16,6 +17,7 @@ int main(int argc, char* argv[]) {
|
||||||
guecs::init(&backend);
|
guecs::init(&backend);
|
||||||
ai::init("ai");
|
ai::init("ai");
|
||||||
animation::init();
|
animation::init();
|
||||||
|
cinematic::init();
|
||||||
GameDB::init();
|
GameDB::init();
|
||||||
|
|
||||||
if(DEBUG_BUILD) sound::mute(true);
|
if(DEBUG_BUILD) sound::mute(true);
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ sources = [
|
||||||
'boss/fight.cpp',
|
'boss/fight.cpp',
|
||||||
'boss/system.cpp',
|
'boss/system.cpp',
|
||||||
'boss/ui.cpp',
|
'boss/ui.cpp',
|
||||||
|
'camera.cpp',
|
||||||
'combat.cpp',
|
'combat.cpp',
|
||||||
'components.cpp',
|
'components.cpp',
|
||||||
'config.cpp',
|
'config.cpp',
|
||||||
|
|
@ -132,9 +133,9 @@ sources = [
|
||||||
executable('runtests', sources + [
|
executable('runtests', sources + [
|
||||||
'tests/ai.cpp',
|
'tests/ai.cpp',
|
||||||
'tests/animation.cpp',
|
'tests/animation.cpp',
|
||||||
'tests/animation2.cpp',
|
|
||||||
'tests/base.cpp',
|
'tests/base.cpp',
|
||||||
'tests/battle.cpp',
|
'tests/battle.cpp',
|
||||||
|
'tests/camera.cpp',
|
||||||
'tests/components.cpp',
|
'tests/components.cpp',
|
||||||
'tests/config.cpp',
|
'tests/config.cpp',
|
||||||
'tests/dbc.cpp',
|
'tests/dbc.cpp',
|
||||||
|
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include <fmt/core.h>
|
|
||||||
#include <string>
|
|
||||||
#include <coroutine>
|
|
||||||
#include <chrono>
|
|
||||||
#include "rand.hpp"
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
#include "components.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
#include "stats.hpp"
|
|
||||||
#include "simplefsm.hpp"
|
|
||||||
#include "textures.hpp"
|
|
||||||
#include "animation.hpp"
|
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
using components::Animation;
|
|
||||||
using TheClock = std::chrono::steady_clock;
|
|
||||||
using TimeDelta = TheClock::duration;
|
|
||||||
using TimePoint = std::chrono::time_point<TheClock>;
|
|
||||||
const TimeDelta MIN_TICK = 200ms;
|
|
||||||
|
|
||||||
struct AnimationState {
|
|
||||||
Animation anim;
|
|
||||||
TimeDelta wait = 1s;
|
|
||||||
sf::Vector2f scale{1.0, 1.0};
|
|
||||||
sf::Vector2f pos{0.0, 0.0};
|
|
||||||
sf::IntRect rect{{0, 0}, {300, 300}};
|
|
||||||
|
|
||||||
void step() {
|
|
||||||
anim.step(scale, pos, rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply(textures::SpriteTexture& st) {
|
|
||||||
anim.apply(*st.sprite, pos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnimationQueue {
|
|
||||||
std::vector<AnimationState> active;
|
|
||||||
TimePoint last_tick;
|
|
||||||
TimeDelta wait_for = MIN_TICK;
|
|
||||||
size_t in_queue = 0;
|
|
||||||
size_t chunk = 1000;
|
|
||||||
|
|
||||||
TimeDelta delta() {
|
|
||||||
return TheClock::now() - last_tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tick() {
|
|
||||||
last_tick = TheClock::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t add(Animation& anim, int initial_wait) {
|
|
||||||
active.emplace_back(anim, TheClock::duration(initial_wait));
|
|
||||||
return active.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimationState& get(size_t id) {
|
|
||||||
return active.at(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
wait_for = MIN_TICK;
|
|
||||||
auto dt = delta();
|
|
||||||
|
|
||||||
for(size_t i = 0; i < chunk; i++) {
|
|
||||||
in_queue++;
|
|
||||||
auto& a = active[in_queue % active.size()];
|
|
||||||
|
|
||||||
if(!a.anim.playing) continue;
|
|
||||||
|
|
||||||
if(a.wait < wait_for) {
|
|
||||||
wait_for = a.wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(a.wait < dt) {
|
|
||||||
// fmt::println("play animation: {} total ", active.size());
|
|
||||||
a.step();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE("simple coroutine animation test", "[coro]") {
|
|
||||||
AnimationQueue queue;
|
|
||||||
Animation anim;
|
|
||||||
anim.play();
|
|
||||||
|
|
||||||
for(int i = 0; i < 100; i++) {
|
|
||||||
int time = Random::uniform(16, 32);
|
|
||||||
size_t id = queue.add(anim, time);
|
|
||||||
auto& what = queue.get(id);
|
|
||||||
|
|
||||||
REQUIRE(what.wait == TheClock::duration(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
dbc::check(queue.active.size() > 0, "zero size queue after adding is impossible.");
|
|
||||||
queue.tick();
|
|
||||||
Stats stats;
|
|
||||||
|
|
||||||
for(int i = 0; i < 10000; i++) {
|
|
||||||
auto start = stats.time_start();
|
|
||||||
auto delta = queue.delta();
|
|
||||||
|
|
||||||
queue.render();
|
|
||||||
|
|
||||||
if(delta > queue.wait_for) {
|
|
||||||
queue.add(anim, Random::uniform(100, 5000));
|
|
||||||
queue.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.sample_time(start);
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt::print("stdev: {}, mean: {}\r", stats.stddev(), stats.mean());
|
|
||||||
}
|
|
||||||
6
tests/camera.cpp
Normal file
6
tests/camera.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
TEST_CASE("view based camera system", "[camera]") {
|
||||||
|
REQUIRE(1 == 1);
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "events.hpp"
|
#include "events.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "gui/event_router.hpp"
|
#include "gui/event_router.hpp"
|
||||||
|
#include "camera.hpp"
|
||||||
|
|
||||||
void craft_weapon() {
|
void craft_weapon() {
|
||||||
auto world = GameDB::current_world();
|
auto world = GameDB::current_world();
|
||||||
|
|
@ -28,6 +29,7 @@ int main(int, char*[]) {
|
||||||
ai::init("ai");
|
ai::init("ai");
|
||||||
animation::init();
|
animation::init();
|
||||||
GameDB::init();
|
GameDB::init();
|
||||||
|
cinematic::init();
|
||||||
|
|
||||||
sf::RenderWindow window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Bossfight Testing Arena");
|
sf::RenderWindow window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Bossfight Testing Arena");
|
||||||
window.setVerticalSyncEnabled(VSYNC);
|
window.setVerticalSyncEnabled(VSYNC);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue