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* origTarget_ = nullptr;
i32 tag_ = -1;
void copyTagTo(Act* other) const { other->tag_ = tag_; }
};
class ActInstant : public Act {

View File

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

View File

@ -21,8 +21,16 @@ public:
}
}
Act* clone() const override { return new MoveTo(dur_, end_); }
Act* reverse() const override { return new MoveTo(dur_, start_); }
Act* clone() const override {
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:
Vec2 start_;
@ -44,8 +52,16 @@ public:
}
}
Act* clone() const override { return new MoveBy(dur_, dlt_); }
Act* reverse() const override { return new MoveBy(dur_, -dlt_); }
Act* clone() const override {
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:
Vec2 dlt_;

View File

@ -42,7 +42,7 @@ public:
void update(f32 t) override {
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*(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 Vec2 &v) {

View File

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

View File

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

View File

@ -31,11 +31,7 @@ Mat3 Node::local() const {
Mat3 m(1.0f);
m = glm::translate(m, pos.toGlm());
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::rotate(m, rot * DEG_TO_RAD);
m = glm::scale(m, scale.toGlm());
return m;

View File

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

View File

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

View File

@ -267,13 +267,22 @@ TEST(Director, MultipleSceneSwitches) {
scene1->setName("Scene1");
director.run(scene1);
std::vector<Ref<TestScene>> scenes;
scenes.push_back(scene1);
for (int i = 0; i < 5; ++i) {
auto newScene = ptr::make<TestScene>();
newScene->setName("Scene" + std::to_string(i + 2));
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;
void onInit() override {
anchor = defaultAnchor();
}
protected:
Vec2 defaultAnchor() const override { return Vec2(0.0f, 0.0f); }
};