Skip to content

Commit 991dd55

Browse files
authored
Merge pull request #6 from SgtCoDFish/sortediterating
Add SortedIteratingSystem
2 parents b0bed72 + 9a77b8d commit 991dd55

File tree

5 files changed

+340
-1
lines changed

5 files changed

+340
-1
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*******************************************************************************
2+
* Copyright 2017 See AUTHORS file.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
******************************************************************************/
16+
17+
#ifndef ACPP_SYSTEMS_SORTEDITERATINGSYSTEM_HPP_
18+
#define ACPP_SYSTEMS_SORTEDITERATINGSYSTEM_HPP_
19+
20+
#include <cstdint>
21+
22+
#include <vector>
23+
#include <functional>
24+
#include <algorithm>
25+
26+
#include "Ashley/systems/IteratingSystem.hpp"
27+
#include "Ashley/core/EntityListener.hpp"
28+
29+
namespace ashley {
30+
31+
/**
32+
* <p>A simple EntitySystem that processes each entity of a given family in the order specified by a comparator and calls
33+
* processEntity() for each entity every time the EntitySystem is updated. This is really just a convenience class as rendering
34+
* systems tend to iterate over a list of entities in a sorted manner. Adding entities will cause the entity list to be resorted.
35+
* Call forceSort() if you changed your sorting criteria.</p>
36+
*
37+
* <em>Java author: Santo Pfingsten</em>
38+
* @author Ashley Davis (SgtCoDFish)
39+
*/
40+
class SortedIteratingSystem : public ashley::IteratingSystem, public ashley::EntityListener {
41+
public:
42+
using Comparator = std::function<bool(ashley::Entity *, ashley::Entity *)>;
43+
44+
SortedIteratingSystem(ashley::Family *family, Comparator comparator, int64_t priority) :
45+
IteratingSystem(family, priority),
46+
comparator { comparator } {
47+
}
48+
49+
virtual ~SortedIteratingSystem() = default;
50+
51+
SortedIteratingSystem(const SortedIteratingSystem &other) = default;
52+
SortedIteratingSystem(SortedIteratingSystem &&other) = default;
53+
54+
SortedIteratingSystem& operator=(const SortedIteratingSystem &other) = default;
55+
SortedIteratingSystem& operator=(SortedIteratingSystem &&other) = default;
56+
57+
void forceSort() {
58+
shouldSort = true;
59+
}
60+
61+
virtual void addedToEngine(ashley::Engine &engine) override;
62+
virtual void removedFromEngine(ashley::Engine &engine) override;
63+
64+
virtual void entityAdded(ashley::Entity &entity) override;
65+
virtual void entityRemoved(ashley::Entity &entity) override;
66+
67+
virtual void update(float deltaTime) override;
68+
69+
protected:
70+
Comparator comparator;
71+
72+
std::vector<Entity *> sortedEntities;
73+
74+
bool shouldSort { false };
75+
76+
private:
77+
void sort();
78+
};
79+
80+
}
81+
82+
#endif /* ACPP_SYSTEMS_SORTEDITERATINGSYSTEM_HPP_ */

src/IteratingSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void ashley::IteratingSystem::removedFromEngine(ashley::Engine &engine) {
3434

3535
void ashley::IteratingSystem::update(float deltaTime) {
3636
std::for_each(entities->begin(), entities->end(),
37-
[&](ashley::Entity *const &found) {processEntity(found, deltaTime);});
37+
[&](ashley::Entity *const &found) {processEntity(found, deltaTime);});
3838
}
3939

4040
bool ashley::IteratingSystem::checkProcessing() {

src/SortedIteratingSystem.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*******************************************************************************
2+
* Copyright 2017 See AUTHORS file.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
******************************************************************************/
16+
17+
#include <iterator>
18+
19+
#include "Ashley/core/Engine.hpp"
20+
#include "Ashley/systems/SortedIteratingSystem.hpp"
21+
22+
namespace ashley {
23+
24+
void SortedIteratingSystem::addedToEngine(Engine &engine) {
25+
IteratingSystem::addedToEngine(engine);
26+
sortedEntities.clear();
27+
28+
if (entities->size() > 0) {
29+
std::copy(entities->begin(), entities->end(), sortedEntities.begin());
30+
forceSort();
31+
sort();
32+
}
33+
34+
engine.addEntityListener(this);
35+
}
36+
37+
void SortedIteratingSystem::removedFromEngine(Engine &engine) {
38+
engine.removeEntityListener(this);
39+
sortedEntities.clear();
40+
shouldSort = false;
41+
}
42+
43+
void SortedIteratingSystem::entityAdded(Entity &entity) {
44+
sortedEntities.emplace_back(&entity);
45+
shouldSort = true;
46+
}
47+
48+
void SortedIteratingSystem::entityRemoved(Entity &entity) {
49+
sortedEntities.erase(std::remove(sortedEntities.begin(), sortedEntities.end(), &entity), sortedEntities.end());
50+
}
51+
52+
void SortedIteratingSystem::update(float deltaTime) {
53+
sort();
54+
std::for_each(sortedEntities.begin(), sortedEntities.end(),
55+
[&](ashley::Entity *const &found) {processEntity(found, deltaTime);});
56+
}
57+
58+
void SortedIteratingSystem::sort() {
59+
if (shouldSort) {
60+
std::sort(sortedEntities.begin(), sortedEntities.end(), comparator);
61+
shouldSort = false;
62+
}
63+
}
64+
65+
}

test/systems/IteratingSystemTests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
#include "Ashley/core/Family.hpp"
2424
#include "Ashley/core/Entity.hpp"
2525
#include "Ashley/core/ComponentMapper.hpp"
26+
2627
#include "Ashley/systems/IteratingSystem.hpp"
28+
2729
#include "AshleyTestCommon.hpp"
2830

2931
#include "gtest/gtest.h"
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*******************************************************************************
2+
* Copyright 2017 See AUTHORS file.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
******************************************************************************/
16+
17+
#include <string>
18+
#include <list>
19+
#include <utility>
20+
21+
#include "Ashley/core/Engine.hpp"
22+
#include "Ashley/core/Component.hpp"
23+
#include "Ashley/core/Family.hpp"
24+
#include "Ashley/core/ComponentMapper.hpp"
25+
26+
#include "Ashley/systems/SortedIteratingSystem.hpp"
27+
28+
#include "AshleyTestCommon.hpp"
29+
30+
#include "gtest/gtest.h"
31+
32+
using ashley::Engine;
33+
using ashley::Entity;
34+
using ashley::Component;
35+
using ashley::Family;
36+
using ashley::ComponentMapper;
37+
using ashley::SortedIteratingSystem;
38+
39+
class ComponentB : public Component {
40+
41+
};
42+
43+
class ComponentC : public Component {
44+
45+
};
46+
47+
class OrderedComponent : public ashley::Component {
48+
public:
49+
std::string name { "" };
50+
int zLayer = 0;
51+
52+
OrderedComponent() {
53+
}
54+
55+
OrderedComponent(std::string &&name, int zLayer) :
56+
name { std::move(name) },
57+
zLayer { zLayer } {
58+
}
59+
};
60+
61+
bool zComparator(Entity *e1, Entity *e2) {
62+
const ComponentMapper<OrderedComponent> zMapper = ComponentMapper<OrderedComponent>::getMapper();
63+
const auto o1 = zMapper.get(e1);
64+
const auto o2 = zMapper.get(e2);
65+
return o1->zLayer < o2->zLayer;
66+
}
67+
68+
class SortedIteratingSystemMock final : public SortedIteratingSystem {
69+
public:
70+
std::list<std::string> expectedNames;
71+
const ComponentMapper<OrderedComponent> zMapper = ComponentMapper<OrderedComponent>::getMapper();
72+
uint64_t numUpdates = 0;
73+
74+
explicit SortedIteratingSystemMock(Family *family) :
75+
SortedIteratingSystem(family, zComparator, 0) {
76+
}
77+
78+
void update(float deltaTime) override final {
79+
SortedIteratingSystem::update(deltaTime);
80+
ASSERT_TRUE(expectedNames.empty());
81+
}
82+
83+
void processEntity(Entity * const entity, float deltaTime) override final {
84+
++numUpdates;
85+
const auto z = zMapper.get(entity);
86+
87+
ASSERT_NE(z, nullptr);
88+
ASSERT_FALSE(expectedNames.empty());
89+
ASSERT_EQ(expectedNames.front(), z->name);
90+
expectedNames.pop_front();
91+
}
92+
};
93+
94+
namespace {
95+
class SortedIteratingSystemTest : public ::testing::Test {
96+
protected:
97+
const ComponentMapper<OrderedComponent> zMapper = ComponentMapper<OrderedComponent>::getMapper();
98+
Engine engine;
99+
100+
Family *sortFamily { Family::getFor( { typeid(OrderedComponent) }) };
101+
102+
const float delta = 1.0f;
103+
};
104+
105+
TEST_F(SortedIteratingSystemTest, ShouldIterateEntitiesWithCorrectFamily) {
106+
const auto family = Family::getFor( { typeid(OrderedComponent), typeid(ComponentB) });
107+
auto mockSystem = engine.addSystem<SortedIteratingSystemMock>(family);
108+
auto e = engine.addEntity();
109+
110+
e->add<OrderedComponent>("A", 0);
111+
engine.update(delta);
112+
113+
ASSERT_EQ(mockSystem->numUpdates, 0);
114+
115+
e->add<ComponentB>();
116+
mockSystem->expectedNames.emplace_back("A");
117+
engine.update(delta);
118+
119+
ASSERT_EQ(mockSystem->numUpdates, 1);
120+
121+
e->add<ComponentC>();
122+
mockSystem->expectedNames.emplace_back("A");
123+
engine.update(delta);
124+
125+
ASSERT_EQ(mockSystem->numUpdates, 2);
126+
127+
e->remove<OrderedComponent>();
128+
e->add<ComponentC>();
129+
engine.update(delta);
130+
131+
ASSERT_EQ(mockSystem->numUpdates, 2);
132+
}
133+
134+
TEST_F(SortedIteratingSystemTest, EntityOrdering) {
135+
auto mockSystem = engine.addSystem<SortedIteratingSystemMock>(sortFamily);
136+
137+
auto a_t = std::unique_ptr<Entity>(new Entity());
138+
a_t->add<OrderedComponent>("A", 0);
139+
140+
auto b_t = std::unique_ptr<Entity>(new Entity());
141+
b_t->add<OrderedComponent>("B", 1);
142+
143+
auto c_t = std::unique_ptr<Entity>(new Entity());
144+
c_t->add<OrderedComponent>("C", 3);
145+
146+
auto d_t = std::unique_ptr<Entity>(new Entity());
147+
d_t->add<OrderedComponent>("D", 2);
148+
149+
auto a = engine.addEntity(std::move(a_t));
150+
auto b = engine.addEntity(std::move(b_t));
151+
auto c = engine.addEntity(std::move(c_t));
152+
153+
{
154+
SCOPED_TRACE("A, B & C");
155+
156+
mockSystem->expectedNames.emplace_back("A");
157+
mockSystem->expectedNames.emplace_back("B");
158+
mockSystem->expectedNames.emplace_back("C");
159+
engine.update(0.0f);
160+
}
161+
162+
auto d = engine.addEntity(std::move(d_t));
163+
164+
{
165+
SCOPED_TRACE("A, B, D & C");
166+
mockSystem->expectedNames.emplace_back("A");
167+
mockSystem->expectedNames.emplace_back("B");
168+
mockSystem->expectedNames.emplace_back("D");
169+
mockSystem->expectedNames.emplace_back("C");
170+
engine.update(0.0f);
171+
}
172+
173+
zMapper.get(a)->zLayer = 3;
174+
zMapper.get(b)->zLayer = 2;
175+
zMapper.get(c)->zLayer = 1;
176+
zMapper.get(d)->zLayer = 0;
177+
mockSystem->forceSort();
178+
179+
{
180+
SCOPED_TRACE("D, C, B, A");
181+
mockSystem->expectedNames.emplace_back("D");
182+
mockSystem->expectedNames.emplace_back("C");
183+
mockSystem->expectedNames.emplace_back("B");
184+
mockSystem->expectedNames.emplace_back("A");
185+
engine.update(0.0f);
186+
}
187+
188+
}
189+
190+
}

0 commit comments

Comments
 (0)