Skip to content

Commit

Permalink
Collision tag update (#25)
Browse files Browse the repository at this point in the history
- Can now get entity collisions by tag
- Fixed bug with setting tags in python script
- Added rough ease functions (not ready to be used!)
  • Loading branch information
Chukobyte authored Jan 8, 2022
1 parent 11ccacd commit a1c750f
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 40 deletions.
2 changes: 1 addition & 1 deletion _version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "0.43.0"
"version": "0.44.0"
}
11 changes: 9 additions & 2 deletions docs/python_api/physics.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ None.

```python
@staticmethod
get_collided_nodes(node: seika.node.Node) -> list:
get_collided_nodes(node: seika.node.Node, offset: seika.math.Vector2) -> list:
```

Returns a `list` of nodes that collided with the passed in `node`.
Returns a `list` of nodes that collided with the passed in `node`. Can configure the position of the collision by adjusting the `offset`.

```python
@staticmethod
get_collided_nodes_by_tag(node: seika.node.Node, tag: str, offset: seika.math.Vector2) -> list:
```

Same as `get_collided_nodes` but filters results by tag.

```python
@staticmethod
Expand Down
39 changes: 39 additions & 0 deletions src/core/ecs/entity/entity_tag_cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <set>
#include <unordered_map>

#include "entity.h"

class EntityTagCache {
public:
void AddEntityTags(Entity entity, const std::vector<std::string> tags) {
for (const std::string& tag : tags) {
if (!HasTag(tag)) {
entityTagCache.emplace(tag, std::set<Entity> {});
}
entityTagCache[tag].insert(entity);
}
}

void RemoveEntityTags(Entity entity, const std::vector<std::string> tags) {
for (const std::string& tag : tags) {
if (HasTag(tag)) {
entityTagCache[tag].erase(entity);
}
}
}

bool HasTag(const std::string& tag) {
return entityTagCache.count(tag) > 0;
}

std::set<Entity> GetTaggedEntities(const std::string& tag) {
if (HasTag(tag)) {
return entityTagCache[tag];
}
return {};
}
private:
std::unordered_map<std::string, std::set<Entity>> entityTagCache = {};
};
5 changes: 4 additions & 1 deletion src/core/ecs/entity_component_orchestrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Entity EntityComponentOrchestrator::CreateEntity() {
return entityManager->CreateEntity();
}

// TODO: Make into hook
void EntityComponentOrchestrator::NewEntity(SceneNode sceneNode) {
if (componentManager->HasComponent<ScriptableClassComponent>(sceneNode.entity)) {
ScriptEntitySystem *scriptEntitySystem = (ScriptEntitySystem*) entitySystemManager->GetEntitySystem<ScriptEntitySystem>();
Expand All @@ -22,6 +23,7 @@ void EntityComponentOrchestrator::NewEntityAddChild(Entity parent, Entity child)
AddChildToEntityScene(childNode.parent, childNode.entity);
NodeComponent nodeComponent = componentManager->GetComponent<NodeComponent>(childNode.entity);
nodeNameToEntityMap.emplace(nodeComponent.name, childNode.entity);
entitySystemManager->OnEntityTagsUpdatedSystemsHook(child, {}, nodeComponent.tags);
CallStartOnScriptInstances(childNode);
}

Expand Down Expand Up @@ -52,7 +54,7 @@ void EntityComponentOrchestrator::DestroyEntity(SceneNode sceneNode) {
nodeNameToEntityMap.erase(nodeComponent.name);
entityManager->DestroyEntity(entityToRemove);
componentManager->EntityDestroyed(entityToRemove);
entitySystemManager->EntityDestroyed(entityToRemove);
entitySystemManager->EntityDestroyed(entityToRemove, nodeComponent.tags);
signalManager->RemoveEntitySignals(entityToRemove);
}
}
Expand Down Expand Up @@ -106,6 +108,7 @@ void EntityComponentOrchestrator::RegisterSceneNodeInstances(SceneNode sceneNode
}
NodeComponent nodeComponent = componentManager->GetComponent<NodeComponent>(sceneNode.entity);
nodeNameToEntityMap.emplace(nodeComponent.name, sceneNode.entity);
entitySystemManager->OnEntityTagsUpdatedSystemsHook(sceneNode.entity, {}, nodeComponent.tags);

for (SceneNode childSceneNode : sceneNode.children) {
RegisterSceneNodeInstances(childSceneNode);
Expand Down
28 changes: 22 additions & 6 deletions src/core/ecs/system/entity_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@

#include <set>

#include "../entity/entity.h"
#include "../entity/entity_tag_cache.h"
#include "../../global_dependencies.h"

const unsigned int MAX_SYSTEMS = 32;

class EntitySystem {
protected:
bool enabled = false;
public:
std::set<Entity> entities;

bool IsEnabled() {
return enabled;
}
Expand All @@ -28,6 +24,10 @@ class EntitySystem {
enabled = false;
}

bool HasEntity(Entity entity) const {
return entities.count(entity) > 0;
}

// Hooks
virtual void OnRegisterEntity(Entity entity) {
entities.insert(entity);
Expand All @@ -36,12 +36,28 @@ class EntitySystem {
entities.erase(entity);
}

virtual void OnEntityDestroyed(Entity entity) {}
virtual void OnEntityDestroyed(Entity entity) {
entities.erase(entity);
}

// Subscribable hooks
virtual void Process(float deltaTime) {}
virtual void PhysicsProcess(float deltaTime) {}
virtual void Render() {}

virtual void OnEntityTagsUpdated(Entity entity, const std::vector<std::string>& oldTags, const std::vector<std::string>& newTags) {
entityTagCache.RemoveEntityTags(entity, oldTags);
entityTagCache.AddEntityTags(entity, newTags);
}

virtual void OnEntityTagsRemoved(Entity entity, const std::vector<std::string>& tags) {
entityTagCache.RemoveEntityTags(entity, tags);
}

protected:
bool enabled = false;
EntityTagCache entityTagCache;
std::set<Entity> entities;
};

#endif //ENTITY_SYSTEM_H
21 changes: 18 additions & 3 deletions src/core/ecs/system/entity_system_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ enum class EntitySystemHook : int {
PROCESS = 2,
PHYSICS_PROCESS = 4,
RENDER = 8,
ALL = PROCESS | PHYSICS_PROCESS | RENDER,
ON_ENTITY_TAGS_UPDATE = 16,
ALL = PROCESS | PHYSICS_PROCESS | RENDER | ON_ENTITY_TAGS_UPDATE,
};
GENERATE_ENUM_CLASS_OPERATORS(EntitySystemHook)

Expand All @@ -28,6 +29,7 @@ class EntitySystemManager : public Singleton<EntitySystemManager> {
std::vector<EntitySystem*> processSystems = {};
std::vector<EntitySystem*> physicsProcessSystems = {};
std::vector<EntitySystem*> renderSystems = {};
std::vector<EntitySystem*> onEntityTagsUpdatedSystems = {};

void ProcessSystemHooks(EntitySystem* system, EntitySystemHook systemHooks) {
if (systemHooks == EntitySystemHook::NONE) {
Expand All @@ -42,6 +44,9 @@ class EntitySystemManager : public Singleton<EntitySystemManager> {
if ((systemHooks & EntitySystemHook::RENDER) == EntitySystemHook::RENDER) {
renderSystems.emplace_back(system);
}
if ((systemHooks & EntitySystemHook::ON_ENTITY_TAGS_UPDATE) == EntitySystemHook::ON_ENTITY_TAGS_UPDATE) {
onEntityTagsUpdatedSystems.emplace_back(system);
}
}

public:
Expand Down Expand Up @@ -125,11 +130,13 @@ class EntitySystemManager : public Singleton<EntitySystemManager> {
}
}

void EntityDestroyed(Entity entity) {
void EntityDestroyed(Entity entity, const std::vector<std::string>& tags) {
for (auto const& pair : systems) {
auto const& system = pair.second;
system->OnEntityDestroyed(entity);
system->entities.erase(entity);
}
for (EntitySystem *tagSystem : onEntityTagsUpdatedSystems) {
tagSystem->OnEntityTagsRemoved(entity, tags);
}
}

Expand Down Expand Up @@ -168,4 +175,12 @@ class EntitySystemManager : public Singleton<EntitySystemManager> {
system->Render();
}
}

void OnEntityTagsUpdatedSystemsHook(Entity entity, const std::vector<std::string>& oldTags, const std::vector<std::string>& newTags) {
for (EntitySystem *tagSystem : onEntityTagsUpdatedSystems) {
if (tagSystem->HasEntity(entity)) {
tagSystem->OnEntityTagsUpdated(entity, oldTags, newTags);
}
}
}
};
48 changes: 23 additions & 25 deletions src/core/ecs/system/systems/collision_entity_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class CollisionEntitySystem : public EntitySystem {
ColliderComponent sourceColliderComponent = componentManager->GetComponent<ColliderComponent>(sourceEntity);
return std::find(sourceColliderComponent.collisionExceptions.begin(), sourceColliderComponent.collisionExceptions.end(), targetEntity) != sourceColliderComponent.collisionExceptions.end();
}

public:
CollisionEntitySystem() {
collisionContext = GD::GetContainer()->collisionContext;
Expand All @@ -82,31 +83,6 @@ class CollisionEntitySystem : public EntitySystem {

void OnEntityDestroyed(Entity entity) override {}

// void ProcessCollisions() {
// collisionContext->ClearCollisionData();
// // TODO: Come up with a better more efficient solution
// for (Entity sourceEntity : entities) {
// std::vector<Entity> collidedEntities;
// for (Entity targetEntity : entities) {
// if (!IsTargetCollisionEntityInExceptionList(sourceEntity, targetEntity)) {
// Rect2 sourceCollisionRectangle = GetCollisionRectangle(sourceEntity);
// Rect2 targetCollisionRectangle = GetCollisionRectangle(targetEntity);
// if (CollisionResolver::DoesRectanglesCollide(sourceCollisionRectangle, targetCollisionRectangle)) {
// collidedEntities.emplace_back(targetEntity);
// // TODO: emit signal if Area2D like functionality for entering and exiting is required
// }
// }
// }
// if (collidedEntities.size() > 0) {
// collisionContext->RegisterCollisionResult(
// CollisionResult{
// .sourceEntity = sourceEntity,
// .collidedEntities = collidedEntities
// });
// }
// }
// }

void ProcessEntityCollisions(Entity sourceEntity, Vector2 offset = Vector2(0.0f, 0.0f)) {
collisionContext->ClearCollisionData();
std::vector<Entity> collidedEntities;
Expand All @@ -129,6 +105,28 @@ class CollisionEntitySystem : public EntitySystem {
}
}

void ProcessEntityCollisionsByTag(Entity sourceEntity, const std::string& tag, Vector2 offset = Vector2(0.0f, 0.0f)) {
collisionContext->ClearCollisionData();
std::vector<Entity> collidedEntities;
for (Entity targetEntity : entityTagCache.GetTaggedEntities(tag)) {
if (!IsTargetCollisionEntityInExceptionList(sourceEntity, targetEntity)) {
Rect2 sourceCollisionRectangle = GetCollisionRectangle(sourceEntity) + offset;
Rect2 targetCollisionRectangle = GetCollisionRectangle(targetEntity);
if (CollisionResolver::DoesRectanglesCollide(sourceCollisionRectangle, targetCollisionRectangle)) {
collidedEntities.emplace_back(targetEntity);
// TODO: emit signal if Area2D like functionality for entering and exiting is required
}
}
}
if (collidedEntities.size() > 0) {
collisionContext->RegisterCollisionResult(
CollisionResult{
.sourceEntity = sourceEntity,
.collidedEntities = collidedEntities
});
}
}

std::vector<Entity> GetEntitiesOnMouse(const Vector2 mousePosition) {
std::vector<Entity> entitiesOnMouse;
Rect2 mouseRectangle = Rect2(mousePosition, Vector2(1.0f, 1.0f));
Expand Down
1 change: 1 addition & 0 deletions src/core/ecs/system/systems/script_entity_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ScriptEntitySystem : public EntitySystem {
void Disable() override {}

void OnEntityDestroyed(Entity entity) override {
EntitySystem::OnEntityDestroyed(entity);
assert(activeScriptContext != nullptr && "No active script context!");
activeScriptContext->DeleteEntityInstance(entity);
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ void Game::InitializeECS() {
scriptSystemSignature.set(entityComponentOrchestrator->GetComponentType<ScriptableClassComponent>(), true);
entityComponentOrchestrator->SetSystemSignature<ScriptEntitySystem>(scriptSystemSignature);

EntitySystemHook collisionSystemHooks = projectProperties->areColliderVisible ? EntitySystemHook::RENDER : EntitySystemHook::NONE;
EntitySystemHook collisionSystemHooks = projectProperties->areColliderVisible ? EntitySystemHook::RENDER | EntitySystemHook::ON_ENTITY_TAGS_UPDATE : EntitySystemHook::ON_ENTITY_TAGS_UPDATE;
entityComponentOrchestrator->RegisterSystem<CollisionEntitySystem>(collisionSystemHooks);
ComponentSignature collisionSystemSignature;
collisionSystemSignature.set(entityComponentOrchestrator->GetComponentType<ColliderComponent>(), true);
Expand Down
1 change: 1 addition & 0 deletions src/core/scene/scene_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "../ecs/component/components/material_component.h"
#include "../ecs/component/components/light3D_component.h"

// TODO: Separate SceneNodeJsonParser into its own file...
class SceneNodeJsonParser {
private:
EntityManager *entityManager = nullptr;
Expand Down
41 changes: 41 additions & 0 deletions src/core/scripting/python/python_modules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,16 +575,19 @@ PyObject* PythonModules::node_get_tags(PyObject *self, PyObject *args, PyObject

PyObject* PythonModules::node_set_tags(PyObject *self, PyObject *args, PyObject *kwargs) {
static EntityComponentOrchestrator *entityComponentOrchestrator = GD::GetContainer()->entityComponentOrchestrator;
static EntitySystemManager* entitySystemManager = EntitySystemManager::GetInstance();
Entity entity;
PyObject *pyObject = nullptr;
if (PyArg_ParseTupleAndKeywords(args, kwargs, "iO", nodeSetTagsKWList, &entity, &pyObject)) {
NodeComponent nodeComponent = entityComponentOrchestrator->GetComponent<NodeComponent>(entity);
std::vector<std::string> nodeTags = {};
for (int i = 0; i < PyList_Size(pyObject); i++) {
CPyObject listItem = PyList_GetItem(pyObject, i);
listItem.AddRef();
const std::string &newTag = std::string(PyUnicode_AsUTF8(listItem));
nodeTags.emplace_back(newTag);
}
entitySystemManager->OnEntityTagsUpdatedSystemsHook(entity, nodeComponent.tags, nodeTags);
nodeComponent.tags = nodeTags;
entityComponentOrchestrator->UpdateComponent<NodeComponent>(entity, nodeComponent);
Py_RETURN_NONE;
Expand Down Expand Up @@ -1480,6 +1483,44 @@ PyObject* PythonModules::collision_get_collided_nodes(PyObject *self, PyObject *
return nullptr;
}

PyObject* PythonModules::collision_get_collided_nodes_by_tag(PyObject *self, PyObject *args, PyObject *kwargs) {
static EntityComponentOrchestrator *entityComponentOrchestrator = GD::GetContainer()->entityComponentOrchestrator;
static CollisionContext *collisionContext = GD::GetContainer()->collisionContext;
static CollisionEntitySystem *collisionEntitySystem = entityComponentOrchestrator->GetSystem<CollisionEntitySystem>();
static PythonCache *pythonCache = PythonCache::GetInstance();
static ComponentManager *componentManager = GD::GetContainer()->componentManager;
Entity entity;
char *pyTag;
float offsetX;
float offsetY;
if (PyArg_ParseTupleAndKeywords(args, kwargs, "isff", collisionGetCollidedNodesByTagKWList, &entity, &pyTag, &offsetX, &offsetY)) {
collisionEntitySystem->ProcessEntityCollisionsByTag(entity, std::string(pyTag), Vector2(offsetX, offsetY));
CPyObject pCollidedNodesList = PyList_New(0);
pCollidedNodesList.AddRef();
assert(pCollidedNodesList != nullptr && "node list empty!");
for (Entity collidedEntity : collisionContext->GetEntitiesCollidedWith(entity)) {
if (!entityComponentOrchestrator->IsEntityQueuedForDeletion(collidedEntity)) {
if (pythonCache->HasActiveInstance(collidedEntity)) {
CPyObject &pCollidedClassInstance = pythonCache->GetClassInstance(collidedEntity);
pCollidedClassInstance.AddRef();
if (PyList_Append(pCollidedNodesList, pCollidedClassInstance) == -1) {
PyErr_Print();
}
} else {
NodeComponent nodeComponent = componentManager->GetComponent<NodeComponent>(collidedEntity);
const std::string &nodeTypeString = NodeTypeHelper::GetNodeTypeString(nodeComponent.type);
if (PyList_Append(pCollidedNodesList, Py_BuildValue("(si)", nodeTypeString.c_str(), collidedEntity)) == -1) {
Logger::GetInstance()->Error("Error appending list");
PyErr_Print();
}
}
}
}
return pCollidedNodesList;
}
return nullptr;
}

PyObject* PythonModules::collision_update_collisions(PyObject *self, PyObject *args, PyObject *kwargs) {
static EntityComponentOrchestrator *entityComponentOrchestrator = GD::GetContainer()->entityComponentOrchestrator;
static CollisionEntitySystem *collisionEntitySystem = entityComponentOrchestrator->GetSystem<CollisionEntitySystem>();
Expand Down
Loading

0 comments on commit a1c750f

Please sign in to comment.