Compare commits

..

8 Commits

Author SHA1 Message Date
Jace 73064c7257 Broken undo 2016-02-26 13:12:49 +01:00
Jace fa78991795 World::MemoryUsage returns approximate value of component pool memory usage 2016-02-26 12:53:33 +01:00
Jace 0c60b637c3 Who's merging without compiling? 2016-02-26 12:52:10 +01:00
William Moberg d411de894f Merge pull request #140 from teamfisk/FixNetworkBugs
Death explosion should always get triggered now.
2016-02-26 12:01:19 +01:00
Jocke 18450ff4f3 Death explosion should always get triggered now. (I hope) 2016-02-26 11:08:05 +01:00
William Moberg 22b473a0b6 Merge pull request #139 from teamfisk/EditorCopyPaste
Editor copy paste
2016-02-26 11:01:18 +01:00
Jace 0343683678 Fixed cubemaps being generated over and over again each frame. 2016-02-25 17:51:40 +01:00
Jace 9e6c67596d Basic editor copy and paste. Doesn't actually copy the entity until you paste it. 2016-02-25 17:51:28 +01:00
21 changed files with 200 additions and 67 deletions
+2
View File
@@ -65,6 +65,8 @@ public:
iterator end() const;
size_t size() const;
std::size_t MemoryUsage() const;
//Dumps information about what the pool memory looks like right now
//into an output stream (e.g. file/std::cout, anything that has an operator<<)
//Interpret the data in the memory as InterpretType.
+3
View File
@@ -27,8 +27,10 @@ struct EntityWrapper
bool HasComponent(const std::string& componentType);
void AttachComponent(const char* componentName);
EntityWrapper Parent();
EntityWrapper BaseParent();
EntityWrapper FirstChildByName(const std::string& name);
EntityWrapper FirstParentWithComponent(const std::string& componentType);
EntityWrapper Clone(EntityWrapper parent = EntityWrapper::Invalid);
bool IsChildOf(EntityWrapper potentialParent);
bool Valid() const;
@@ -39,6 +41,7 @@ struct EntityWrapper
private:
EntityWrapper firstChildByNameRecursive(const std::string& name, EntityID parent);
EntityWrapper cloneRecursive(EntityWrapper entity, EntityWrapper parent);
};
namespace std
+5
View File
@@ -194,6 +194,11 @@ public:
return m_ExtraMemory.size();
}
std::size_t MemoryUsage() const
{
return size() * m_Stride;
}
//Dumps information about what the pool memory looks like right now
//into an output stream (e.g. file/std::cout, anything that has an operator<<)
//Interpret the data in the memory as InterpretType.
+5 -2
View File
@@ -18,7 +18,7 @@ public:
World(const World& other);
// Create empty entity
EntityID CreateEntity(EntityID parent = 0);
EntityID CreateEntity(EntityID parent = EntityID_Invalid);
// Delete entity and all components within
void DeleteEntity(EntityID entity);
// Check if an entity exists
@@ -40,7 +40,7 @@ public:
// Change the parent of an entity
void SetParent(EntityID entity, EntityID parent);
// Get children of an entity
const std::pair<std::unordered_multimap<EntityID, EntityID>::const_iterator, std::unordered_multimap<EntityID, EntityID>::const_iterator> GetChildren(EntityID entity);
const std::pair<std::unordered_multimap<EntityID, EntityID>::const_iterator, std::unordered_multimap<EntityID, EntityID>::const_iterator> GetDirectChildren(EntityID entity);
// Get all component pools
const std::unordered_map<std::string, ComponentPool*>& GetComponentPools() const { return m_ComponentPools; }
// Get the entity children map
@@ -50,6 +50,9 @@ public:
// Get the textual name of an entity
std::string GetName(EntityID entity) const;
// Get an approximate number for component pool memory usage
std::size_t MemoryUsage() const;
private:
EventBroker* m_EventBroker = nullptr;
EntityID m_CurrentEntityID = 0;
+17 -1
View File
@@ -73,6 +73,12 @@ public:
// Called when the user means to rename an entity.
typedef std::function<void(EntityWrapper, const std::string&)> OnEntityChangeName_t;
void SetEntityChangeNameCallback(OnEntityChangeName_t f) { m_OnEntityChangeName = f; }
// Called when the user pastes an entity previously "copied"
// @param EntityWrapper The entity to copy
// @param EntityWrapper The entity to parent the new copy to
// @return The new copy of the entity
typedef std::function<EntityWrapper(EntityWrapper, EntityWrapper)> OnEntityPaste_t;
void SetEntityPasteCallback(OnEntityPaste_t f) { m_OnEntityPaste = f; }
// Called when the user means to attach a new component to an entity.
typedef std::function<void(EntityWrapper, const std::string&)> OnComponentAttach_t;
void SetComponentAttachCallback(OnComponentAttach_t f) { m_OnComponentAttach = f; }
@@ -85,7 +91,13 @@ public:
// Called when the user selects a widget space.
typedef std::function<void(WidgetSpace)> OnWidgetSpace_t;
void SetWidgetSpaceCallback(OnWidgetSpace_t f) { m_OnWidgetSpace = f; }
// Called when anything is modified making the world dirty
// @param EntityWrapper The entity that was changed and marked as dirty
typedef std::function<void(EntityWrapper)> OnDirty_t;
void SetDirtyCallback(OnDirty_t f) { m_OnDirty = f; }
// Called when the user wishes to undo
typedef std::function<void()> OnUndo_t;
void SetUndoCallback(OnUndo_t f) { m_OnUndo = f; }
private:
World* m_World;
EventBroker* m_EventBroker;
@@ -111,6 +123,7 @@ private:
std::string m_DroppedFile = "";
bool m_Paused = false;
bool m_MouseLocked = false;
EntityWrapper m_CopyTarget = EntityWrapper::Invalid;
// Callbacks
OnEntitySelectedCallback_t m_OnEntitySelected = nullptr;
@@ -124,6 +137,9 @@ private:
OnComponentDelete_t m_OnComponentDelete = nullptr;
OnWidgetMode_t m_OnWidgetMode = nullptr;
OnWidgetSpace_t m_OnWidgetSpace = nullptr;
OnEntityPaste_t m_OnEntityPaste = nullptr;
OnDirty_t m_OnDirty = nullptr;
OnUndo_t m_OnUndo = nullptr;
// Events
EventRelay<EditorGUI, Events::KeyDown> m_EKeyDown;
+5
View File
@@ -36,6 +36,7 @@ private:
EditorCameraInputController<EditorSystem>* m_EditorCameraInputController;
EditorGUI* m_EditorGUI;
EditorStats* m_EditorStats;
std::vector<World> m_UndoLevels;
// State
double m_LastTime = 0.f;
@@ -44,6 +45,7 @@ private:
EditorGUI::WidgetSpace m_WidgetSpace = EditorGUI::WidgetSpace::Global;
EntityWrapper m_Widget = EntityWrapper::Invalid;
EntityWrapper m_CurrentSelection = EntityWrapper::Invalid;
bool m_SaveUndoLevel = false; // Only save undo state once per update
// Utility functions
EntityWrapper importEntity(EntityWrapper parent, boost::filesystem::path filePath);
@@ -56,9 +58,12 @@ private:
void OnEntityDelete(EntityWrapper entity);
void OnEntityChangeParent(EntityWrapper entity, EntityWrapper parent);
void OnEntityChangeName(EntityWrapper entity, const std::string& name);
EntityWrapper OnEntityPaste(EntityWrapper entityToCopy, EntityWrapper parent);
void OnComponentAttach(EntityWrapper entity, const std::string& componentType);
void OnComponentDelete(EntityWrapper entity, const std::string& componentType);
void OnWidgetSpace(EditorGUI::WidgetSpace widgetSpace);
void OnDirty(EntityWrapper entity);
void OnUndo();
// Events
EventRelay<EditorSystem, Events::MousePress> m_EMousePress;
+1
View File
@@ -19,6 +19,7 @@
#include "Core/World.h"
#include "Core/EventBroker.h"
#include "Core/ConfigFile.h"
#include "Core/EPlayerDeath.h"
#include "Input/EInputCommand.h"
#include "Core/EPlayerDamage.h"
#include "../Game/Events/EDoubleJump.h"
-2
View File
@@ -21,8 +21,6 @@ public:
std::string GetFileName() const;
GLuint GetHandle() const;
bool IsCompiled() const;
static std::string ReadFile(std::string fileName);
private:
protected:
GLenum m_ShaderType;
std::string m_FileName;
+15 -1
View File
@@ -5,6 +5,20 @@
<c:Transform/>
</Components>
<Children/>
<Children>
<Entity>
<Components>
<c:Transform/>
</Components>
<Children>
<Entity>
<Components>
<c:Transform/>
</Components>
<Children/>
</Entity>
</Children>
</Entity>
</Children>
</Entity>
+6 -2
View File
@@ -116,7 +116,12 @@ LightResult CalcDirectionalLightSource(vec4 direction, vec4 color, float intensi
return result;
}
#include "Shaders/Util/CommonUniforms.glsl"
vec4 CalcNormalMappedValue(vec3 normal, vec3 tangent, vec3 bitangent, vec2 textureCoordinate, sampler2D normalMap)
{
mat3 TBN = mat3(tangent, bitangent, normal);
vec3 NormalMap = texture(normalMap, textureCoordinate).xyz * 2.0 - vec3(1.0);
return vec4(TBN * normalize(NormalMap), 0.0);
}
void main()
{
@@ -175,7 +180,6 @@ void main()
color_result += FillColor;
}
sceneColor = vec4(color_result.xyz, clamp(color_result.a, 0, 1));
//sceneColor = CommonUniforms.testColour;
//sceneColor = vec4(reflectionColor.xyz, 1);
color_result += glowTexel*GlowIntensity;
@@ -1,6 +0,0 @@
vec4 CalcNormalMappedValue(vec3 normal, vec3 tangent, vec3 bitangent, vec2 textureCoordinate, sampler2D normalMap)
{
mat3 TBN = mat3(tangent, bitangent, normal);
vec3 NormalMap = texture(normalMap, textureCoordinate).xyz * 2.0 - vec3(1.0);
return vec4(TBN * normalize(NormalMap), 0.0);
}
+5
View File
@@ -112,6 +112,11 @@ size_t ComponentPool::size() const
return m_Pool.size();
}
std::size_t ComponentPool::MemoryUsage() const
{
return m_Pool.MemoryUsage();
}
template <typename InterpretType /*= char*/>
void ComponentPool::Dump() const
{
+45 -1
View File
@@ -34,6 +34,15 @@ EntityWrapper EntityWrapper::Parent()
}
}
EntityWrapper EntityWrapper::BaseParent()
{
EntityWrapper baseParent = Parent();
while (baseParent.Parent().Valid()) {
baseParent = baseParent.Parent();
}
return baseParent;
}
EntityWrapper EntityWrapper::FirstChildByName(const std::string& name)
{
return firstChildByNameRecursive(name, this->ID);
@@ -51,6 +60,17 @@ EntityWrapper EntityWrapper::FirstParentWithComponent(const std::string& compone
return EntityWrapper::Invalid;
}
EntityWrapper EntityWrapper::Clone(EntityWrapper parent /*= Invalid*/)
{
if (!Valid()) {
return EntityWrapper::Invalid;
}
EntityWrapper clone = cloneRecursive(*this, EntityWrapper::Invalid);
this->World->SetParent(clone.ID, parent.ID);
return clone;
}
bool EntityWrapper::IsChildOf(EntityWrapper potentialParent)
{
EntityWrapper entity = *this;
@@ -111,7 +131,7 @@ EntityWrapper EntityWrapper::firstChildByNameRecursive(const std::string& name,
return EntityWrapper::Invalid;
}
auto itPair = this->World->GetChildren(parent);
auto itPair = this->World->GetDirectChildren(parent);
if (itPair.first == itPair.second) {
return EntityWrapper::Invalid;
}
@@ -131,3 +151,27 @@ EntityWrapper EntityWrapper::firstChildByNameRecursive(const std::string& name,
return EntityWrapper::Invalid;
}
EntityWrapper EntityWrapper::cloneRecursive(EntityWrapper entity, EntityWrapper parent)
{
EntityWrapper clone = EntityWrapper(entity.World, entity.World->CreateEntity(parent.ID));
entity.World->SetName(clone.ID, entity.Name());
// Clone components
for (auto& kv : entity.World->GetComponentPools()) {
if (kv.second->KnowsEntity(entity.ID)) {
ComponentWrapper c1 = kv.second->GetByEntity(entity.ID);
ComponentWrapper c2 = entity.World->AttachComponent(clone.ID, kv.first);
c1.Copy(c2);
}
}
// Clone children
auto children = entity.World->GetDirectChildren(entity.ID);
for (auto it = children.first; it != children.second; ++it) {
EntityWrapper child(entity.World, it->second);
cloneRecursive(child, clone);
}
return clone;
}
+12 -1
View File
@@ -127,7 +127,7 @@ void World::SetParent(EntityID entity, EntityID parent)
m_EntityChildren.insert(std::make_pair(parent, entity));
}
const std::pair<std::unordered_multimap<EntityID, EntityID>::const_iterator, std::unordered_multimap<EntityID, EntityID>::const_iterator> World::GetChildren(EntityID entity)
const std::pair<std::unordered_multimap<EntityID, EntityID>::const_iterator, std::unordered_multimap<EntityID, EntityID>::const_iterator> World::GetDirectChildren(EntityID entity)
{
return m_EntityChildren.equal_range(entity);
}
@@ -151,6 +151,17 @@ std::string World::GetName(EntityID entity) const
}
}
std::size_t World::MemoryUsage() const
{
std::size_t mem = 0;
for (auto& kv : m_ComponentPools) {
mem += kv.second->MemoryUsage();
}
return mem;
}
EntityID World::generateEntityID()
{
// TODO: Make EntityID generation smarter
+29 -6
View File
@@ -598,6 +598,28 @@ bool EditorGUI::OnKeyDown(const Events::KeyDown& e)
entityImport(m_World);
}
if (e.ModCtrl && e.KeyCode == GLFW_KEY_C) {
m_CopyTarget = m_CurrentSelection;
}
if (e.ModCtrl && e.KeyCode == GLFW_KEY_Z) {
if (m_OnUndo != nullptr) {
m_OnUndo();
if (!m_CurrentSelection.Valid()) {
SelectEntity(EntityWrapper::Invalid);
}
}
}
if (e.ModCtrl && e.KeyCode == GLFW_KEY_V) {
if (m_OnEntityPaste != nullptr) {
EntityWrapper copy = m_OnEntityPaste(m_CopyTarget, m_CurrentSelection);
if (copy != EntityWrapper::Invalid) {
SelectEntity(copy);
}
}
}
if (e.KeyCode == GLFW_KEY_DELETE) {
if (m_CurrentSelection.Valid()) {
entityDelete(m_CurrentSelection);
@@ -761,13 +783,13 @@ bool EditorGUI::compareCharArray(const char* c1, const char* c2)
void EditorGUI::SetDirty(EntityWrapper entity)
{
EntityWrapper baseParent = entity;
while (baseParent.Parent().Valid()) {
baseParent = baseParent.Parent();
}
EntityWrapper baseParent = entity.BaseParent();
if (m_EntityFiles.find(baseParent) != m_EntityFiles.end()) {
m_EntityFiles.at(baseParent).Dirty = true;
}
if (m_OnDirty != nullptr) {
m_OnDirty(entity);
}
}
void EditorGUI::entityImport(World* world)
@@ -817,6 +839,7 @@ void EditorGUI::entityCreate(World* world, EntityWrapper parent)
parent.World = world;
}
EntityWrapper newEntity = m_OnEntityCreate(parent);
SetDirty(newEntity);
SelectEntity(newEntity);
}
}
@@ -832,9 +855,9 @@ void EditorGUI::entityDelete(EntityWrapper entity)
if (boost::any_cast<EntityWrapper>(m_ModalData[modalName]) == entity) {
EntityWrapper parent = entity.Parent();
if (m_OnEntityDelete != nullptr) {
SetDirty(entity);
m_OnEntityDelete(entity);
m_EntityFiles.erase(entity);
SetDirty(parent);
}
if (!m_CurrentSelection.Valid()) {
SelectEntity(parent);
@@ -851,8 +874,8 @@ void EditorGUI::entityChangeParent(EntityWrapper entity, EntityWrapper parent)
}
if (m_OnEntityChangeParent != nullptr) {
SetDirty(entity);
m_OnEntityChangeParent(entity, parent);
SetDirty(entity);
LOG_DEBUG("Changed parent of %i to %i", entity.ID, parent.ID);
}
}
+26
View File
@@ -28,10 +28,13 @@ EditorSystem::EditorSystem(SystemParams params, IRenderer* renderer, RenderFrame
m_EditorGUI->SetEntityDeleteCallback(std::bind(&EditorSystem::OnEntityDelete, this, std::placeholders::_1));
m_EditorGUI->SetEntityChangeParentCallback(std::bind(&EditorSystem::OnEntityChangeParent, this, std::placeholders::_1, std::placeholders::_2));
m_EditorGUI->SetEntityChangeNameCallback(std::bind(&EditorSystem::OnEntityChangeName, this, std::placeholders::_1, std::placeholders::_2));
m_EditorGUI->SetEntityPasteCallback(std::bind(&EditorSystem::OnEntityPaste, this, std::placeholders::_1, std::placeholders::_2));
m_EditorGUI->SetComponentAttachCallback(std::bind(&EditorSystem::OnComponentAttach, this, std::placeholders::_1, std::placeholders::_2));
m_EditorGUI->SetComponentDeleteCallback(std::bind(&EditorSystem::OnComponentDelete, this, std::placeholders::_1, std::placeholders::_2));
m_EditorGUI->SetWidgetModeCallback(std::bind(&EditorSystem::setWidgetMode, this, std::placeholders::_1));
m_EditorGUI->SetWidgetSpaceCallback(std::bind(&EditorSystem::OnWidgetSpace, this, std::placeholders::_1));
m_EditorGUI->SetDirtyCallback(std::bind(&EditorSystem::OnDirty, this, std::placeholders::_1));
m_EditorGUI->SetUndoCallback(std::bind(&EditorSystem::OnUndo, this));
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &EditorSystem::OnMousePress);
EVENT_SUBSCRIBE_MEMBER(m_EWidgetDelta, &EditorSystem::OnWidgetDelta);
@@ -86,6 +89,11 @@ void EditorSystem::Update(double dt)
glm::vec3& pos = cameraTransform["Position"];
pos += m_EditorCameraInputController->Movement() * glm::inverse(glm::quat(ori)) * (float)actualDelta;
}
if (m_SaveUndoLevel) {
m_UndoLevels.push_back(*m_World);
m_SaveUndoLevel = false;
}
}
void EditorSystem::Enable()
@@ -160,6 +168,11 @@ void EditorSystem::OnEntityChangeName(EntityWrapper entity, const std::string& n
}
}
EntityWrapper EditorSystem::OnEntityPaste(EntityWrapper entityToCopy, EntityWrapper parent)
{
return entityToCopy.Clone(parent);
}
void EditorSystem::OnComponentAttach(EntityWrapper entity, const std::string& componentType)
{
if (entity.Valid()) {
@@ -179,6 +192,19 @@ void EditorSystem::OnWidgetSpace(EditorGUI::WidgetSpace widgetSpace)
m_WidgetSpace = widgetSpace;
}
void EditorSystem::OnDirty(EntityWrapper entity)
{
m_SaveUndoLevel = true;
}
void EditorSystem::OnUndo()
{
// The last "undo level" is always the most recent change
if (m_UndoLevels.size() >= 2) {
*m_World = m_UndoLevels.at(m_UndoLevels.size() - 2);
}
}
bool EditorSystem::OnMousePress(const Events::MousePress& e)
{
ImGuiIO& io = ImGui::GetIO();
+8 -2
View File
@@ -261,8 +261,14 @@ void Client::parseEntityDeletion(Packet & packet)
if (m_ServerIDToClientID.find(entityToDelete) != m_ServerIDToClientID.end()) {
EntityID localEntity = m_ServerIDToClientID.at(entityToDelete);
if (m_World->ValidEntity(localEntity)) {
m_World->DeleteEntity(localEntity);
deleteFromServerClientMaps(entityToDelete, localEntity);
if (m_World->HasComponent(localEntity,"Player")) {
Events::PlayerDeath e;
e.Player = EntityWrapper(m_World, localEntity);
m_EventBroker->Publish(e);
} else {
m_World->DeleteEntity(localEntity);
deleteFromServerClientMaps(entityToDelete, localEntity);
}
}
}
}
+3 -3
View File
@@ -186,7 +186,7 @@ void Server::addInputCommandsToPacket(Packet& packet)
void Server::addPlayersToPacket(Packet & packet, EntityID entityID)
{
auto itPair = m_World->GetChildren(entityID);
auto itPair = m_World->GetDirectChildren(entityID);
std::unordered_map<std::string, ComponentPool*> worldComponentPools = m_World->GetComponentPools();
// Loop through every child
for (auto it = itPair.first; it != itPair.second; it++) {
@@ -234,7 +234,7 @@ void Server::addPlayersToPacket(Packet & packet, EntityID entityID)
void Server::addChildrenToPacket(Packet & packet, EntityID entityID)
{
auto itPair = m_World->GetChildren(entityID);
auto itPair = m_World->GetDirectChildren(entityID);
std::unordered_map<std::string, ComponentPool*> worldComponentPools = m_World->GetComponentPools();
// Loop through every child
for (auto it = itPair.first; it != itPair.second; it++) {
@@ -601,7 +601,7 @@ void Server::parsePlayerTransform(Packet& packet)
bool Server::shouldSendToClient(EntityWrapper childEntity)
{
auto children = m_World->GetChildren(childEntity.ID);
auto children = m_World->GetDirectChildren(childEntity.ID);
for (auto it = children.first; it != children.second; it++) {
EntityWrapper child(m_World, it->second);
if(child.HasComponent("CapturePoint")) {
+1
View File
@@ -17,6 +17,7 @@ void CubeMapPass::LoadTextures(std::string input)
m_CubeMapTextures.push_back(img);
}
GenerateCubeMapTexture();
m_PreviusCubeMapTexture = input;
}
}
+11 -39
View File
@@ -4,32 +4,22 @@ GLuint Shader::CompileShader(GLenum shaderType, std::string fileName)
{
LOG_INFO("Compiling shader \"%s\"", fileName.c_str());
std::string shaderFile = ReadFile(fileName);
std::string shaderFile;
std::ifstream in(fileName, std::ios::in);
if (!in) {
LOG_ERROR("Error: Failed to open shader file \"%s\"", fileName.c_str());
return 0;
}
in.seekg(0, std::ios::end);
shaderFile.resize((int)in.tellg());
in.seekg(0, std::ios::beg);
in.read(&shaderFile[0], shaderFile.size());
in.close();
GLuint shader = glCreateShader(shaderType);
if (GLERROR("glCreateShader"))
return 0;
std::size_t startPos = 0;
std::size_t SEofNewFile[2];
std::string key = "#include";
while((startPos = shaderFile.find(key, startPos)) != std::string::npos)
{
SEofNewFile[0] = shaderFile.find('"', startPos+key.length())+1;
SEofNewFile[1] = shaderFile.find('"', SEofNewFile[0]);
if (SEofNewFile[0] == std::string::npos || SEofNewFile[1] == std::string::npos)
return 0;
std::string replacementFileName = shaderFile.substr(SEofNewFile[0], SEofNewFile[1] - SEofNewFile[0]);
std::string replacementString = ReadFile(replacementFileName);
size_t firstof = replacementString.find_first_of((char)0);
replacementString.erase(firstof, replacementString.size() - firstof);
if (replacementString.length() <= 0)
return 0;
shaderFile.replace(startPos, SEofNewFile[1]+2 - startPos, replacementString + "\n");
startPos += replacementString.length(); //This might not be wanted.
}
const GLchar* shaderFiles = shaderFile.c_str();
const GLint length = static_cast<GLint>(shaderFile.length());
glShaderSource(shader, 1, &shaderFiles, &length);
@@ -56,24 +46,6 @@ GLuint Shader::CompileShader(GLenum shaderType, std::string fileName)
return shader;
}
std::string Shader::ReadFile(std::string fileName)
{
std::string shaderFile;
std::ifstream in(fileName, std::ios::in);
if (!in) {
LOG_ERROR("Error: Failed to open shader file \"%s\"", fileName.c_str());
return "";
}
in.seekg(0, std::ios::end);
shaderFile.resize((int)in.tellg());
in.seekg(0, std::ios::beg);
in.read(&shaderFile[0], shaderFile.size());
in.close();
return shaderFile;
}
Shader::Shader(GLenum shaderType, std::string fileName) : m_ShaderType(shaderType), m_FileName(fileName)
{
m_ShaderHandle = 0;
+1 -1
View File
@@ -36,7 +36,7 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /
}
// Find any SpawnPoints existing as children of spawner
auto children = spawner.World->GetChildren(spawner.ID);
auto children = spawner.World->GetDirectChildren(spawner.ID);
std::vector<EntityWrapper> spawnPoints;
for (auto kv = children.first; kv != children.second; ++kv) {
const EntityID& child = kv->second;