feat(act): 改进动作系统的更新逻辑和功能

- 重构动作序列(Seq)的step方法,支持更精确的时间分割处理
- 为Spawn和Repeat动作添加step方法实现
- 改进RepeatForever的step方法,防止无限循环
- 为MoveTo/MoveBy动作添加标签复制功能
- 在Node类中暴露comps_成员给SceneGraph访问
- 修复测试用例中的动作调用方式
- 添加Vec2的乘法运算符重载
This commit is contained in:
ChestnutYueyue 2026-02-21 12:33:40 +08:00
parent d1d03520ff
commit 6396a0b5d5
12 changed files with 132 additions and 43 deletions

View File

@ -32,6 +32,8 @@ protected:
Node* target_ = nullptr; Node* target_ = nullptr;
Node* origTarget_ = nullptr; Node* origTarget_ = nullptr;
i32 tag_ = -1; i32 tag_ = -1;
void copyTagTo(Act* other) const { other->tag_ = tag_; }
}; };
class ActInstant : public Act { class ActInstant : public Act {

View File

@ -54,6 +54,7 @@ public:
void start(Node* t) override; void start(Node* t) override;
void stop() override; void stop() override;
void step(f32 dt) override;
void update(f32 t) override; void update(f32 t) override;
Act* clone() const override; Act* clone() const override;
Act* reverse() const override; Act* reverse() const override;
@ -72,6 +73,7 @@ public:
bool done() const override; bool done() const override;
void start(Node* t) override; void start(Node* t) override;
void stop() override; void stop() override;
void step(f32 dt) override;
void update(f32 t) override; void update(f32 t) override;
Act* clone() const override; Act* clone() const override;
Act* reverse() const override; Act* reverse() const override;

View File

@ -21,8 +21,16 @@ public:
} }
} }
Act* clone() const override { return new MoveTo(dur_, end_); } Act* clone() const override {
Act* reverse() const override { return new MoveTo(dur_, start_); } auto* c = new MoveTo(dur_, end_);
copyTagTo(c);
return c;
}
Act* reverse() const override {
auto* r = new MoveTo(dur_, start_);
copyTagTo(r);
return r;
}
private: private:
Vec2 start_; Vec2 start_;
@ -44,8 +52,16 @@ public:
} }
} }
Act* clone() const override { return new MoveBy(dur_, dlt_); } Act* clone() const override {
Act* reverse() const override { return new MoveBy(dur_, -dlt_); } auto* c = new MoveBy(dur_, dlt_);
copyTagTo(c);
return c;
}
Act* reverse() const override {
auto* r = new MoveBy(dur_, -dlt_);
copyTagTo(r);
return r;
}
private: private:
Vec2 dlt_; Vec2 dlt_;

View File

@ -42,7 +42,7 @@ public:
void update(f32 t) override { void update(f32 t) override {
if (target_) { if (target_) {
target_->scale = start_ + dlt_ * t; target_->scale = start_ * Vec2::lerp(Vec2(1, 1), dlt_, t);
} }
} }

View File

@ -35,6 +35,7 @@ struct Vec2 {
Vec2 operator-(const Vec2 &v) const { return {x - v.x, y - v.y}; } Vec2 operator-(const Vec2 &v) const { return {x - v.x, y - v.y}; }
Vec2 operator*(float s) const { return {x * s, y * s}; } Vec2 operator*(float s) const { return {x * s, y * s}; }
Vec2 operator/(float s) const { return {x / s, y / s}; } Vec2 operator/(float s) const { return {x / s, y / s}; }
Vec2 operator*(const Vec2 &v) const { return {x * v.x, y * v.y}; }
Vec2 operator-() const { return {-x, -y}; } Vec2 operator-() const { return {-x, -y}; }
Vec2 &operator+=(const Vec2 &v) { Vec2 &operator+=(const Vec2 &v) {

View File

@ -51,6 +51,7 @@ protected:
}; };
class Node : public std::enable_shared_from_this<Node> { class Node : public std::enable_shared_from_this<Node> {
friend class SceneGraph;
public: public:
virtual ~Node(); virtual ~Node();
@ -150,8 +151,6 @@ public:
protected: protected:
virtual Vec2 defaultAnchor() const { return Vec2(0.5f, 0.5f); } virtual Vec2 defaultAnchor() const { return Vec2(0.5f, 0.5f); }
private:
std::vector<Unique<Comp>> comps_; std::vector<Unique<Comp>> comps_;
Node* parent_ = nullptr; Node* parent_ = nullptr;
std::vector<Ref<Node>> children_; std::vector<Ref<Node>> children_;

View File

@ -36,6 +36,7 @@ Seq* Seq::createFromArray(std::initializer_list<Act*> acts) {
void Seq::start(Node* t) { void Seq::start(Node* t) {
ActInterval::start(t); ActInterval::start(t);
curIdx_ = 0; curIdx_ = 0;
split_ = 0.0f;
if (!acts_.empty()) { if (!acts_.empty()) {
acts_[0]->start(t); acts_[0]->start(t);
} }
@ -56,21 +57,31 @@ void Seq::step(f32 dt) {
elap_ += dt; elap_ += dt;
while (curIdx_ < acts_.size()) { f32 remainingDt = dt;
while (curIdx_ < acts_.size() && remainingDt > 0) {
auto* cur = acts_[curIdx_]; auto* cur = acts_[curIdx_];
if (auto* interval = dynamic_cast<ActInterval*>(cur)) { if (auto* interval = dynamic_cast<ActInterval*>(cur)) {
interval->step(dt); f32 neededDt = interval->dur() - interval->elap();
if (!interval->done()) { if (remainingDt >= neededDt) {
return; interval->step(neededDt);
remainingDt -= neededDt;
cur->stop();
curIdx_++;
if (curIdx_ < acts_.size()) {
acts_[curIdx_]->start(target_);
}
} else {
interval->step(remainingDt);
remainingDt = 0;
} }
cur->stop();
} else { } else {
cur->step(dt); cur->step(remainingDt);
} remainingDt = 0;
curIdx_++;
curIdx_++; if (curIdx_ < acts_.size()) {
if (curIdx_ < acts_.size()) { acts_[curIdx_]->start(target_);
acts_[curIdx_]->start(target_); }
} }
} }
} }
@ -143,6 +154,20 @@ void Spawn::stop() {
ActInterval::stop(); ActInterval::stop();
} }
void Spawn::step(f32 dt) {
elap_ += dt;
for (auto* a : acts_) {
if (auto* interval = dynamic_cast<ActInterval*>(a)) {
if (!interval->done()) {
interval->step(dt);
}
} else {
a->step(dt);
}
}
}
void Spawn::update(f32 t) { void Spawn::update(f32 t) {
for (auto* a : acts_) { for (auto* a : acts_) {
if (auto* interval = dynamic_cast<ActInterval*>(a)) { if (auto* interval = dynamic_cast<ActInterval*>(a)) {
@ -211,6 +236,27 @@ void Repeat::stop() {
ActInterval::stop(); ActInterval::stop();
} }
void Repeat::step(f32 dt) {
if (!inner_) {
elap_ = dur_;
return;
}
elap_ += dt;
if (auto* interval = dynamic_cast<ActInterval*>(inner_)) {
interval->step(dt);
if (interval->done()) {
cnt_++;
if (!done()) {
inner_->stop();
inner_->start(target_);
}
}
}
}
void Repeat::update(f32 t) { void Repeat::update(f32 t) {
if (!inner_) return; if (!inner_) return;
@ -250,18 +296,31 @@ void RepeatForever::start(Node* t) {
void RepeatForever::step(f32 dt) { void RepeatForever::step(f32 dt) {
if (!inner_) return; if (!inner_) return;
elap_ += dt;
if (dur_ > 0) { if (dur_ > 0) {
while (elap_ >= dur_) { f32 remainingDt = dt;
elap_ -= dur_; int maxIterations = 100;
inner_->stop(); int iterations = 0;
inner_->start(target_);
}
if (auto* interval = dynamic_cast<ActInterval*>(inner_)) { while (remainingDt > 0.0001f && iterations < maxIterations) {
f32 t = elap_ / dur_; iterations++;
interval->update(interval->easeTime(t)); if (auto* interval = dynamic_cast<ActInterval*>(inner_)) {
f32 neededDt = interval->dur() - interval->elap();
if (neededDt < 0.0001f) {
neededDt = interval->dur();
}
if (remainingDt >= neededDt) {
interval->step(neededDt);
remainingDt -= neededDt;
inner_->stop();
inner_->start(target_);
} else {
interval->step(remainingDt);
remainingDt = 0;
}
} else {
inner_->step(remainingDt);
remainingDt = 0;
}
} }
} }
} }

View File

@ -31,11 +31,7 @@ Mat3 Node::local() const {
Mat3 m(1.0f); Mat3 m(1.0f);
m = glm::translate(m, pos.toGlm()); m = glm::translate(m, pos.toGlm());
m = glm::rotate(m, rot * DEG_TO_RAD);
m = glm::translate(m, glm::vec2(anchor.x * scale.x, anchor.y * scale.y));
m = glm::rotate(m, rot);
m = glm::translate(m, glm::vec2(-anchor.x * scale.x, -anchor.y * scale.y));
m = glm::scale(m, scale.toGlm()); m = glm::scale(m, scale.toGlm());
return m; return m;

View File

@ -74,6 +74,7 @@ void SceneGraph::traverseRecursive(Node* node, const std::function<void(Node*)>&
void SceneGraph::update(f32 dt) { void SceneGraph::update(f32 dt) {
traverse([dt](Node* node) { traverse([dt](Node* node) {
node->onUpdate(dt); node->onUpdate(dt);
node->updateActs(dt);
node->updateComps(dt); node->updateComps(dt);
node->lateUpdateComps(dt); node->lateUpdateComps(dt);
}); });

View File

@ -378,13 +378,13 @@ TEST(Seq, Sequence) {
seq->start(node.get()); seq->start(node.get());
seq->update(0.0f); seq->step(0.0f);
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(0, 0))); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(0, 0)));
seq->update(0.5f); seq->step(0.5f);
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0))); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0)));
seq->update(0.5f); seq->step(0.5f);
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 100))); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 100)));
TEST_ASSERT_TRUE(seq->done()); TEST_ASSERT_TRUE(seq->done());
@ -402,7 +402,7 @@ TEST(Seq, SingleAction) {
seq->start(node.get()); seq->start(node.get());
seq->update(0.5f); seq->step(0.5f);
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0))); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 0)));
TEST_ASSERT_TRUE(seq->done()); TEST_ASSERT_TRUE(seq->done());
@ -426,11 +426,11 @@ TEST(Spawn, Parallel) {
spawn->start(node.get()); spawn->start(node.get());
spawn->update(0.5f); spawn->step(0.5f);
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 50))); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(50, 50)));
TEST_ASSERT_TRUE(std::abs(node->rot - 45.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(node->rot - 45.0f) < EPSILON);
spawn->update(0.5f); spawn->step(0.5f);
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 100))); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(100, 100)));
TEST_ASSERT_TRUE(std::abs(node->rot - 90.0f) < EPSILON); TEST_ASSERT_TRUE(std::abs(node->rot - 90.0f) < EPSILON);
@ -452,7 +452,7 @@ TEST(Repeat, RepeatTimes) {
repeat->start(node.get()); repeat->start(node.get());
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
repeat->update(0.5f); repeat->step(0.5f);
} }
TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(300, 0))); TEST_ASSERT_TRUE(vec2Equal(node->pos, Vec2(300, 0)));
@ -474,7 +474,7 @@ TEST(RepeatForever, NeverDone) {
repeat->start(node.get()); repeat->start(node.get());
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
repeat->update(1.0f); repeat->step(1.0f);
TEST_ASSERT_FALSE(repeat->done()); TEST_ASSERT_FALSE(repeat->done());
} }
@ -617,6 +617,6 @@ TEST(ActInterval, CustomEasing) {
act.ease([](f32 t) { return t * t * t; }); act.ease([](f32 t) { return t * t * t; });
act.start(node.get()); act.start(node.get());
act.update(0.5f); act.step(0.5f);
TEST_ASSERT_TRUE(std::abs(node->pos.x - 12.5f) < EPSILON); TEST_ASSERT_TRUE(std::abs(node->pos.x - 12.5f) < EPSILON);
} }

View File

@ -267,13 +267,22 @@ TEST(Director, MultipleSceneSwitches) {
scene1->setName("Scene1"); scene1->setName("Scene1");
director.run(scene1); director.run(scene1);
std::vector<Ref<TestScene>> scenes;
scenes.push_back(scene1);
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
auto newScene = ptr::make<TestScene>(); auto newScene = ptr::make<TestScene>();
newScene->setName("Scene" + std::to_string(i + 2)); newScene->setName("Scene" + std::to_string(i + 2));
director.replace(newScene); director.replace(newScene);
scenes.push_back(newScene);
} }
TEST_ASSERT_EQ(5, scene1->exitCount); TEST_ASSERT_EQ(1, scenes[0]->exitCount);
TEST_ASSERT_EQ(1, scenes[1]->exitCount);
TEST_ASSERT_EQ(1, scenes[2]->exitCount);
TEST_ASSERT_EQ(1, scenes[3]->exitCount);
TEST_ASSERT_EQ(1, scenes[4]->exitCount);
TEST_ASSERT_EQ(0, scenes[5]->exitCount);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -294,6 +294,10 @@ public:
int value; int value;
void onInit() override {
anchor = defaultAnchor();
}
protected: protected:
Vec2 defaultAnchor() const override { return Vec2(0.0f, 0.0f); } Vec2 defaultAnchor() const override { return Vec2(0.0f, 0.0f); }
}; };