Skip to content

Commit a3c5afb

Browse files
committed
Add sprite layer methods
1 parent e6342bd commit a3c5afb

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed

include/scratchcpp/iengine.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ class LIBSCRATCHCPP_EXPORT IEngine
247247
*/
248248
virtual int findTarget(const std::string &targetName) const = 0;
249249

250+
/*! Moves the given sprite to the front layer. */
251+
virtual void moveSpriteToFront(Sprite *sprite) = 0;
252+
253+
/*! Moves the given sprite to the back layer. */
254+
virtual void moveSpriteToBack(Sprite *sprite) = 0;
255+
256+
/*! Moves the given sprite forward a number of layers. */
257+
virtual void moveSpriteForwardLayers(Sprite *sprite, int layers) = 0;
258+
259+
/*! Moves the given sprite backward a number of layers. */
260+
virtual void moveSpriteBackwardLayers(Sprite *sprite, int layers) = 0;
261+
262+
/*! Moves the given sprite behind some other sprite. */
263+
virtual void moveSpriteBehindOther(Sprite *sprite, Sprite *other) = 0;
264+
250265
/*! Returns the Stage. */
251266
virtual Stage *stage() const = 0;
252267

src/engine/internal/engine.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,101 @@ int Engine::findTarget(const std::string &targetName) const
853853
return -1;
854854
}
855855

856+
void Engine::moveSpriteToFront(Sprite *sprite)
857+
{
858+
if (!sprite || m_executableTargets.size() <= 2)
859+
return;
860+
861+
auto it = std::find(m_executableTargets.begin(), m_executableTargets.end(), sprite);
862+
863+
if (it != m_executableTargets.end()) {
864+
std::rotate(it, it + 1, m_executableTargets.end());
865+
updateSpriteLayerOrder();
866+
}
867+
}
868+
869+
void Engine::moveSpriteToBack(Sprite *sprite)
870+
{
871+
if (!sprite || m_executableTargets.size() <= 2)
872+
return;
873+
874+
auto it = std::find(m_executableTargets.begin(), m_executableTargets.end(), sprite);
875+
876+
if (it != m_executableTargets.end()) {
877+
std::rotate(m_executableTargets.begin() + 1, it, it + 1); // stage is always the first
878+
updateSpriteLayerOrder();
879+
}
880+
}
881+
882+
void Engine::moveSpriteForwardLayers(Sprite *sprite, int layers)
883+
{
884+
if (!sprite || layers == 0)
885+
return;
886+
887+
auto it = std::find(m_executableTargets.begin(), m_executableTargets.end(), sprite);
888+
889+
if (it == m_executableTargets.end())
890+
return;
891+
892+
auto target = it + layers;
893+
894+
if (target <= m_executableTargets.begin()) {
895+
moveSpriteToBack(sprite);
896+
return;
897+
}
898+
899+
if (target >= m_executableTargets.end()) {
900+
moveSpriteToFront(sprite);
901+
return;
902+
}
903+
904+
if (layers > 0)
905+
std::rotate(it, it + 1, target + 1);
906+
else
907+
std::rotate(target, it, it + 1);
908+
909+
updateSpriteLayerOrder();
910+
}
911+
912+
void Engine::moveSpriteBackwardLayers(Sprite *sprite, int layers)
913+
{
914+
moveSpriteForwardLayers(sprite, -layers);
915+
}
916+
917+
void Engine::moveSpriteBehindOther(Sprite *sprite, Sprite *other)
918+
{
919+
if (sprite == other)
920+
return;
921+
922+
auto itSprite = std::find(m_executableTargets.begin(), m_executableTargets.end(), sprite);
923+
auto itOther = std::find(m_executableTargets.begin(), m_executableTargets.end(), other);
924+
925+
if ((itSprite == m_executableTargets.end()) || (itOther == m_executableTargets.end()))
926+
return;
927+
928+
auto target = itOther - 1; // behind
929+
930+
if (target < itSprite)
931+
target++;
932+
933+
if (target <= m_executableTargets.begin()) {
934+
moveSpriteToBack(sprite);
935+
return;
936+
}
937+
938+
if (target >= m_executableTargets.end()) {
939+
moveSpriteToFront(sprite);
940+
return;
941+
}
942+
943+
if (target > itSprite)
944+
std::rotate(itSprite, itSprite + 1, target + 1);
945+
else
946+
std::rotate(target, itSprite, itSprite + 1);
947+
948+
updateSpriteLayerOrder();
949+
}
950+
856951
Stage *Engine::stage() const
857952
{
858953
auto it = std::find_if(m_targets.begin(), m_targets.end(), [](std::shared_ptr<Target> target) { return target && target->isStage(); });
@@ -986,6 +1081,14 @@ std::shared_ptr<IBlockSection> Engine::blockSection(const std::string &opcode) c
9861081
return nullptr;
9871082
}
9881083

1084+
void Engine::updateSpriteLayerOrder()
1085+
{
1086+
assert(m_executableTargets.empty() || m_executableTargets[0]->isStage());
1087+
1088+
for (size_t i = 1; i < m_executableTargets.size(); i++) // i = 1 to skip the stage
1089+
m_executableTargets[i]->setLayerOrder(i);
1090+
}
1091+
9891092
BlockSectionContainer *Engine::blockSectionContainer(const std::string &opcode) const
9901093
{
9911094
for (const auto &pair : m_sections) {

src/engine/internal/engine.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ class Engine : public IEngine
105105
Target *targetAt(int index) const override;
106106
int findTarget(const std::string &targetName) const override;
107107

108+
void moveSpriteToFront(Sprite *sprite) override;
109+
void moveSpriteToBack(Sprite *sprite) override;
110+
void moveSpriteForwardLayers(Sprite *sprite, int layers) override;
111+
void moveSpriteBackwardLayers(Sprite *sprite, int layers) override;
112+
void moveSpriteBehindOther(Sprite *sprite, Sprite *other) override;
113+
108114
Stage *stage() const override;
109115

110116
const std::vector<std::string> &extensions() const override;
@@ -132,6 +138,8 @@ class Engine : public IEngine
132138
std::shared_ptr<Entity> getEntity(const std::string &id);
133139
std::shared_ptr<IBlockSection> blockSection(const std::string &opcode) const;
134140

141+
void updateSpriteLayerOrder();
142+
135143
void updateFrameDuration();
136144
void addRunningScript(std::shared_ptr<VirtualMachine> vm);
137145
void startWhenKeyPressedScripts(const std::vector<Script *> &scripts);

test/engine/engine_test.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,193 @@ TEST(EngineTest, Targets)
718718
ASSERT_EQ(block2->engine(), &engine);
719719
}
720720

721+
void createTargets(Engine *engine, std::vector<Sprite *> &sprites)
722+
{
723+
auto stage = std::make_shared<Stage>();
724+
stage->setLayerOrder(0);
725+
auto sprite1 = std::make_shared<Sprite>();
726+
sprite1->setLayerOrder(1);
727+
auto sprite2 = std::make_shared<Sprite>();
728+
sprite2->setLayerOrder(5);
729+
auto sprite3 = std::make_shared<Sprite>();
730+
sprite3->setLayerOrder(3);
731+
auto sprite4 = std::make_shared<Sprite>();
732+
sprite4->setLayerOrder(4);
733+
auto sprite5 = std::make_shared<Sprite>();
734+
sprite5->setLayerOrder(2);
735+
736+
engine->setTargets({ stage, sprite1, sprite2, sprite3, sprite4, sprite5 });
737+
sprites = { sprite1.get(), sprite2.get(), sprite3.get(), sprite4.get(), sprite5.get() };
738+
739+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
740+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
741+
ASSERT_EQ(sprites[2]->layerOrder(), 3);
742+
ASSERT_EQ(sprites[3]->layerOrder(), 4);
743+
ASSERT_EQ(sprites[4]->layerOrder(), 2);
744+
}
745+
746+
TEST(EngineTest, MoveSpriteToFront)
747+
{
748+
Engine engine;
749+
std::vector<Sprite *> sprites;
750+
createTargets(&engine, sprites);
751+
752+
engine.moveSpriteToFront(sprites[2]);
753+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
754+
ASSERT_EQ(sprites[1]->layerOrder(), 4);
755+
ASSERT_EQ(sprites[2]->layerOrder(), 5);
756+
ASSERT_EQ(sprites[3]->layerOrder(), 3);
757+
ASSERT_EQ(sprites[4]->layerOrder(), 2);
758+
759+
for (int i = 0; i < 2; i++) {
760+
engine.moveSpriteToFront(sprites[0]);
761+
ASSERT_EQ(sprites[0]->layerOrder(), 5);
762+
ASSERT_EQ(sprites[1]->layerOrder(), 3);
763+
ASSERT_EQ(sprites[2]->layerOrder(), 4);
764+
ASSERT_EQ(sprites[3]->layerOrder(), 2);
765+
ASSERT_EQ(sprites[4]->layerOrder(), 1);
766+
}
767+
768+
auto stage = std::make_shared<Stage>();
769+
stage->setLayerOrder(0);
770+
auto sprite = std::make_shared<Sprite>();
771+
sprite->setLayerOrder(1);
772+
773+
engine.setTargets({ stage, sprite });
774+
engine.moveSpriteToFront(sprite.get());
775+
ASSERT_EQ(sprite->layerOrder(), 1);
776+
}
777+
778+
TEST(EngineTest, MoveSpriteToBack)
779+
{
780+
Engine engine;
781+
std::vector<Sprite *> sprites;
782+
createTargets(&engine, sprites);
783+
784+
engine.moveSpriteToBack(sprites[2]);
785+
ASSERT_EQ(sprites[0]->layerOrder(), 2);
786+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
787+
ASSERT_EQ(sprites[2]->layerOrder(), 1);
788+
ASSERT_EQ(sprites[3]->layerOrder(), 4);
789+
ASSERT_EQ(sprites[4]->layerOrder(), 3);
790+
791+
for (int i = 0; i < 1; i++) {
792+
engine.moveSpriteToBack(sprites[1]);
793+
ASSERT_EQ(sprites[0]->layerOrder(), 3);
794+
ASSERT_EQ(sprites[1]->layerOrder(), 1);
795+
ASSERT_EQ(sprites[2]->layerOrder(), 2);
796+
ASSERT_EQ(sprites[3]->layerOrder(), 5);
797+
ASSERT_EQ(sprites[4]->layerOrder(), 4);
798+
}
799+
800+
auto stage = std::make_shared<Stage>();
801+
stage->setLayerOrder(0);
802+
auto sprite = std::make_shared<Sprite>();
803+
sprite->setLayerOrder(1);
804+
805+
engine.setTargets({ stage, sprite });
806+
engine.moveSpriteToBack(sprite.get());
807+
ASSERT_EQ(sprite->layerOrder(), 1);
808+
}
809+
810+
TEST(EngineTest, MoveSpriteForwardLayers)
811+
{
812+
Engine engine;
813+
std::vector<Sprite *> sprites;
814+
createTargets(&engine, sprites);
815+
816+
engine.moveSpriteForwardLayers(sprites[4], 2);
817+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
818+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
819+
ASSERT_EQ(sprites[2]->layerOrder(), 2);
820+
ASSERT_EQ(sprites[3]->layerOrder(), 3);
821+
ASSERT_EQ(sprites[4]->layerOrder(), 4);
822+
823+
engine.moveSpriteForwardLayers(sprites[4], 2);
824+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
825+
ASSERT_EQ(sprites[1]->layerOrder(), 4);
826+
ASSERT_EQ(sprites[2]->layerOrder(), 2);
827+
ASSERT_EQ(sprites[3]->layerOrder(), 3);
828+
ASSERT_EQ(sprites[4]->layerOrder(), 5);
829+
830+
engine.moveSpriteForwardLayers(sprites[4], -3);
831+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
832+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
833+
ASSERT_EQ(sprites[2]->layerOrder(), 3);
834+
ASSERT_EQ(sprites[3]->layerOrder(), 4);
835+
ASSERT_EQ(sprites[4]->layerOrder(), 2);
836+
837+
engine.moveSpriteForwardLayers(sprites[2], -3);
838+
ASSERT_EQ(sprites[0]->layerOrder(), 2);
839+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
840+
ASSERT_EQ(sprites[2]->layerOrder(), 1);
841+
ASSERT_EQ(sprites[3]->layerOrder(), 4);
842+
ASSERT_EQ(sprites[4]->layerOrder(), 3);
843+
}
844+
845+
TEST(EngineTest, MoveSpriteBackwardLayers)
846+
{
847+
Engine engine;
848+
std::vector<Sprite *> sprites;
849+
createTargets(&engine, sprites);
850+
851+
engine.moveSpriteBackwardLayers(sprites[4], -2);
852+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
853+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
854+
ASSERT_EQ(sprites[2]->layerOrder(), 2);
855+
ASSERT_EQ(sprites[3]->layerOrder(), 3);
856+
ASSERT_EQ(sprites[4]->layerOrder(), 4);
857+
858+
engine.moveSpriteBackwardLayers(sprites[4], -2);
859+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
860+
ASSERT_EQ(sprites[1]->layerOrder(), 4);
861+
ASSERT_EQ(sprites[2]->layerOrder(), 2);
862+
ASSERT_EQ(sprites[3]->layerOrder(), 3);
863+
ASSERT_EQ(sprites[4]->layerOrder(), 5);
864+
865+
engine.moveSpriteBackwardLayers(sprites[4], 3);
866+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
867+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
868+
ASSERT_EQ(sprites[2]->layerOrder(), 3);
869+
ASSERT_EQ(sprites[3]->layerOrder(), 4);
870+
ASSERT_EQ(sprites[4]->layerOrder(), 2);
871+
872+
engine.moveSpriteBackwardLayers(sprites[2], 3);
873+
ASSERT_EQ(sprites[0]->layerOrder(), 2);
874+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
875+
ASSERT_EQ(sprites[2]->layerOrder(), 1);
876+
ASSERT_EQ(sprites[3]->layerOrder(), 4);
877+
ASSERT_EQ(sprites[4]->layerOrder(), 3);
878+
}
879+
880+
TEST(EngineTest, MoveSpriteBehindOther)
881+
{
882+
Engine engine;
883+
std::vector<Sprite *> sprites;
884+
createTargets(&engine, sprites);
885+
886+
engine.moveSpriteBehindOther(sprites[4], sprites[3]);
887+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
888+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
889+
ASSERT_EQ(sprites[2]->layerOrder(), 2);
890+
ASSERT_EQ(sprites[3]->layerOrder(), 4);
891+
ASSERT_EQ(sprites[4]->layerOrder(), 3);
892+
893+
engine.moveSpriteBehindOther(sprites[3], sprites[2]);
894+
ASSERT_EQ(sprites[0]->layerOrder(), 1);
895+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
896+
ASSERT_EQ(sprites[2]->layerOrder(), 3);
897+
ASSERT_EQ(sprites[3]->layerOrder(), 2);
898+
ASSERT_EQ(sprites[4]->layerOrder(), 4);
899+
900+
engine.moveSpriteBehindOther(sprites[4], sprites[0]);
901+
ASSERT_EQ(sprites[0]->layerOrder(), 2);
902+
ASSERT_EQ(sprites[1]->layerOrder(), 5);
903+
ASSERT_EQ(sprites[2]->layerOrder(), 4);
904+
ASSERT_EQ(sprites[3]->layerOrder(), 3);
905+
ASSERT_EQ(sprites[4]->layerOrder(), 1);
906+
}
907+
721908
TEST(EngineTest, Stage)
722909
{
723910
Engine engine;

test/mocks/enginemock.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ class EngineMock : public IEngine
8989
MOCK_METHOD(Target *, targetAt, (int), (const, override));
9090
MOCK_METHOD(int, findTarget, (const std::string &), (const, override));
9191

92+
MOCK_METHOD(void, moveSpriteToFront, (Sprite * sprite), (override));
93+
MOCK_METHOD(void, moveSpriteToBack, (Sprite * sprite), (override));
94+
MOCK_METHOD(void, moveSpriteForwardLayers, (Sprite * sprite, int layers), (override));
95+
MOCK_METHOD(void, moveSpriteBackwardLayers, (Sprite * sprite, int layers), (override));
96+
MOCK_METHOD(void, moveSpriteBehindOther, (Sprite * sprite, Sprite *other), (override));
97+
9298
MOCK_METHOD(Stage *, stage, (), (const, override));
9399

94100
MOCK_METHOD(std::vector<std::string> &, extensions, (), (const, override));

0 commit comments

Comments
 (0)