Skip to content

Commit

Permalink
add entity list (by components)
Browse files Browse the repository at this point in the history
  • Loading branch information
Green-Sky committed Sep 14, 2020
1 parent fa303c1 commit 0b91302
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 62 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 Erik Scholz
Copyright (c) 2020 Erik Scholz, Gnik Droy

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ A drop-in, single-file entity editor for EnTT, with ImGui as graphical backend.

[demo-code](https://github.com/Green-Sky/imgui_entt_entity_editor_demo) [(live)](http://scam.rocks/imgui_entt_entity_editor_demo/)

![screenshot](https://github.com/Green-Sky/imgui_entt_entity_editor_demo/blob/master/imgui_entt_entity_editor_screenshot0.png)
Editor
![screenshot0](https://github.com/Green-Sky/imgui_entt_entity_editor_demo/blob/master/imgui_entt_entity_editor_screenshot0.png)

Editor with Entiy-List
![screenshot1](https://github.com/Green-Sky/imgui_entt_entity_editor_demo/blob/master/imgui_entt_entity_editor_screenshot1.png)

With Drag and Drop
![vid](https://github.com/Green-Sky/imgui_entt_entity_editor_demo/blob/master/imgui_entt_entity_editor_dnd0.gif)

# example usage
```c++
Expand Down Expand Up @@ -44,8 +51,8 @@ editor.registerComponent<Velocity>("Velocity");
```
# dependencies
The editor uses (the latest) EnTTv3.4.0 interface and ImGui 1.72b but should work with prior versions. (tested with ImGui 1.68)
The editor uses EnTTv3.4.0 interface and ImGui. (tested with ImGui 1.68, 1.72b, 1.75, 1.78)
To use it with EnTTv3.0.0, use the dedicated branch.
For specific EnTT version check the tags.
Tested against EnTT 3.1.0, 3.1.1, 3.2.0, 3.2.1, 3.2.2, 3.3.x, 3.4.0 .
Releases available for EnTT 3.1.0, 3.1.1, 3.2.0, 3.2.1, 3.2.2, 3.3.x, 3.4.0 .
251 changes: 193 additions & 58 deletions imgui_entt_entity_editor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,55 @@

#include <map>
#include <set>
#include <functional>

#include <entt/entt.hpp>
#include <imgui.h>

#ifndef MM_IEEE_ASSERT
#define MM_IEEE_ASSERT(x) assert(x)
#endif

#define MM_IEEE_IMGUI_PAYLOAD_TYPE_ENTITY "MM_IEEE_ENTITY"

#ifndef MM_IEEE_ENTITY_WIDGET
#define MM_IEEE_ENTITY_WIDGET ::MM::EntityWidget
#endif

namespace MM {

template <class EntityType>
inline void EntityWidget(EntityType& e, entt::basic_registry<EntityType>& reg, bool dropTarget = false)
{
ImGui::PushID(entt::to_integral(e));

if (reg.valid(e)) {
ImGui::Text("ID: %d", entt::to_integral(e));
} else {
ImGui::Text("Invalid Entity");
}

if (reg.valid(e)) {
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
ImGui::SetDragDropPayload(MM_IEEE_IMGUI_PAYLOAD_TYPE_ENTITY, &e, sizeof(e));
ImGui::Text("ID: %d", entt::to_integral(e));
ImGui::EndDragDropSource();
}
}

if (dropTarget && ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(MM_IEEE_IMGUI_PAYLOAD_TYPE_ENTITY)) {
e = *(EntityType*)payload->Data;
}

ImGui::EndDragDropTarget();
}

ImGui::PopID();
}

template <class Component, class EntityType>
void ComponentEditorWidget(entt::basic_registry<EntityType>& registry, EntityType entity) {}
void ComponentEditorWidget([[maybe_unused]] entt::basic_registry<EntityType>& registry, EntityType entity) {}

template <class Component, class EntityType>
void ComponentAddAction(entt::basic_registry<EntityType>& registry, EntityType entity)
Expand Down Expand Up @@ -53,8 +94,8 @@ class EntityEditor {
ComponentInfo& registerComponent(const ComponentInfo& component_info)
{
auto index = entt::type_info<Component>::id();
auto [it, insert_result] = component_infos.insert_or_assign(index, component_info);
assert(insert_result);
[[maybe_unused]] auto [it, insert_result] = component_infos.insert_or_assign(index, component_info);
MM_IEEE_ASSERT(insert_result);
return std::get<ComponentInfo>(*it);
}

Expand All @@ -75,77 +116,171 @@ class EntityEditor {
return registerComponent<Component>(name, ComponentEditorWidget<Component, EntityType>);
}

void render(Registry& registry, EntityType& e)
void renderEditor(Registry& registry, EntityType& e)
{
if (show_window) {
if (ImGui::Begin("Entity Editor", &show_window)) {
ImGui::TextUnformatted("Editing:");
ImGui::SameLine();
ImGui::TextUnformatted("Editing:");
ImGui::SameLine();

MM_IEEE_ENTITY_WIDGET(e, registry, true);

if (ImGui::Button("New")) {
e = registry.create();
}
if (registry.valid(e)) {
ImGui::SameLine();

// clone would go here
//if (ImGui::Button("Clone")) {
//auto old_e = e;
//e = registry.create();
//}

ImGui::Dummy({10, 0}); // space destroy a bit, to not accidentally click it
ImGui::SameLine();

// red button
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.65, 0.15, 0.15, 1));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.8, 0.3, 0.3, 1));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1, 0.2, 0.2, 1));
if (ImGui::Button("Destroy")) {
registry.destroy(e);
e = entt::null;
}
ImGui::PopStyleColor(3);
}

ImGui::Separator();

if (registry.valid(e)) {
ImGui::Text("ID: %d", entt::to_integral(e));
if (registry.valid(e)) {
ImGui::PushID(entt::to_integral(e));
std::map<ComponentTypeID, ComponentInfo> has_not;
for (auto& [component_type_id, ci] : component_infos) {
if (entityHasComponent(registry, e, component_type_id)) {
ImGui::PushID(component_type_id);
if (ImGui::Button("-")) {
ci.destroy(registry, e);
ImGui::PopID();
continue; // early out to prevent access to deleted data
} else {
ImGui::SameLine();
}

if (ImGui::CollapsingHeader(ci.name.c_str())) {
ImGui::Indent(30.f);
ImGui::PushID("Widget");
ci.widget(registry, e);
ImGui::PopID();
ImGui::Unindent(30.f);
}
ImGui::PopID();
} else {
ImGui::Text("Invalid Entity");
has_not[component_type_id] = ci;
}
}

if (ImGui::Button("New Entity")) {
e = registry.create();
if (!has_not.empty()) {
if (ImGui::Button("+ Add Component")) {
ImGui::OpenPopup("Add Component");
}

ImGui::Separator();

if (registry.valid(e)) {
ImGui::PushID(entt::to_integral(e));
std::map<ComponentTypeID, ComponentInfo> has_not;
for (auto& [component_type_id, ci] : component_infos) {
if (entityHasComponent(registry, e, component_type_id)) {
ImGui::PushID(component_type_id);
if (ImGui::Button("-")) {
ci.destroy(registry, e);
ImGui::PopID();
continue; // early out to prevent access to deleted data
} else {
ImGui::SameLine();
}

if (ImGui::CollapsingHeader(ci.name.c_str())) {
ImGui::Indent(30.f);
ImGui::PushID("Widget");
ci.widget(registry, e);
ImGui::PopID();
ImGui::Unindent(30.f);
}
ImGui::PopID();
} else {
has_not[component_type_id] = ci;
if (ImGui::BeginPopup("Add Component")) {
ImGui::TextUnformatted("Available:");
ImGui::Separator();

for (auto& [component_type_id, ci] : has_not) {
ImGui::PushID(component_type_id);
if (ImGui::Selectable(ci.name.c_str())) {
ci.create(registry, e);
}
ImGui::PopID();
}
ImGui::EndPopup();
}
}
ImGui::PopID();
}
}

if (!has_not.empty()) {
if (ImGui::Button("+ Add Component")) {
ImGui::OpenPopup("Add Component");
}
void renderEntityList(Registry& registry, std::set<ComponentTypeID>& comp_list)
{
ImGui::Text("Components Filter:");
ImGui::SameLine();
if (ImGui::SmallButton("clear")) {
comp_list.clear();
}

if (ImGui::BeginPopup("Add Component")) {
ImGui::TextUnformatted("Available:");
ImGui::Separator();

for (auto& [component_type_id, ci] : has_not) {
ImGui::PushID(component_type_id);
if (ImGui::Selectable(ci.name.c_str())) {
ci.create(registry, e);
}
ImGui::PopID();
}
ImGui::EndPopup();
}
}
ImGui::PopID();
ImGui::Indent();

for (const auto& [component_type_id, ci] : component_infos) {
bool is_in_list = comp_list.count(component_type_id);
bool active = is_in_list;

ImGui::Checkbox(ci.name.c_str(), &active);

if (is_in_list && !active) { // remove
comp_list.erase(component_type_id);
} else if (!is_in_list && active) { // add
comp_list.emplace(component_type_id);
}
}

ImGui::Unindent();
ImGui::Separator();

if (comp_list.empty()) {
ImGui::Text("Orphans:");
registry.orphans([&registry](auto e){
MM_IEEE_ENTITY_WIDGET(e, registry, false);
});
} else {
auto view = registry.runtime_view(comp_list.begin(), comp_list.end());
ImGui::Text("%lu Entities Matching:", view.size());

if (ImGui::BeginChild("entity list")) {
for (auto e : view) {
MM_IEEE_ENTITY_WIDGET(e, registry, false);
}
}
ImGui::EndChild();
}
}

[[deprecated("Use renderEditor() instead. And manage the window yourself.")]]
void render(Registry& registry, EntityType& e)
{
if (show_window) {
if (ImGui::Begin("Entity Editor", &show_window)) {
renderEditor(registry, e);
}
ImGui::End();
}
}

// displays both, editor and list
// uses static internally, use only as a quick way to get going!
void renderSimpleCombo(Registry& registry, EntityType& e)
{
if (show_window) {
ImGui::SetNextWindowSize(ImVec2(550, 400), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Entity Editor", &show_window)) {
if (ImGui::BeginChild("list", {200, 0}, true)) {
static std::set<ComponentTypeID> comp_list;
renderEntityList(registry, comp_list);
}
ImGui::EndChild();

ImGui::SameLine();

if (ImGui::BeginChild("editor")) {
renderEditor(registry, e);
}
ImGui::EndChild();

}
ImGui::End();
}
}

};

} // MM
Expand Down

0 comments on commit 0b91302

Please sign in to comment.