/** * @file test_action.cpp * @brief Action 系统单元测试 * * 测试动作系统的基础类、即时动作、间隔动作、复合动作等功能。 */ #include "test_framework.h" #include #include #include #include #include #include #include #include #include #include using namespace extra2d; using namespace extra2d::test; namespace { constexpr f32 EPSILON = 0.001f; bool vec2Equal(const Vec2& a, const Vec2& b, f32 eps = EPSILON) { return std::abs(a.x - b.x) < eps && std::abs(a.y - b.y) < eps; } } // --------------------------------------------------------------------------- // Act 基础类测试 // --------------------------------------------------------------------------- TEST(Act, Tag) { auto node = ptr::make(); class TestAct : public Act { public: bool done() const override { return true; } void update(f32 t) override {} Act* clone() const override { return new TestAct(); } Act* reverse() const override { return new TestAct(); } }; TestAct act; TEST_ASSERT_EQ(-1, act.tag()); act.tag(42); TEST_ASSERT_EQ(42, act.tag()); } // --------------------------------------------------------------------------- // ActInstant 测试 // --------------------------------------------------------------------------- TEST(ActInstant, Done) { CallFunc act([](){}); TEST_ASSERT_TRUE(act.done()); } TEST(CallFunc, Execute) { auto node = ptr::make(); bool called = false; CallFunc act([&called](){ called = true; }); act.start(node.get()); act.step(0.016f); TEST_ASSERT_TRUE(called); } TEST(Place, SetPosition) { auto node = ptr::make(); node->setPos(0, 0); Place act(Vec2(100, 200)); act.start(node.get()); act.step(0.016f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 200))); } TEST(Show, SetVisible) { auto node = ptr::make(); node->setVisible(false); Show act; act.start(node.get()); act.step(0.016f); TEST_ASSERT_TRUE(node->visible()); } TEST(Hide, SetInvisible) { auto node = ptr::make(); node->setVisible(true); Hide act; act.start(node.get()); act.step(0.016f); TEST_ASSERT_FALSE(node->visible()); } // --------------------------------------------------------------------------- // ActInterval 基础测试 // --------------------------------------------------------------------------- TEST(ActInterval, Duration) { MoveTo act(2.0f, Vec2(100, 100)); TEST_ASSERT_TRUE(std::abs(act.dur() - 2.0f) < EPSILON); TEST_ASSERT_FALSE(act.done()); } TEST(ActInterval, Elapsed) { MoveTo act(1.0f, Vec2(100, 100)); TEST_ASSERT_TRUE(std::abs(act.elap()) < EPSILON); act.step(0.5f); TEST_ASSERT_TRUE(std::abs(act.elap() - 0.5f) < EPSILON); } TEST(ActInterval, Done) { MoveTo act(1.0f, Vec2(100, 100)); TEST_ASSERT_FALSE(act.done()); act.step(1.0f); TEST_ASSERT_TRUE(act.done()); act.step(0.5f); TEST_ASSERT_TRUE(act.done()); } // --------------------------------------------------------------------------- // MoveTo/MoveBy 测试 // --------------------------------------------------------------------------- TEST(MoveTo, MoveToPosition) { auto node = ptr::make(); node->setPos(0, 0); MoveTo act(1.0f, Vec2(100, 200)); act.start(node.get()); act.update(0.0f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(0, 0))); act.update(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 100))); act.update(1.0f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 200))); } TEST(MoveBy, MoveByOffset) { auto node = ptr::make(); node->setPos(50, 50); MoveBy act(1.0f, Vec2(100, 100)); act.start(node.get()); act.update(0.0f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 50))); act.update(1.0f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(150, 150))); } TEST(MoveTo, Clone) { MoveTo act(2.0f, Vec2(100, 200)); act.tag(42); Act* cloned = act.clone(); TEST_ASSERT_NOT_NULL(cloned); TEST_ASSERT_EQ(42, cloned->tag()); MoveTo* clonedMove = dynamic_cast(cloned); TEST_ASSERT_NOT_NULL(clonedMove); delete cloned; } TEST(MoveBy, Reverse) { MoveBy act(1.0f, Vec2(100, 100)); Act* reversed = act.reverse(); TEST_ASSERT_NOT_NULL(reversed); MoveBy* reversedMove = dynamic_cast(reversed); TEST_ASSERT_NOT_NULL(reversedMove); delete reversed; } // --------------------------------------------------------------------------- // RotTo/RotBy 测试 // --------------------------------------------------------------------------- TEST(RotTo, RotateToAngle) { auto node = ptr::make(); node->setRot(0); RotTo act(1.0f, 90.0f); act.start(node.get()); act.update(0.0f); TEST_ASSERT_TRUE(std::abs(node->rot) < EPSILON); act.update(0.5f); TEST_ASSERT_TRUE(std::abs(node->rot - 45.0f) < EPSILON); act.update(1.0f); TEST_ASSERT_TRUE(std::abs(node->rot - 90.0f) < EPSILON); } TEST(RotBy, RotateByAngle) { auto node = ptr::make(); node->setRot(45.0f); RotBy act(1.0f, 90.0f); act.start(node.get()); act.update(0.0f); TEST_ASSERT_TRUE(std::abs(node->rot - 45.0f) < EPSILON); act.update(1.0f); TEST_ASSERT_TRUE(std::abs(node->rot - 135.0f) < EPSILON); } // --------------------------------------------------------------------------- // ScaleTo/ScaleBy 测试 // --------------------------------------------------------------------------- TEST(ScaleTo, ScaleToValue) { auto node = ptr::make(); node->setScale(1.0f); ScaleTo act(1.0f, 2.0f); act.start(node.get()); act.update(0.0f); TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(1, 1))); act.update(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(1.5f, 1.5f))); act.update(1.0f); TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(2, 2))); } TEST(ScaleBy, ScaleByValue) { auto node = ptr::make(); node->setScale(1.0f); ScaleBy act(1.0f, 2.0f); act.start(node.get()); act.update(1.0f); TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(2, 2))); } TEST(ScaleTo, ScaleXY) { auto node = ptr::make(); node->setScale(1.0f); ScaleTo act(1.0f, Vec2(2.0f, 3.0f)); act.start(node.get()); act.update(1.0f); TEST_ASSERT_TRUE(vec2Equal(node->scale, Vec2(2, 3))); } // --------------------------------------------------------------------------- // FadeTo/FadeIn/FadeOut 测试 // --------------------------------------------------------------------------- TEST(FadeTo, FadeToOpacity) { auto node = ptr::make(); FadeTo act(1.0f, 0.5f); act.start(node.get()); act.update(0.0f); act.update(1.0f); } TEST(FadeIn, FadeIn) { auto node = ptr::make(); FadeIn act(1.0f); act.start(node.get()); act.update(1.0f); } TEST(FadeOut, FadeOut) { auto node = ptr::make(); FadeOut act(1.0f); act.start(node.get()); act.update(1.0f); } // --------------------------------------------------------------------------- // Easing 函数测试 // --------------------------------------------------------------------------- TEST(Easing, Linear) { TEST_ASSERT_TRUE(std::abs(ActInterval::easeLinear(0.0f) - 0.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeLinear(0.5f) - 0.5f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeLinear(1.0f) - 1.0f) < EPSILON); } TEST(Easing, EaseIn) { TEST_ASSERT_TRUE(std::abs(ActInterval::easeIn(0.0f) - 0.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeIn(0.5f) - 0.25f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeIn(1.0f) - 1.0f) < EPSILON); } TEST(Easing, EaseOut) { TEST_ASSERT_TRUE(std::abs(ActInterval::easeOut(0.0f) - 0.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeOut(0.5f) - 0.75f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeOut(1.0f) - 1.0f) < EPSILON); } TEST(Easing, EaseInOut) { TEST_ASSERT_TRUE(std::abs(ActInterval::easeInOut(0.0f) - 0.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeInOut(0.25f) - 0.125f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeInOut(0.5f) - 0.5f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeInOut(0.75f) - 0.875f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeInOut(1.0f) - 1.0f) < EPSILON); } TEST(Easing, EaseBackIn) { f32 result = ActInterval::easeBackIn(0.5f); TEST_ASSERT_TRUE(result < 0.5f); } TEST(Easing, EaseBackOut) { f32 result = ActInterval::easeBackOut(0.5f); TEST_ASSERT_TRUE(result > 0.5f); } TEST(Easing, EaseBounceOut) { TEST_ASSERT_TRUE(std::abs(ActInterval::easeBounceOut(0.0f) - 0.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeBounceOut(1.0f) - 1.0f) < EPSILON); } TEST(Easing, EaseElasticOut) { TEST_ASSERT_TRUE(std::abs(ActInterval::easeElasticOut(0.0f) - 0.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(ActInterval::easeElasticOut(1.0f) - 1.0f) < EPSILON); } // --------------------------------------------------------------------------- // Seq 序列动作测试 // --------------------------------------------------------------------------- TEST(Seq, Sequence) { auto node = ptr::make(); node->setPos(0, 0); auto seq = Seq::create( new MoveTo(0.5f, Vec2(100, 0)), new MoveTo(0.5f, Vec2(100, 100)) ); seq->start(node.get()); seq->step(0.0f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(0, 0))); seq->step(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0))); seq->step(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 100))); TEST_ASSERT_TRUE(seq->done()); delete seq; } TEST(Seq, SingleAction) { auto node = ptr::make(); node->setPos(0, 0); auto seq = Seq::create( new MoveTo(0.5f, Vec2(100, 0)) ); seq->start(node.get()); seq->step(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0))); TEST_ASSERT_TRUE(seq->done()); delete seq; } // --------------------------------------------------------------------------- // Spawn 并行动作测试 // --------------------------------------------------------------------------- TEST(Spawn, Parallel) { auto node = ptr::make(); node->setPos(0, 0); node->setRot(0); auto spawn = Spawn::create( new MoveTo(1.0f, Vec2(100, 100)), new RotTo(1.0f, 90.0f) ); spawn->start(node.get()); spawn->step(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 50))); TEST_ASSERT_TRUE(std::abs(node->rot - 45.0f) < EPSILON); spawn->step(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 100))); TEST_ASSERT_TRUE(std::abs(node->rot - 90.0f) < EPSILON); TEST_ASSERT_TRUE(spawn->done()); delete spawn; } // --------------------------------------------------------------------------- // Repeat 重复动作测试 // --------------------------------------------------------------------------- TEST(Repeat, RepeatTimes) { auto node = ptr::make(); node->setPos(0, 0); auto repeat = new Repeat(new MoveBy(0.5f, Vec2(100, 0)), 3); repeat->start(node.get()); for (int i = 0; i < 3; ++i) { repeat->step(0.5f); } TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(300, 0))); TEST_ASSERT_TRUE(repeat->done()); delete repeat; } // --------------------------------------------------------------------------- // RepeatForever 无限重复测试 // --------------------------------------------------------------------------- TEST(RepeatForever, NeverDone) { auto node = ptr::make(); node->setPos(0, 0); auto repeat = new RepeatForever(new MoveBy(1.0f, Vec2(100, 0))); repeat->start(node.get()); for (int i = 0; i < 10; ++i) { repeat->step(1.0f); TEST_ASSERT_FALSE(repeat->done()); } TEST_ASSERT_TRUE(node->pos.x > 900); delete repeat; } // --------------------------------------------------------------------------- // Speed 速度控制测试 // --------------------------------------------------------------------------- TEST(Speed, DoubleSpeed) { auto node = ptr::make(); node->setPos(0, 0); auto speed = new Speed(new MoveTo(2.0f, Vec2(100, 0)), 2.0f); speed->start(node.get()); speed->step(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 0))); speed->step(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0))); TEST_ASSERT_TRUE(speed->done()); delete speed; } TEST(Speed, HalfSpeed) { auto node = ptr::make(); node->setPos(0, 0); auto speed = new Speed(new MoveTo(1.0f, Vec2(100, 0)), 0.5f); speed->start(node.get()); speed->step(1.0f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 0))); TEST_ASSERT_FALSE(speed->done()); speed->step(1.0f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0))); TEST_ASSERT_TRUE(speed->done()); delete speed; } // --------------------------------------------------------------------------- // Node 动作管理测试 // --------------------------------------------------------------------------- TEST(Node, RunAction) { auto node = ptr::make(); node->setPos(0, 0); MoveTo* act = new MoveTo(1.0f, Vec2(100, 100)); Act* returned = node->run(act); TEST_ASSERT_EQ(act, returned); TEST_ASSERT_EQ(1, node->runningActs()); } TEST(Node, StopAction) { auto node = ptr::make(); node->setPos(0, 0); MoveTo* act = new MoveTo(1.0f, Vec2(100, 100)); act->tag(42); node->run(act); node->stop(act); TEST_ASSERT_EQ(0, node->runningActs()); } TEST(Node, StopByTag) { auto node = ptr::make(); MoveTo* act = new MoveTo(1.0f, Vec2(100, 100)); act->tag(42); node->run(act); node->stopByTag(42); TEST_ASSERT_EQ(0, node->runningActs()); } TEST(Node, StopAll) { auto node = ptr::make(); node->run(new MoveTo(1.0f, Vec2(100, 0))); node->run(new RotTo(1.0f, 90.0f)); node->run(new ScaleTo(1.0f, 2.0f)); TEST_ASSERT_EQ(3, node->runningActs()); node->stopAll(); TEST_ASSERT_EQ(0, node->runningActs()); } TEST(Node, GetActByTag) { auto node = ptr::make(); MoveTo* act = new MoveTo(1.0f, Vec2(100, 100)); act->tag(42); node->run(act); Act* found = node->getActByTag(42); TEST_ASSERT_EQ(act, found); Act* notFound = node->getActByTag(999); TEST_ASSERT_NULL(notFound); } TEST(Node, UpdateActions) { auto node = ptr::make(); node->setPos(0, 0); node->run(new MoveTo(1.0f, Vec2(100, 0))); node->updateActs(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 0))); node->updateActs(0.5f); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0))); } // --------------------------------------------------------------------------- // 自定义缓动测试 // --------------------------------------------------------------------------- TEST(ActInterval, CustomEasing) { auto node = ptr::make(); node->setPos(0, 0); MoveTo act(1.0f, Vec2(100, 0)); act.ease([](f32 t) { return t * t * t; }); act.start(node.get()); act.step(0.5f); TEST_ASSERT_TRUE(std::abs(node->pos.x - 12.5f) < EPSILON); }