feat(anim): suport de flip H/V per frame i per animació
This commit is contained in:
1 parent
39eda56c9d
commit
1d43a77800
5 files changed
+438
-87
No files matched your search
@@ -0,0 +1,214 @@
|
||||
// Smoke test del parser d'animacions amb suport de flip H/V. Stand-alone:
|
||||
// només enllaça `animated_sprite_parser.cpp` (la part del motor que NO toca
|
||||
// Resource::Cache ni Surface), perquè la cobertura interessa el contracte
|
||||
// de càrrega del YAML, no el render real.
|
||||
//
|
||||
// Tests coberts:
|
||||
// T1 backward compat — YAML sense cap flip declarat carrega tots els
|
||||
// frames amb (flip_h=false, flip_v=false) i sense
|
||||
// que l'animació prengui control del flip.
|
||||
// T2 flip a nivell d'animació — `flip_h: true` amb frames `int` →
|
||||
// tots els frames hereten flip_h=true.
|
||||
// T3 override per-frame — frame mapping `{ index: N, flip_h: true }`
|
||||
// en una animació sense flip → només aquest frame
|
||||
// té el flag.
|
||||
// T4 mapping no hereta — animació amb `flip_h: true` però un frame
|
||||
// `{ index: N }` (mapping sense flip) → flip_h=false
|
||||
// en aquest frame (el mapping declara el seu propi
|
||||
// estat, sense heretar).
|
||||
// T5 combinat V+H — frame `{ index, flip_h: true, flip_v: true }` →
|
||||
// ambdós flags actius.
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "core/rendering/sprite/animated_sprite.hpp"
|
||||
#include "external/fkyaml_node.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
int g_failures = 0;
|
||||
|
||||
void check(bool cond, const char* msg, const char* file, int line) {
|
||||
if (!cond) {
|
||||
std::cerr << " FAIL: " << msg << " (" << file << ":" << line << ")\n";
|
||||
++g_failures;
|
||||
}
|
||||
}
|
||||
|
||||
#define REQUIRE(cond) check((cond), #cond, __FILE__, __LINE__)
|
||||
|
||||
// Paràmetres comuns per a tots els tests: spritesheet hipotètic 64×64
|
||||
// amb tiles de 16×16 → grid 4×4, max_tiles=16.
|
||||
auto sheetParams() -> AnimatedSprite::SheetParams {
|
||||
return AnimatedSprite::SheetParams{
|
||||
.frame_width = 16.0F,
|
||||
.frame_height = 16.0F,
|
||||
.frames_per_row = 4,
|
||||
.max_tiles = 16,
|
||||
};
|
||||
}
|
||||
|
||||
auto parse(const std::string& yaml_str) {
|
||||
return AnimatedSprite::parseAnimationsFromYaml(
|
||||
fkyaml::node::deserialize(yaml_str), sheetParams());
|
||||
}
|
||||
|
||||
void testBackwardCompat() {
|
||||
std::cout << "[T1] backward compat: cap flip declarat\n";
|
||||
const std::string YAML = R"(
|
||||
animations:
|
||||
walk: { frames: [0, 1, 2, 3], frame_ms: 100 }
|
||||
stand: { frames: [4] }
|
||||
)";
|
||||
const auto ANIMS = parse(YAML);
|
||||
REQUIRE(ANIMS.size() == 2);
|
||||
|
||||
const auto WALK_IT = ANIMS.find("walk");
|
||||
REQUIRE(WALK_IT != ANIMS.end());
|
||||
if (WALK_IT != ANIMS.end()) {
|
||||
const auto& WALK = WALK_IT->second;
|
||||
REQUIRE(WALK.frames.size() == 4);
|
||||
REQUIRE(!WALK.controls_flip);
|
||||
for (const auto& F : WALK.frames) {
|
||||
REQUIRE(!F.flip_h);
|
||||
REQUIRE(!F.flip_v);
|
||||
}
|
||||
}
|
||||
|
||||
const auto STAND_IT = ANIMS.find("stand");
|
||||
REQUIRE(STAND_IT != ANIMS.end());
|
||||
if (STAND_IT != ANIMS.end()) {
|
||||
const auto& STAND = STAND_IT->second;
|
||||
REQUIRE(STAND.frames.size() == 1);
|
||||
REQUIRE(!STAND.controls_flip);
|
||||
REQUIRE(!STAND.frames[0].flip_h);
|
||||
REQUIRE(!STAND.frames[0].flip_v);
|
||||
}
|
||||
}
|
||||
|
||||
void testFlipAtAnimationLevel() {
|
||||
std::cout << "[T2] flip a nivell d'anim hereten frames int\n";
|
||||
const std::string YAML = R"(
|
||||
animations:
|
||||
walk_west: { frames: [8, 9, 10, 11], frame_ms: 100, flip_h: true }
|
||||
)";
|
||||
const auto ANIMS = parse(YAML);
|
||||
const auto IT = ANIMS.find("walk_west");
|
||||
REQUIRE(IT != ANIMS.end());
|
||||
if (IT != ANIMS.end()) {
|
||||
const auto& A = IT->second;
|
||||
REQUIRE(A.frames.size() == 4);
|
||||
REQUIRE(A.controls_flip);
|
||||
for (const auto& F : A.frames) {
|
||||
REQUIRE(F.flip_h);
|
||||
REQUIRE(!F.flip_v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testOverridePerFrame() {
|
||||
std::cout << "[T3] override per-frame en animació sense flip\n";
|
||||
const std::string YAML = R"(
|
||||
animations:
|
||||
death_spin:
|
||||
frames:
|
||||
- 0
|
||||
- 4
|
||||
- 8
|
||||
- { index: 8, flip_h: true }
|
||||
frame_ms: 100
|
||||
)";
|
||||
const auto ANIMS = parse(YAML);
|
||||
const auto IT = ANIMS.find("death_spin");
|
||||
REQUIRE(IT != ANIMS.end());
|
||||
if (IT != ANIMS.end()) {
|
||||
const auto& A = IT->second;
|
||||
REQUIRE(A.frames.size() == 4);
|
||||
// Hi ha un mapping → l'animació pren el control del flip.
|
||||
REQUIRE(A.controls_flip);
|
||||
|
||||
// Els tres primers (int, sense flip a l'anim) → tot a false.
|
||||
REQUIRE(!A.frames[0].flip_h);
|
||||
REQUIRE(!A.frames[0].flip_v);
|
||||
REQUIRE(!A.frames[1].flip_h);
|
||||
REQUIRE(!A.frames[1].flip_v);
|
||||
REQUIRE(!A.frames[2].flip_h);
|
||||
REQUIRE(!A.frames[2].flip_v);
|
||||
|
||||
// El quart (mapping amb flip_h: true) → només flip_h.
|
||||
REQUIRE(A.frames[3].flip_h);
|
||||
REQUIRE(!A.frames[3].flip_v);
|
||||
}
|
||||
}
|
||||
|
||||
void testMappingDoesNotInherit() {
|
||||
std::cout << "[T4] mapping NO hereta el flip de l'animació\n";
|
||||
const std::string YAML = R"(
|
||||
animations:
|
||||
mixed:
|
||||
frames:
|
||||
- 0
|
||||
- { index: 4 }
|
||||
frame_ms: 100
|
||||
flip_h: true
|
||||
)";
|
||||
const auto ANIMS = parse(YAML);
|
||||
const auto IT = ANIMS.find("mixed");
|
||||
REQUIRE(IT != ANIMS.end());
|
||||
if (IT != ANIMS.end()) {
|
||||
const auto& A = IT->second;
|
||||
REQUIRE(A.frames.size() == 2);
|
||||
REQUIRE(A.controls_flip);
|
||||
|
||||
// Frame int hereta el flip_h true de l'animació.
|
||||
REQUIRE(A.frames[0].flip_h);
|
||||
REQUIRE(!A.frames[0].flip_v);
|
||||
|
||||
// Frame mapping declara els seus propis flags (cap declarat
|
||||
// → false) i NO hereta del flip a nivell d'anim.
|
||||
REQUIRE(!A.frames[1].flip_h);
|
||||
REQUIRE(!A.frames[1].flip_v);
|
||||
}
|
||||
}
|
||||
|
||||
void testCombinedVerticalAndHorizontal() {
|
||||
std::cout << "[T5] flip combinat V+H per-frame\n";
|
||||
const std::string YAML = R"(
|
||||
animations:
|
||||
upside_down:
|
||||
frames:
|
||||
- { index: 5, flip_h: true, flip_v: true }
|
||||
frame_ms: 100
|
||||
)";
|
||||
const auto ANIMS = parse(YAML);
|
||||
const auto IT = ANIMS.find("upside_down");
|
||||
REQUIRE(IT != ANIMS.end());
|
||||
if (IT != ANIMS.end()) {
|
||||
const auto& A = IT->second;
|
||||
REQUIRE(A.frames.size() == 1);
|
||||
REQUIRE(A.controls_flip);
|
||||
REQUIRE(A.frames[0].flip_h);
|
||||
REQUIRE(A.frames[0].flip_v);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto main() -> int {
|
||||
std::cout << "== smoke tests: animation flip parser ==\n";
|
||||
|
||||
testBackwardCompat();
|
||||
testFlipAtAnimationLevel();
|
||||
testOverridePerFrame();
|
||||
testMappingDoesNotInherit();
|
||||
testCombinedVerticalAndHorizontal();
|
||||
|
||||
if (g_failures == 0) {
|
||||
std::cout << "ALL OK\n";
|
||||
return 0;
|
||||
}
|
||||
std::cerr << g_failures << " smoke check(s) failed\n";
|
||||
return 1;
|
||||
}
|
||||
Reference in new issue
Block a user