Added Entityx to project

This commit is contained in:
Julian Nießner
2018-05-10 23:36:35 +02:00
parent 50fe9dbe02
commit 9083487ac9
58 changed files with 15308 additions and 1 deletions

View File

@@ -19,6 +19,7 @@ set(FREETYPE_DIR ${DarkRP2D_EXTERNAL_DIR}/freetype-2.9)
set(GLM_DIR ${DarkRP2D_EXTERNAL_DIR}/glm-0.9.9-a2)
set(BOX2D_DIR ${DarkRP2D_EXTERNAL_DIR}/Box2D-2.3.1)
set(IMGUI_DIR ${DarkRP2D_EXTERNAL_DIR}/imgui-1.60)
set(ENTITYX_DIR ${DarkRP2D_EXTERNAL_DIR}/entityx-1.1.2)
#Libs
find_package(SDL2 REQUIRED CONFIG)
@@ -41,6 +42,9 @@ find_package(OPENGL REQUIRED)
find_package(BOX2D REQUIRED CONFIG)
include_directories(${BOX2D_INCLUDE_DIRS})
find_package(ENTITYX REQUIRED CONFIG)
include_directories(${ENTITYX_INCLUDE_DIRS})
MESSAGE( STATUS "OpenGL Lib: " ${OPENGL_gl_LIBRARY} )
MESSAGE( STATUS "SDL2 Lib: " ${SDL2_LIBRARIES} )
MESSAGE( STATUS "SDL2_IMG Lib: " ${SDL2_IMAGE_LIBRARIES} )
@@ -63,7 +67,8 @@ target_link_libraries(DarkRP2D ${SDL2_LIBRARIES}
${GLEW_LIBRARIES}
${FREETYPE_LIBRARIES}
${OPENGL_gl_LIBRARY}
${BOX2D_LIBRARIES})
${BOX2D_LIBRARIES}
${ENTITYX_LIBRARIES})
add_custom_target(CopyBinaries
COMMAND ${CMAKE_COMMAND} -E copy ${SDL2_DLL} ${CMAKE_BINARY_DIR}
@@ -71,6 +76,7 @@ add_custom_target(CopyBinaries
COMMAND ${CMAKE_COMMAND} -E copy ${FREETYPE_DLL} ${CMAKE_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${GLEW_DLL} ${CMAKE_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${BOX2D_DLL} ${CMAKE_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${ENTITYX_DLL} ${CMAKE_BINARY_DIR}
)
add_dependencies(DarkRP2D CopyBinaries)

View File

@@ -0,0 +1,2 @@
-std=c++11
-I.

10
external/entityx-1.1.2/.gitattributes vendored Normal file
View File

@@ -0,0 +1,10 @@
* text=auto
*.cc text
*.h text
*.txt text
CMake* text
*.cmake text
*.md text
*.in text
Doxyfile text
Makefile text

10
external/entityx-1.1.2/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
.*.dep
*.a
*.so
*.o
build/*
entityx/config.h
Makefile
html/
Vagrantfile
.vagrant

15
external/entityx-1.1.2/.travis.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
language: cpp
compiler:
- clang
- gcc
before_install:
- sudo apt-add-repository -y ppa:jkeiren/ppa
- if test $CC = gcc; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi
- sudo apt-get update -qq
- if test $CC = gcc; then sudo apt-get install --yes --force-yes gcc-4.7 g++-4.7; fi
- if test $CC = gcc; then sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 20; fi
- if test $CC = gcc; then sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.7 20; fi
- if test $CC = gcc; then sudo update-alternatives --config gcc; fi
- if test $CC = gcc; then sudo update-alternatives --config g++; fi
script: ./scripts/travis.sh

19
external/entityx-1.1.2/Android.mk vendored Normal file
View File

@@ -0,0 +1,19 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := entityx
LOCAL_MODULE_FILENAME := libentityx
LOCAL_SRC_FILES := \
entityx/Entity.cc \
entityx/Event.cc \
entityx/System.cc \
entityx/help/Pool.cc \
entityx/help/Timer.cc \
LOCAL_C_INCLUDES := $(LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)

45
external/entityx-1.1.2/CHANGES.md vendored Normal file
View File

@@ -0,0 +1,45 @@
# Change Log
## 2014-03-02 - 1.0.0alpha1 - Cache coherence + breaking changes
EntityX has switched to a more cache-friendly memory layout for components. This is achieved by requiring the use of `assign<Component>(arg0, arg1, ...)` and removing `assign(component)`. This allows EntityX to explicitly control the layout of components. The current lyout algorithm reserves space for components in chunks (8192 by default).
This change also necessitated a move away from the use of `shared_ptr<>` for components, which I had never been that pleased with anyway. Replacing it is a very lightweight `ComponentHandle<C>` smart pointer. This checks for validity of the entity associated with the component, and validity of the component itself. It also allows future iterations of EntityX to do even more interesting things with memory layout if desirable.
## 2014-02-13 - Support for Visual C++
[Jarrett Chisholm](https://github.com/jarrettchisholm) has added conditional compilation support for VC++ and fixed some issues that prevented compilation, so EntityX now fully supports Visual C++!
You will need at least [Visual Studio 2013](http://www.microsoft.com/en-ca/download/details.aspx?id=40787) with [Update 1](http://www.microsoft.com/en-us/download/details.aspx?id=41650) and [Update 2 CTP](http://www.microsoft.com/en-us/download/details.aspx?id=41699) installed. The usual CMake installation instructions should "just work" and correctly provide VC++ support.
## 2013-10-29 [no-boost branch] - Removed boost dependency for everything except python integration.
This branch requires C++11 support and has removed all the non-boost::python dependecies, reducing the overhead of running entityx.
## 2013-08-22 - Remove `boost::signal` and switch to `Simple::Signal`.
According to the [benchmarks](http://timj.testbit.eu/2013/cpp11-signal-system-performance/) Simple::Signal is an order of magnitude faster than `boost::signal`. Additionally, `boost::signal` is now deprecated in favor of `boost::signal2`, which is not supported on versions of Boost on a number of platforms.
This is an implementation detail and should not affect EntityX users at all.
## 2013-08-18 - Destroying an entity invalidates all other references
Previously, `Entity::Id` was a simple integer index (slot) into vectors in the `EntityManager`. EntityX also maintains a list of deleted entity slots that are reused when new entities are created. This reduces the size and frequency of vector reallocation. The downside though, was that if a slot was reused, entity IDs referencing the entity before reallocation would be invalidated on reuse.
Each slot now also has a version number and a "valid" bit associated with it. When an entity is allocated the version is incremented and the valid bit set. When an entity is destroyed, the valid bit is cleared. `Entity::Id` now contains all of this information and can correctly determine if an ID is still valid across destroy/create.
## 2013-08-17 - Python scripting, and a more robust build system
Two big changes in this release:
1. Python scripting support (alpha).
- Bridges the EntityX entity-component system into Python.
- Components and entities can both be defined in Python.
- Systems must still be defined in C++, for performance reasons.
Note that there is one major design difference between the Python ECS model and the C++ model: entities in Python can receive and handle events.
See the [README](https://github.com/alecthomas/entityx/blob/master/entityx/python/README.md) for help, and the [C++](https://github.com/alecthomas/entityx/blob/master/entityx/python/PythonSystem_test.cc) and [Python](https://github.com/alecthomas/entityx/tree/master/entityx/python/entityx/tests) test source for more examples.
2. Made the build system much more robust, including automatic feature selection with manual override.

187
external/entityx-1.1.2/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,187 @@
cmake_minimum_required(VERSION 3.0)
set(ENTITYX_MAJOR_VERSION 1)
set(ENTITYX_MINOR_VERSION 1)
set(ENTITYX_PATCH_VERSION 2)
set(ENTITYX_VERSION ${ENTITYX_MAJOR_VERSION}.${ENTITYX_MINOR_VERSION}.${ENTITYX_PATCH_VERSION})
project(EntityX VERSION ${ENTITYX_VERSION})
message("EntityX version ${ENTITYX_VERSION}")
if(NOT DEFINED CMAKE_MACOSX_RPATH)
set(CMAKE_MACOSX_RPATH 0)
endif()
include_directories(${CMAKE_CURRENT_LIST_DIR})
set(ENTITYX_BUILD_TESTING true CACHE BOOL "Enable building of tests.")
set(ENTITYX_RUN_BENCHMARKS false CACHE BOOL "Run benchmarks (in conjunction with -DENTITYX_BUILD_TESTING=1).")
set(ENTITYX_MAX_COMPONENTS 64 CACHE STRING "Set the maximum number of components.")
set(ENTITYX_DT_TYPE double CACHE STRING "The type used for delta time in EntityX update methods.")
set(ENTITYX_BUILD_SHARED true CACHE BOOL "Build shared libraries?")
include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake)
include(CheckCXXSourceCompiles)
# Default compiler args
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|.*Clang)")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Werror -Wall -Wextra -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=sign-compare -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-g -Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-g -O2 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# /Zi - Produces a program database (PDB) that contains type information and symbolic debugging information for use with the debugger.
# /FS - Allows multiple cl.exe processes to write to the same .pdb file
# /DEBUG - Enable debug during linking
# /Od - Disables optimization
set(CMAKE_CXX_FLAGS_DEBUG "/Zi /FS /DEBUG /Od /MDd")
# /Ox - Full optimization
set(CMAKE_CXX_FLAGS_RELEASE "/Ox -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Ox /Zi /FS /DEBUG")
endif()
# if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything -Wno-c++98-compat -Wno-shadow -Wno-padded -Wno-missing-noreturn -Wno-global-constructors")
# endif()
# Library installation directory
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR lib)
endif(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
# C++11 feature checks
include(CheckCXX11Features.cmake)
set(OLD_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
check_cxx_source_compiles(
"
#include <memory>
int main() {
std::shared_ptr<int>();
}
"
ENTITYX_HAVE_CXX11_STDLIB
)
if (NOT ENTITYX_HAVE_CXX11_STDLIB)
message("-- Not using -stdlib=libc++ (test failed to build)")
set(CMAKE_CXX_FLAGS "${OLD_CMAKE_CXX_FLAGS}")
else ()
message("-- Using -stdlib=libc++")
endif ()
# Misc features
check_include_file("stdint.h" HAVE_STDINT_H)
macro(require FEATURE_NAME MESSAGE_STRING)
if (NOT ${${FEATURE_NAME}})
message(FATAL_ERROR "${MESSAGE_STRING} required -- ${${FEATURE_NAME}}")
else()
message("-- ${MESSAGE_STRING} found")
endif()
endmacro(require)
macro(create_test TARGET_NAME SOURCE)
add_executable(${TARGET_NAME} ${SOURCE})
target_link_libraries(
${TARGET_NAME}
entityx
${ARGN}
)
add_test(${TARGET_NAME} ${TARGET_NAME})
endmacro()
if (NOT CMAKE_BUILD_TYPE)
message("-- Defaulting to release build (use -DCMAKE_BUILD_TYPE:STRING=Debug for debug build)")
set(CMAKE_BUILD_TYPE "Release")
endif()
message("-- Checking C++ features")
require(HAS_CXX11_AUTO "C++11 auto support")
require(HAS_CXX11_NULLPTR "C++11 nullptr support")
require(HAS_CXX11_RVALUE_REFERENCES "C++11 rvalue reference support")
#require(HAS_CXX11_CSTDINT_H "C++11 stdint support")
require(HAS_CXX11_VARIADIC_TEMPLATES "C++11 variadic templates")
require(HAS_CXX11_RVALUE_REFERENCES "C++11 rvalue references")
require(HAS_CXX11_LONG_LONG "C++11 long long")
require(HAS_CXX11_LONG_LONG "C++11 lambdas")
message("-- Checking misc features")
require(HAVE_STDINT_H "stdint.h")
# Things to install
set(install_libs entityx)
set(sources entityx/System.cc entityx/Event.cc entityx/Entity.cc entityx/help/Timer.cc entityx/help/Pool.cc)
add_library(entityx STATIC ${sources})
set_target_properties(entityx PROPERTIES DEBUG_POSTFIX -d)
if (ENTITYX_BUILD_SHARED)
message("-- Building shared libraries (-DENTITYX_BUILD_SHARED=0 to only build static librarires)")
add_library(entityx_shared SHARED ${sources})
target_link_libraries(
entityx_shared
)
set_target_properties(entityx_shared PROPERTIES
OUTPUT_NAME entityx
VERSION ${ENTITYX_VERSION}
SOVERSION ${ENTITYX_MAJOR_VERSION})
list(APPEND install_libs entityx_shared)
endif (ENTITYX_BUILD_SHARED)
if (ENTITYX_BUILD_TESTING)
enable_testing()
create_test(pool_test entityx/help/Pool_test.cc)
create_test(entity_test entityx/Entity_test.cc)
create_test(event_test entityx/Event_test.cc)
create_test(system_test entityx/System_test.cc)
create_test(tags_component_test entityx/tags/TagsComponent_test.cc)
create_test(dependencies_test entityx/deps/Dependencies_test.cc)
if (ENTITYX_RUN_BENCHMARKS)
message("-- Running benchmarks")
create_test(benchmarks_test entityx/Benchmarks_test.cc)
else ()
message("-- Not running benchmarks (use -DENTITYX_RUN_BENCHMARKS=1 to enable)")
endif ()
endif (ENTITYX_BUILD_TESTING)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/entityx/config.h.in
${CMAKE_CURRENT_SOURCE_DIR}/entityx/config.h
)
if (NOT WINDOWS OR CYGWIN)
set(entityx_libs -lentityx)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/entityx.pc.in
${CMAKE_CURRENT_BINARY_DIR}/entityx.pc
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/entityx.pc
DESTINATION "${libdir}/pkgconfig"
)
endif()
install(
DIRECTORY "entityx"
DESTINATION "include"
FILES_MATCHING PATTERN "*.h"
)
install(
TARGETS ${install_libs}
LIBRARY DESTINATION "${libdir}"
ARCHIVE DESTINATION "${libdir}"
)

19
external/entityx-1.1.2/COPYING vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (C) 2012 Alec Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,105 @@
# Checks for C++11 features
# CXX11_FEATURE_LIST - a list containing all supported features
# HAS_CXX11_AUTO - auto keyword
# HAS_CXX11_NULLPTR - nullptr
# HAS_CXX11_LAMBDA - lambdas
# HAS_CXX11_STATIC_ASSERT - static_assert()
# HAS_CXX11_RVALUE_REFERENCES - rvalue references
# HAS_CXX11_DECLTYPE - decltype keyword
# HAS_CXX11_CSTDINT_H - cstdint header
# HAS_CXX11_LONG_LONG - long long signed & unsigned types
# HAS_CXX11_VARIADIC_TEMPLATES - variadic templates
# HAS_CXX11_CONSTEXPR - constexpr keyword
# HAS_CXX11_SIZEOF_MEMBER - sizeof() non-static members
# HAS_CXX11_FUNC - __func__ preprocessor constant
#
# Original script by Rolf Eike Beer
# Modifications by Andreas Weis
#
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3)
SET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
IF(CMAKE_COMPILER_IS_GNUCXX)
SET(CMAKE_CXX_FLAGS "-std=c++0x")
ELSE("${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" MATCHES ".*clang.*")
SET(CMAKE_CXX_FLAGS "-std=c++11")
ENDIF()
MACRO(CXX11_CHECK_FEATURE FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
IF (NOT DEFINED ${RESULT_VAR})
SET(_bindir "${CMAKE_CURRENT_BINARY_DIR}/cxx11/cxx11_${FEATURE_NAME}")
IF (${FEATURE_NUMBER})
SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/cxx11/c++11-test-${FEATURE_NAME}-N${FEATURE_NUMBER})
SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
ELSE (${FEATURE_NUMBER})
SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/cxx11/c++11-test-${FEATURE_NAME})
SET(_LOG_NAME "\"${FEATURE_NAME}\"")
ENDIF (${FEATURE_NUMBER})
MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME}")
SET(_SRCFILE "${_SRCFILE_BASE}.cpp")
SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp")
SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp")
IF (CROSS_COMPILING)
try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
ELSE (CROSS_COMPILING)
try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
"${_bindir}" "${_SRCFILE}")
IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
SET(${RESULT_VAR} TRUE)
ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
SET(${RESULT_VAR} FALSE)
ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
"${_bindir}_fail" "${_SRCFILE_FAIL}")
IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
SET(${RESULT_VAR} TRUE)
ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
SET(${RESULT_VAR} FALSE)
ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
ENDIF (CROSS_COMPILING)
IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
IF (_TMP_RESULT)
SET(${RESULT_VAR} FALSE)
ELSE (_TMP_RESULT)
SET(${RESULT_VAR} TRUE)
ENDIF (_TMP_RESULT)
ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
IF (${RESULT_VAR})
MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- works")
LIST(APPEND CXX11_FEATURE_LIST ${RESULT_VAR})
ELSE (${RESULT_VAR})
MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- not supported")
ENDIF (${RESULT_VAR})
SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}")
ENDIF (NOT DEFINED ${RESULT_VAR})
ENDMACRO(CXX11_CHECK_FEATURE)
CXX11_CHECK_FEATURE("auto" 2546 HAS_CXX11_AUTO)
CXX11_CHECK_FEATURE("nullptr" 2431 HAS_CXX11_NULLPTR)
CXX11_CHECK_FEATURE("lambda" 2927 HAS_CXX11_LAMBDA)
CXX11_CHECK_FEATURE("static_assert" 1720 HAS_CXX11_STATIC_ASSERT)
CXX11_CHECK_FEATURE("rvalue_references" 2118 HAS_CXX11_RVALUE_REFERENCES)
CXX11_CHECK_FEATURE("decltype" 2343 HAS_CXX11_DECLTYPE)
CXX11_CHECK_FEATURE("cstdint" "" HAS_CXX11_CSTDINT_H)
CXX11_CHECK_FEATURE("long_long" 1811 HAS_CXX11_LONG_LONG)
CXX11_CHECK_FEATURE("variadic_templates" 2555 HAS_CXX11_VARIADIC_TEMPLATES)
CXX11_CHECK_FEATURE("constexpr" 2235 HAS_CXX11_CONSTEXPR)
CXX11_CHECK_FEATURE("sizeof_member" 2253 HAS_CXX11_SIZEOF_MEMBER)
CXX11_CHECK_FEATURE("__func__" 2340 HAS_CXX11_FUNC)
SET(CXX11_FEATURE_LIST ${CXX11_FEATURE_LIST} CACHE STRING "C++11 feature support list")
MARK_AS_ADVANCED(FORCE CXX11_FEATURE_LIST)
SET(CMAKE_CXX_FLAGS ${CHECK_CXX11_OLD_CMAKE_CXX_FLAGS})
UNSET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS)

1896
external/entityx-1.1.2/Doxyfile vendored Normal file

File diff suppressed because it is too large Load Diff

392
external/entityx-1.1.2/README.md vendored Normal file
View File

@@ -0,0 +1,392 @@
# EntityX - A fast, type-safe C++ Entity Component System [![Build Status](https://travis-ci.org/alecthomas/entityx.png)](https://travis-ci.org/alecthomas/entityx) [![Build status](https://ci.appveyor.com/api/projects/status/qc8s0pqb5ci092iv/branch/master)](https://ci.appveyor.com/project/alecthomas/entityx/branch/master)
***NOTE: The current stable release 1.0.0 breaks backwards compataibility with < 1.0.0. See the [change log](CHANGES.md) for details.***
Entity Component Systems (ECS) are a form of decomposition that completely decouples entity logic and data from the entity "objects" themselves. The [Evolve your Hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/) article provides a solid overview of EC systems and why you should use them.
EntityX is an EC system that uses C++11 features to provide type-safe component management, event delivery, etc. It was built during the creation of a 2D space shooter.
## Downloading
You can acquire stable releases [here](https://github.com/alecthomas/entityx/releases).
Alternatively, you can check out the current development version with:
```
git clone https://github.com/alecthomas/entityx.git
```
See [below](#installation) for installation instructions.
## Contact
EntityX now has a mailing list! Send a mail to [entityx@librelist.com](mailto:entityx@librelist.com) to subscribe. Instructions will follow.
You can also contact me directly via [email](mailto:alec@swapoff.org) or [Twitter](https://twitter.com/alecthomas).
## Recent Notable Changes
- 2014-03-02 - (1.0.0alpha1) Switch to using cache friendly component storage (big breaking change). Also eradicated use of `std::shared_ptr` for components.
- 2014-02-13 - Visual C++ support thanks to [Jarrett Chisholm](https://github.com/jarrettchisholm)!
- 2013-10-29 - Boost has been removed as a primary dependency for builds not using python.
- 2013-08-21 - Remove dependency on `boost::signal` and switch to embedded [Simple::Signal](http://timj.testbit.eu/2013/cpp11-signal-system-performance/).
- 2013-08-18 - Destroying an entity invalidates all other references
- 2013-08-17 - Python scripting, and a more robust build system
See the [ChangeLog](https://github.com/alecthomas/entityx/blob/master/CHANGES.md) for details.
## EntityX extensions and example applications
- [Will Usher](https://github.com/Twinklebear) has also written an [Asteroids clone](https://github.com/Twinklebear/asteroids).
- [Roc Solid Productions](https://github.com/RocSolidProductions) have written a [space shooter](https://github.com/RocSolidProductions/Space-Shooter)!
- Giovani Milanez's first [game](https://github.com/giovani-milanez/SpaceTD).
- [A game](https://github.com/ggc87/BattleCity2014) using Ogre3D and EntityX.
**DEPRECATED - 0.1.x ONLY**
- [Wu Zhenwei](https://github.com/acaly) has written [Lua bindings](https://github.com/acaly/entityx_lua) for EntityX, allowing entity logic to be extended through Lua scripts.
- [Python bindings](https://github.com/alecthomas/entityx_python) allowing entity logic to be extended through Python scripts.
- [Rodrigo Setti](https://github.com/rodrigosetti) has written an OpenGL [Asteroids clone](https://github.com/rodrigosetti/azteroids) which uses EntityX.
## Example
An SFML2 example application is [available](/examples/example.cc) that shows most of EntityX's concepts. It spawns random circles on a 2D plane moving in random directions. If two circles collide they will explode and emit particles. All circles and particles are entities.
It illustrates:
- Separation of data via components.
- Separation of logic via systems.
- Use of events (colliding bodies trigger a CollisionEvent).
Compile with:
c++ -O3 -std=c++11 -Wall -lsfml-system -lsfml-window -lsfml-graphics -lentityx example.cc -o example
## Overview
In EntityX data associated with an entity is called a `entityx::Component`. `Systems` encapsulate logic and can use as many component types as necessary. An `entityx::EventManager` allows systems to interact without being tightly coupled. Finally, a `Manager` object ties all of the systems together for convenience.
As an example, a physics system might need *position* and *mass* data, while a collision system might only need *position* - the data would be logically separated into two components, but usable by any system. The physics system might emit *collision* events whenever two entities collide.
## Tutorial
Following is some skeleton code that implements `Position` and `Direction` components, a `MovementSystem` using these data components, and a `CollisionSystem` that emits `Collision` events when two entities collide.
To start with, add the following line to your source file:
```c++
#include "entityx/entityx.h"
```
### Entities
An `entityx::Entity` is a convenience class wrapping an opaque `uint64_t` value allocated by the `entityx::EntityManager`. Each entity has a set of components associated with it that can be added, queried or retrieved directly.
Creating an entity is as simple as:
```c++
#include <entityx/entityx.h>
EntityX entityx;
entityx::Entity entity = entityx.entities.create();
```
And destroying an entity is done with:
```c++
entity.destroy();
```
#### Implementation details
- Each `entityx::Entity` is a convenience class wrapping an `entityx::Entity::Id`.
- An `entityx::Entity` handle can be invalidated with `invalidate()`. This does not affect the underlying entity.
- When an entity is destroyed the manager adds its ID to a free list and invalidates the `entityx::Entity` handle.
- When an entity is created IDs are recycled from the free list first, before allocating new ones.
- An `entityx::Entity` ID contains an index and a version. When an entity is destroyed, the version associated with the index is incremented, invalidating all previous entities referencing the previous ID.
- To improve cache coherence, components are constructed in contiguous memory ranges by using `entityx::EntityManager::assign<C>(id, ...)`.
### Components (entity data)
The general idea with the EntityX interpretation of ECS is to have as little logic in components as possible. All logic should be contained in Systems.
To that end Components are typically [POD types](http://en.wikipedia.org/wiki/Plain_Old_Data_Structures) consisting of self-contained sets of related data. Components can be any user defined struct/class.
#### Creating components
As an example, position and direction information might be represented as:
```c++
struct Position {
Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
float x, y;
};
struct Direction {
Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
float x, y;
};
```
#### Assigning components to entities
To associate a component with a previously created entity call ``entityx::Entity::assign<C>()`` with the component type, and any component constructor arguments:
```c++
// Assign a Position with x=1.0f and y=2.0f to "entity"
entity.assign<Position>(1.0f, 2.0f);
```
#### Querying entities and their components
To query all entities with a set of components assigned, use ``entityx::EntityManager::entities_with_components()``. This method will return only those entities that have *all* of the specified components associated with them, assigning each component pointer to the corresponding component instance:
```c++
ComponentHandle<Position> position;
ComponentHandle<Direction> direction;
for (Entity entity : entities.entities_with_components(position, direction)) {
// Do things with entity, position and direction.
}
```
To retrieve a component associated with an entity use ``entityx::Entity::component<C>()``:
```c++
ComponentHandle<Position> position = entity.component<Position>();
if (position) {
// Do stuff with position
}
```
#### Component dependencies
In the case where a component has dependencies on other components, a helper class exists that will automatically create these dependencies.
eg. The following will also add `Position` and `Direction` components when a `Physics` component is added to an entity.
```c++
#include "entityx/deps/Dependencies.h"
system_manager->add<entityx::deps::Dependency<Physics, Position, Direction>>();
```
#### Implementation notes
- Components must provide a no-argument constructor.
- The default implementation can handle up to 64 components in total. This can be extended by changing the `entityx::EntityManager::MAX_COMPONENTS` constant.
- Each type of component is allocated in (mostly) contiguous blocks to improve cache coherency.
### Systems (implementing behavior)
Systems implement behavior using one or more components. Implementations are subclasses of `System<T>` and *must* implement the `update()` method, as shown below.
A basic movement system might be implemented with something like the following:
```c++
struct MovementSystem : public System<MovementSystem> {
void update(entityx::EntityManager &es, entityx::EventManager &events, TimeDelta dt) override {
ComponentHandle<Position> position;
ComponentHandle<Direction> direction;
for (Entity entity : es.entities_with_components(position, direction)) {
position->x += direction->x * dt;
position->y += direction->y * dt;
}
};
};
```
### Events (communicating between systems)
Events are objects emitted by systems, typically when some condition is met. Listeners subscribe to an event type and will receive a callback for each event object emitted. An ``entityx::EventManager`` coordinates subscription and delivery of events between subscribers and emitters. Typically subscribers will be other systems, but need not be.
Events are not part of the original ECS pattern, but they are an efficient alternative to component flags for sending infrequent data.
As an example, we might want to implement a very basic collision system using our ``Position`` data from above.
#### Creating event types
First, we define the event type, which for our example is simply the two entities that collided:
```c++
struct Collision {
Collision(entityx::Entity left, entityx::Entity right) : left(left), right(right) {}
entityx::Entity left, right;
};
```
#### Emitting events
Next we implement our collision system, which emits ``Collision`` objects via an ``entityx::EventManager`` instance whenever two entities collide.
```c++
class CollisionSystem : public System<CollisionSystem> {
public:
void update(entityx::EntityManager &es, entityx::EventManager &events, TimeDelta dt) override {
ComponentHandle<Position> left_position, right_position;
for (Entity left_entity : es.entities_with_components(left_position)) {
for (Entity right_entity : es.entities_with_components(right_position)) {
if (collide(left_position, right_position)) {
events.emit<Collision>(left_entity, right_entity);
}
}
}
};
};
```
#### Subscribing to events
Objects interested in receiving collision information can subscribe to ``Collision`` events by first subclassing the CRTP class ``Receiver<T>``:
```c++
struct DebugSystem : public System<DebugSystem>, Receiver<DebugSystem> {
void configure(entityx::EventManager &event_manager) {
event_manager.subscribe<Collision>(*this);
}
void update(entityx::EntityManager &entities, entityx::EventManager &events, TimeDelta dt) {}
void receive(const Collision &collision) {
LOG(DEBUG) << "entities collided: " << collision.left << " and " << collision.right << endl;
}
};
```
#### Builtin events
Several events are emitted by EntityX itself:
- `EntityCreatedEvent` - emitted when a new entityx::Entity has been created.
- `entityx::Entity entity` - Newly created entityx::Entity.
- `EntityDestroyedEvent` - emitted when an entityx::Entity is *about to be* destroyed.
- `entityx::Entity entity` - entityx::Entity about to be destroyed.
- `ComponentAddedEvent<C>` - emitted when a new component is added to an entity.
- `entityx::Entity entity` - entityx::Entity that component was added to.
- `ComponentHandle<C> component` - The component added.
- `ComponentRemovedEvent<C>` - emitted when a component is removed from an entity.
- `entityx::Entity entity` - entityx::Entity that component was removed from.
- `ComponentHandle<C> component` - The component removed.
#### Implementation notes
- There can be more than one subscriber for an event; each one will be called.
- Event objects are destroyed after delivery, so references should not be retained.
- A single class can receive any number of types of events by implementing a ``receive(const EventType &)`` method for each event type.
- Any class implementing `Receiver` can receive events, but typical usage is to make `System`s also be `Receiver`s.
### Manager (tying it all together)
Managing systems, components and entities can be streamlined by using the
"quick start" class `EntityX`. It simply provides pre-initialized
`EventManager`, `EntityManager` and `SystemManager` instances.
To use it, subclass `EntityX`:
```c++
class Level : public EntityX {
public:
explicit Level(filename string) {
systems.add<DebugSystem>();
systems.add<MovementSystem>();
systems.add<CollisionSystem>();
systems.configure();
level.load(filename);
for (auto e : level.entity_data()) {
entityx::Entity entity = entities.create();
entity.assign<Position>(rand() % 100, rand() % 100);
entity.assign<Direction>((rand() % 10) - 5, (rand() % 10) - 5);
}
}
void update(TimeDelta dt) {
systems.update<DebugSystem>(dt);
systems.update<MovementSystem>(dt);
systems.update<CollisionSystem>(dt);
}
Level level;
};
```
You can then step the entities explicitly inside your own game loop:
```c++
while (true) {
level.update(0.1);
}
```
## Installation
EntityX has the following build and runtime requirements:
- A C++ compiler that supports a basic set of C++11 features (ie. Clang >= 3.1, GCC >= 4.7, and Visual C++.
- For Visual C++ support you will need at least [Visual Studio 2013](http://www.microsoft.com/en-ca/download/details.aspx?id=40787) with [Update 1](http://www.microsoft.com/en-us/download/details.aspx?id=41650) and [Update 2 CTP](http://www.microsoft.com/en-us/download/details.aspx?id=41699) installed.
- [CMake](http://cmake.org/)
### C++11 compiler and library support
C++11 support is quite...raw. To make life more interesting, C++ support really means two things: language features supported by the compiler, and library features. EntityX tries to support the most common options, including the default C++ library for the compiler/platform, and libstdc++.
### Installing on OSX Mountain Lion
On OSX you must use Clang as the GCC version is practically prehistoric.
I use Homebrew, and the following works for me:
For libstdc++:
```bash
cmake -DENTITYX_BUILD_SHARED=0 -DENTITYX_BUILD_TESTING=1 ..
```
### Installing on Ubuntu 12.04
On Ubuntu LTS (12.04, Precise) you will need to add some PPAs to get either clang-3.1 or gcc-4.7. Respective versions prior to these do not work.
For gcc-4.7:
```bash
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update -qq
sudo apt-get install gcc-4.7 g++4.7
CC=gcc-4.7 CXX=g++4.7 cmake ...
```
For clang-3.1 (or 3.2 or 3.3):
```bash
sudo apt-add-repository ppa:h-rayflood/llvm
sudo apt-get update -qq
sudo apt-get install clang-3.1
CC=clang-3.1 CXX=clang++3.1 cmake ...
```
### Options
Once these dependencies are installed you should be able to build and install EntityX as below. The following options can be passed to cmake to modify how EntityX is built:
- `-DENTITYX_RUN_BENCHMARKS=1` - In conjunction with `-DENTITYX_BUILD_TESTING=1`, also build benchmarks.
- `-DENTITYX_MAX_COMPONENTS=64` - Override the maximum number of components that can be assigned to each entity.
- `-DENTITYX_BUILD_SHARED=1` - Whether to build shared libraries (defaults to 1).
- `-DENTITYX_BUILD_TESTING=1` - Whether to build tests (defaults to 0). Run with "make && make test".
- `-DENTITYX_DT_TYPE=double` - The type used for delta time in EntityX update methods.
Once you have selected your flags, build and install with:
```sh
mkdir build
cd build
cmake <flags> ..
make
make install
```
EntityX has currently only been tested on Mac OSX (Lion and Mountain Lion), and Linux Debian 12.04. Reports and patches for builds on other platforms are welcome.

View File

@@ -0,0 +1,28 @@
# Look for a version of EntityX on the local machine
#
# By default, this will look in all common places. If EntityX is built or
# installed in a custom location, you're able to either modify the
# CMakeCache.txt file yourself or simply pass the path to CMake using either the
# environment variable `ENTITYX_ROOT` or the CMake define with the same name.
set(ENTITYX_PATHS ${ENTITYX_ROOT}
$ENV{ENTITYX_ROOT}
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt)
find_path(ENTITYX_INCLUDE_DIR entityx/entityx.h PATH_SUFFIXES include PATHS ${ENTITYX_PATHS})
find_library(ENTITYX_LIBRARY NAMES entityx PATH_SUFFIXES lib PATHS ${ENTITYX_PATHS})
find_library(ENTITYX_LIBRARY_DEBUG NAMES entityx-d PATH_SUFFIXES lib PATHS ${ENTITYX_PATHS})
mark_as_advanced(ENTITYX_INCLUDE_DIR ENTITYX_LIBRARY)
if(ENTITYX_INCLUDE_DIR AND ENTITYX_LIBRARY)
set(ENTITYX_FOUND TRUE)
else()
set(ENTITYX_FOUND FALSE)
endif()

View File

@@ -0,0 +1,8 @@
#include <cstring>
int main()
{
if (!__func__) { return 1; }
if(std::strlen(__func__) <= 0) { return 1; }
return 0;
}

View File

@@ -0,0 +1,12 @@
int main()
{
auto i = 5;
auto f = 3.14159f;
auto d = 3.14159;
bool ret = (
(sizeof(f) < sizeof(d)) &&
(sizeof(i) == sizeof(int))
);
return ret ? 0 : 1;
}

View File

@@ -0,0 +1,19 @@
constexpr int square(int x)
{
return x*x;
}
constexpr int the_answer()
{
return 42;
}
int main()
{
int test_arr[square(3)];
bool ret = (
(square(the_answer()) == 1764) &&
(sizeof(test_arr)/sizeof(test_arr[0]) == 9)
);
return ret ? 0 : 1;
}

View File

@@ -0,0 +1,10 @@
#include <cstdint>
int main()
{
bool test =
(sizeof(std::int8_t) == 1) &&
(sizeof(std::int16_t) == 2) &&
(sizeof(std::int32_t) == 4) &&
(sizeof(std::int64_t) == 8);
return test ? 0 : 1;
}

View File

@@ -0,0 +1,11 @@
bool check_size(int i)
{
return sizeof(int) == sizeof(decltype(i));
}
int main()
{
bool ret = check_size(42);
return ret ? 0 : 1;
}

View File

@@ -0,0 +1,5 @@
int main()
{
int ret = 0;
return ([&ret]() -> int { return ret; })();
}

View File

@@ -0,0 +1,7 @@
int main(void)
{
long long l;
unsigned long long ul;
return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1;
}

View File

@@ -0,0 +1,5 @@
int main()
{
int* test = nullptr;
return test ? 1 : 0;
}

View File

@@ -0,0 +1,5 @@
int main()
{
int i = nullptr;
return 1;
}

View File

@@ -0,0 +1,15 @@
int foo(int& lvalue)
{
return 123;
}
int foo(int&& rvalue)
{
return 321;
}
int main()
{
int i = 42;
return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1;
}

View File

@@ -0,0 +1,14 @@
struct foo {
char bar;
int baz;
};
int main(void)
{
bool ret = (
(sizeof(foo::bar) == 1) &&
(sizeof(foo::baz) >= sizeof(foo::bar)) &&
(sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz))
);
return ret ? 0 : 1;
}

View File

@@ -0,0 +1,5 @@
int main()
{
static_assert(0 < 1, "your ordering of integers is screwed");
return 0;
}

View File

@@ -0,0 +1,5 @@
int main()
{
static_assert(1 < 0, "this should fail");
return 0;
}

View File

@@ -0,0 +1,23 @@
int Accumulate()
{
return 0;
}
template<typename T, typename... Ts>
int Accumulate(T v, Ts... vs)
{
return v + Accumulate(vs...);
}
template<int... Is>
int CountElements()
{
return sizeof...(Is);
}
int main()
{
int acc = Accumulate(1, 2, 3, 4, -5);
int count = CountElements<1,2,3,4,5>();
return ((acc == 5) && (count == 5)) ? 0 : 1;
}

23
external/entityx-1.1.2/cxx11/demo.cpp vendored Normal file
View File

@@ -0,0 +1,23 @@
#include <iostream>
int main()
{
std::cout << "Testing\n";
std::cout << "Has static_assert: " <<
#ifdef HAS_CXX11_STATIC_ASSERT
"yes :)"
#else
"no"
#endif
<< "\n";
std::cout << "Has variadic templates: " <<
#ifdef HAS_CXX11_VARIADIC_TEMPLATES
"yes :)"
#else
"no"
#endif
<< "\n";
return 0;
}

View File

@@ -0,0 +1,10 @@
set(ENTITYX_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/entityx")
# Support both 32 and 64 bit builds
if (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
else ()
set(ENTITYX_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/build/Release/entityx.lib")
set(ENTITYX_DLL "${CMAKE_CURRENT_LIST_DIR}/build/Release/entityx.dll")
endif ()
string(STRIP "${ENTITYX_LIBRARIES}" ENTITYX_LIBRARIES)

6
external/entityx-1.1.2/entityx.pc.in vendored Normal file
View File

@@ -0,0 +1,6 @@
# entityx pkg-config source file
Name: entityx
Description: EntityX is an EC system that uses C++11 features to provide type-safe component management, event delivery, etc.
Version: @ENTITYX_VERSION@
Libs: @entityx_libs@

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,580 @@
// CC0 Public Domain: http://creativecommons.org/publicdomain/zero/1.0/
#ifndef SIMPLE_SIGNAL_H__
#define SIMPLE_SIGNAL_H__
#include <assert.h>
#include <stdint.h>
#include <vector>
#include <functional>
namespace Simple {
namespace Lib {
/// ProtoSignal is the template implementation for callback list.
template <typename, typename>
class ProtoSignal; // undefined
/// CollectorInvocation invokes signal handlers differently depending on return
/// type.
template <typename, typename>
struct CollectorInvocation;
/// CollectorLast returns the result of the last signal handler from a signal
/// emission.
template <typename Result>
struct CollectorLast {
typedef Result CollectorResult;
explicit CollectorLast() : last_() {}
inline bool operator()(Result r) {
last_ = r;
return true;
}
CollectorResult result() { return last_; }
private:
Result last_;
};
/// CollectorDefault implements the default signal handler collection behaviour.
template <typename Result>
struct CollectorDefault : CollectorLast<Result> {};
/// CollectorDefault specialisation for signals with void return type.
template <>
struct CollectorDefault<void> {
typedef void CollectorResult;
void result() {}
inline bool operator()(void) { return true; }
};
/// CollectorInvocation specialisation for regular signals.
template <class Collector, class R, class... Args>
struct CollectorInvocation<Collector, R(Args...)> {
inline bool invoke(Collector &collector, const std::function<R(Args...)> &cbf,
Args... args) {
return collector(cbf(args...));
}
};
/// CollectorInvocation specialisation for signals with void return type.
template <class Collector, class... Args>
struct CollectorInvocation<Collector, void(Args...)> {
inline bool invoke(Collector &collector,
const std::function<void(Args...)> &cbf, Args... args) {
cbf(args...);
return collector();
}
};
/// ProtoSignal template specialised for the callback signature and collector.
template <class Collector, class R, class... Args>
class ProtoSignal<R(Args...), Collector> : private CollectorInvocation<
Collector, R(Args...)> {
protected:
typedef std::function<R(Args...)> CbFunction;
typedef typename CbFunction::result_type Result;
typedef typename Collector::CollectorResult CollectorResult;
private:
/// SignalLink implements a doubly-linked ring with ref-counted nodes
/// containing the signal handlers.
struct SignalLink {
SignalLink *next, *prev;
CbFunction function;
int ref_count;
explicit SignalLink(const CbFunction &cbf)
: next(0), prev(0), function(cbf), ref_count(1) {}
/*dtor*/ ~SignalLink() { assert(ref_count == 0); }
void incref() {
ref_count += 1;
assert(ref_count > 0);
}
void decref() {
ref_count -= 1;
if (!ref_count)
delete this;
else
assert(ref_count > 0);
}
void unlink() {
function = nullptr;
if (next) next->prev = prev;
if (prev) prev->next = next;
decref();
// leave intact ->next, ->prev for stale iterators
}
size_t add_before(const CbFunction &cb) {
SignalLink *link = new SignalLink(cb);
link->prev = prev; // link to last
link->next = this;
prev->next = link; // link from last
prev = link;
static_assert(sizeof(link) == sizeof(size_t), "sizeof size_t");
return size_t(link);
}
bool deactivate(const CbFunction &cbf) {
if (cbf == function) {
function = 0; // deactivate static head
return true;
}
for (SignalLink *link = this->next ? this->next : this; link != this;
link = link->next)
if (cbf == link->function) {
link->unlink(); // deactivate and unlink sibling
return true;
}
return false;
}
bool remove_sibling(size_t id) {
for (SignalLink *link = this->next ? this->next : this; link != this;
link = link->next)
if (id == size_t(link)) {
link->unlink(); // deactivate and unlink sibling
return true;
}
return false;
}
};
SignalLink *callback_ring_; // linked ring of callback nodes
/*copy-ctor*/ ProtoSignal(const ProtoSignal &) = delete;
ProtoSignal &operator=(const ProtoSignal &) = delete;
void ensure_ring() {
if (!callback_ring_) {
callback_ring_ = new SignalLink(CbFunction()); // ref_count = 1
callback_ring_->incref(); // ref_count = 2, head of ring, can be
// deactivated but not removed
callback_ring_->next = callback_ring_; // ring head initialization
callback_ring_->prev = callback_ring_; // ring tail initialization
}
}
public:
/// ProtoSignal constructor, connects default callback if non-0.
ProtoSignal(const CbFunction &method) : callback_ring_(0) {
if (method != 0) {
ensure_ring();
callback_ring_->function = method;
}
}
/// ProtoSignal destructor releases all resources associated with this signal.
~ProtoSignal() {
if (callback_ring_) {
while (callback_ring_->next != callback_ring_)
callback_ring_->next->unlink();
assert(callback_ring_->ref_count >= 2);
callback_ring_->decref();
callback_ring_->decref();
}
}
/// Operator to add a new function or lambda as signal handler, returns a
/// handler connection ID.
size_t connect(const CbFunction &cb) {
ensure_ring();
return callback_ring_->add_before(cb);
}
/// Operator to remove a signal handler through it connection ID, returns if a
/// handler was removed.
bool disconnect(size_t connection) {
return callback_ring_ ? callback_ring_->remove_sibling(connection) : false;
}
/// Emit a signal, i.e. invoke all its callbacks and collect return types with
/// the Collector.
CollectorResult emit(Args... args) {
Collector collector;
if (!callback_ring_) return collector.result();
SignalLink *link = callback_ring_;
link->incref();
do {
if (link->function != 0) {
const bool continue_emission =
this->invoke(collector, link->function, args...);
if (!continue_emission) break;
}
SignalLink *old = link;
link = old->next;
link->incref();
old->decref();
} while (link != callback_ring_);
link->decref();
return collector.result();
}
// Number of connected slots.
std::size_t size() {
std::size_t size = 0;
SignalLink *link = callback_ring_;
link->incref();
do {
if (link->function != 0) {
size++;
}
SignalLink *old = link;
link = old->next;
link->incref();
old->decref();
} while (link != callback_ring_);
return size;
}
};
} // Lib
// namespace Simple
/**
* Signal is a template type providing an interface for arbitrary callback
* lists.
* A signal type needs to be declared with the function signature of its
* callbacks,
* and optionally a return result collector class type.
* Signal callbacks can be added with operator+= to a signal and removed with
* operator-=, using
* a callback connection ID return by operator+= as argument.
* The callbacks of a signal are invoked with the emit() method and arguments
* according to the signature.
* The result returned by emit() depends on the signal collector class. By
* default, the result of
* the last callback is returned from emit(). Collectors can be implemented to
* accumulate callback
* results or to halt a running emissions in correspondance to callback results.
* The signal implementation is safe against recursion, so callbacks may be
* removed and
* added during a signal emission and recursive emit() calls are also safe.
* The overhead of an unused signal is intentionally kept very low, around the
* size of a single pointer.
* Note that the Signal template types is non-copyable.
*/
template <typename SignalSignature,
class Collector = Lib::CollectorDefault<
typename std::function<SignalSignature>::result_type>>
struct Signal /*final*/ : Lib::ProtoSignal<SignalSignature, Collector> {
typedef Lib::ProtoSignal<SignalSignature, Collector> ProtoSignal;
typedef typename ProtoSignal::CbFunction CbFunction;
/// Signal constructor, supports a default callback as argument.
Signal(const CbFunction &method = CbFunction()) : ProtoSignal(method) {}
};
/// This function creates a std::function by binding @a object to the member
/// function pointer @a method.
template <class Instance, class Class, class R, class... Args>
std::function<R(Args...)> slot(Instance &object, R (Class::*method)(Args...)) {
return [&object, method](Args... args) { return (object.*method)(args...); };
}
/// This function creates a std::function by binding @a object to the member
/// function pointer @a method.
template <class Class, class R, class... Args>
std::function<R(Args...)> slot(Class *object, R (Class::*method)(Args...)) {
return [object, method](Args... args) { return (object->*method)(args...); };
}
/// Keep signal emissions going while all handlers return !0 (true).
template <typename Result>
struct CollectorUntil0 {
typedef Result CollectorResult;
explicit CollectorUntil0() : result_() {}
const CollectorResult &result() { return result_; }
inline bool operator()(Result r) {
result_ = r;
return result_ ? true : false;
}
private:
CollectorResult result_;
};
/// Keep signal emissions going while all handlers return 0 (false).
template <typename Result>
struct CollectorWhile0 {
typedef Result CollectorResult;
explicit CollectorWhile0() : result_() {}
const CollectorResult &result() { return result_; }
inline bool operator()(Result r) {
result_ = r;
return result_ ? false : true;
}
private:
CollectorResult result_;
};
/// CollectorVector returns the result of the all signal handlers from a signal
/// emission in a std::vector.
template <typename Result>
struct CollectorVector {
typedef std::vector<Result> CollectorResult;
const CollectorResult &result() { return result_; }
inline bool operator()(Result r) {
result_.push_back(r);
return true;
}
private:
CollectorResult result_;
};
} // Simple
#endif // SIMPLE_SIGNAL_H__
#ifdef ENABLE_SIMPLE_SIGNAL_TESTS
#include <string>
#include <stdarg.h>
#include <time.h>
#include <sys/time.h>
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif
static std::string string_printf(const char *format, ...)
__attribute__((__format__(__printf__, 1, 2)));
static std::string string_printf(const char *format, ...) {
std::string result;
char *str = 0;
va_list args;
va_start(args, format);
if (vasprintf(&str, format, args) >= 0) result = str;
va_end(args);
if (str) free(str);
return result;
}
static uint64_t timestamp_benchmark() {
struct timespec tp = {0, 0};
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
tp.tv_sec = mts.tv_sec;
tp.tv_nsec = mts.tv_nsec;
#else
clock_gettime(CLOCK_REALTIME, &tp);
#endif
uint64_t stamp = tp.tv_sec * 1000000000ULL + tp.tv_nsec;
return stamp;
}
struct TestCounter {
static uint64_t get();
static void set(uint64_t);
static void add2(void *, uint64_t);
};
namespace { // Anon
void (*test_counter_add2)(void *, uint64_t) =
TestCounter::add2; // external symbol to prevent easy inlining
static uint64_t test_counter_var = 0;
} // Anon
class BasicSignalTests {
static std::string accu;
struct Foo {
char foo_bool(float f, int i, std::string s) {
accu += string_printf("Foo: %.2f\n", f + i + s.size());
return true;
}
};
static char float_callback(float f, int, std::string) {
accu += string_printf("float: %.2f\n", f);
return 0;
}
public:
static void run() {
accu = "";
Simple::Signal<char(float, int, std::string)> sig1;
size_t id1 = sig1.connect(float_callback);
size_t id2 = sig1.connect([](float, int i, std::string) {
accu += string_printf("int: %d\n", i);
return 0;
});
size_t id3 = sig1.connect([](float, int, const std::string &s) {
accu += string_printf("string: %s\n", s.c_str());
return 0;
});
sig1.emit(.3, 4, "huhu");
bool success;
success = sig1.disconnect(id1);
assert(success == true);
success = sig1.disconnect(id1);
assert(success == false);
success = sig1.disconnect(id2);
assert(success == true);
success = sig1.disconnect(id3);
assert(success == true);
success = sig1.disconnect(id3);
assert(success == false);
success = sig1.disconnect(id2);
assert(success == false);
Foo foo;
sig1.connect(Simple::slot(foo, &Foo::foo_bool));
sig1.connect(Simple::slot(&foo, &Foo::foo_bool));
sig1.emit(.5, 1, "12");
Simple::Signal<void(std::string, int)> sig2;
sig2.connect([](std::string msg,
int) { accu += string_printf("msg: %s", msg.c_str()); });
sig2.connect([](std::string,
int d) { accu += string_printf(" *%d*\n", d); });
sig2.emit("in sig2", 17);
accu += "DONE";
const char *expected =
"float: 0.30\n"
"int: 4\n"
"string: huhu\n"
"Foo: 3.50\n"
"Foo: 3.50\n"
"msg: in sig2 *17*\n"
"DONE";
assert(accu == expected);
}
};
std::string BasicSignalTests::accu;
class TestCollectorVector {
static int handler1() { return 1; }
static int handler42() { return 42; }
static int handler777() { return 777; }
public:
static void run() {
Simple::Signal<int(), Simple::CollectorVector<int>> sig_vector;
sig_vector.connect(handler777);
sig_vector.connect(handler42);
sig_vector.connect(handler1);
sig_vector.connect(handler42);
sig_vector.connect(handler777);
std::vector<int> results = sig_vector.emit();
const std::vector<int> reference = {777, 42, 1, 42, 777, };
assert(results == reference);
}
};
class TestCollectorUntil0 {
bool check1, check2;
TestCollectorUntil0() : check1(0), check2(0) {}
bool handler_true() {
check1 = true;
return true;
}
bool handler_false() {
check2 = true;
return false;
}
bool handler_abort() { abort(); }
public:
static void run() {
TestCollectorUntil0 self;
Simple::Signal<bool(), Simple::CollectorUntil0<bool>> sig_until0;
sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_true));
sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_false));
sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_abort));
assert(!self.check1 && !self.check2);
const bool result = sig_until0.emit();
assert(!result && self.check1 && self.check2);
}
};
class TestCollectorWhile0 {
bool check1, check2;
TestCollectorWhile0() : check1(0), check2(0) {}
bool handler_0() {
check1 = true;
return false;
}
bool handler_1() {
check2 = true;
return true;
}
bool handler_abort() { abort(); }
public:
static void run() {
TestCollectorWhile0 self;
Simple::Signal<bool(), Simple::CollectorWhile0<bool>> sig_while0;
sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_0));
sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_1));
sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_abort));
assert(!self.check1 && !self.check2);
const bool result = sig_while0.emit();
assert(result == true && self.check1 && self.check2);
}
};
static void bench_simple_signal() {
Simple::Signal<void(void *, uint64_t)> sig_increment;
sig_increment.connect(test_counter_add2);
const uint64_t start_counter = TestCounter::get();
const uint64_t benchstart = timestamp_benchmark();
uint64_t i;
for (i = 0; i < 999999; i++) {
sig_increment.emit(0, 1);
}
const uint64_t benchdone = timestamp_benchmark();
const uint64_t end_counter = TestCounter::get();
assert(end_counter - start_counter == i);
printf("OK\n Benchmark: Simple::Signal: %fns per emission (size=%zu): ",
size_t(benchdone - benchstart) * 1.0 / size_t(i),
sizeof(sig_increment));
}
static void bench_callback_loop() {
void (*counter_increment)(void *, uint64_t) = test_counter_add2;
const uint64_t start_counter = TestCounter::get();
const uint64_t benchstart = timestamp_benchmark();
uint64_t i;
for (i = 0; i < 999999; i++) {
counter_increment(0, 1);
}
const uint64_t benchdone = timestamp_benchmark();
const uint64_t end_counter = TestCounter::get();
assert(end_counter - start_counter == i);
printf("OK\n Benchmark: callback loop: %fns per round: ",
size_t(benchdone - benchstart) * 1.0 / size_t(i));
}
uint64_t TestCounter::get() { return test_counter_var; }
void TestCounter::set(uint64_t v) { test_counter_var = v; }
void TestCounter::add2(void *, uint64_t v) { test_counter_var += v; }
int main(int argc, char *argv[]) {
printf("Signal/Basic Tests: ");
BasicSignalTests::run();
printf("OK\n");
printf("Signal/CollectorVector: ");
TestCollectorVector::run();
printf("OK\n");
printf("Signal/CollectorUntil0: ");
TestCollectorUntil0::run();
printf("OK\n");
printf("Signal/CollectorWhile0: ");
TestCollectorWhile0::run();
printf("OK\n");
printf("Signal/Benchmark: Simple::Signal: ");
bench_simple_signal();
printf("OK\n");
printf("Signal/Benchmark: callback loop: ");
bench_callback_loop();
printf("OK\n");
return 0;
}
#endif // DISABLE_TESTS
// g++ -Wall -O2 -std=gnu++0x -pthread simplesignal.cc -lrt && ./a.out

View File

@@ -0,0 +1,146 @@
#define CATCH_CONFIG_MAIN
#include <iostream>
#include <vector>
#include "entityx/3rdparty/catch.hpp"
#include "entityx/help/Timer.h"
#include "entityx/Entity.h"
using namespace std;
using namespace entityx;
using std::uint64_t;
struct AutoTimer {
~AutoTimer() {
cout << timer_.elapsed() << " seconds elapsed" << endl;
}
private:
entityx::help::Timer timer_;
};
struct Listener : public Receiver<Listener> {
void receive(const EntityCreatedEvent &event) { ++created; }
void receive(const EntityDestroyedEvent &event) { ++destroyed; }
int created = 0;
int destroyed = 0;
};
struct Position : public Component<Position> {
};
struct Direction : public Component<Direction> {
};
struct BenchmarkFixture {
BenchmarkFixture() : em(ev) {}
EventManager ev;
EntityManager em;
};
TEST_CASE_METHOD(BenchmarkFixture, "TestCreateEntities") {
AutoTimer t;
uint64_t count = 10000000L;
cout << "creating " << count << " entities" << endl;
for (uint64_t i = 0; i < count; i++) {
em.create();
}
}
TEST_CASE_METHOD(BenchmarkFixture, "TestDestroyEntities") {
uint64_t count = 10000000L;
vector<Entity> entities;
for (uint64_t i = 0; i < count; i++) {
entities.push_back(em.create());
}
AutoTimer t;
cout << "destroying " << count << " entities" << endl;
for (auto e : entities) {
e.destroy();
}
}
TEST_CASE_METHOD(BenchmarkFixture, "TestCreateEntitiesWithListener") {
Listener listen;
ev.subscribe<EntityCreatedEvent>(listen);
int count = 10000000L;
AutoTimer t;
cout << "creating " << count << " entities while notifying a single EntityCreatedEvent listener" << endl;
vector<Entity> entities;
for (int i = 0; i < count; i++) {
entities.push_back(em.create());
}
REQUIRE(entities.size() == count);
REQUIRE(listen.created == count);
}
TEST_CASE_METHOD(BenchmarkFixture, "TestDestroyEntitiesWithListener") {
int count = 10000000;
vector<Entity> entities;
for (int i = 0; i < count; i++) {
entities.push_back(em.create());
}
Listener listen;
ev.subscribe<EntityDestroyedEvent>(listen);
AutoTimer t;
cout << "destroying " << count << " entities while notifying a single EntityDestroyedEvent listener" << endl;
for (auto &e : entities) {
e.destroy();
}
REQUIRE(entities.size() == count);
REQUIRE(listen.destroyed == count);
}
TEST_CASE_METHOD(BenchmarkFixture, "TestEntityIteration") {
int count = 10000000;
for (int i = 0; i < count; i++) {
auto e = em.create();
e.assign<Position>();
}
AutoTimer t;
cout << "iterating over " << count << " entities, unpacking one component" << endl;
ComponentHandle<Position> position;
for (auto e : em.entities_with_components(position)) {
(void)e;
}
}
TEST_CASE_METHOD(BenchmarkFixture, "TestEntityIterationUnpackTwo") {
int count = 10000000;
for (int i = 0; i < count; i++) {
auto e = em.create();
e.assign<Position>();
e.assign<Direction>();
}
AutoTimer t;
cout << "iterating over " << count << " entities, unpacking two components" << endl;
ComponentHandle<Position> position;
ComponentHandle<Direction> direction;
for (auto e : em.entities_with_components(position, direction)) {
(void)e;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#include <algorithm>
#include "entityx/Entity.h"
namespace entityx {
const Entity::Id Entity::INVALID;
BaseComponent::Family BaseComponent::family_counter_ = 0;
void Entity::invalidate() {
id_ = INVALID;
manager_ = nullptr;
}
void Entity::destroy() {
assert(valid());
manager_->destroy(id_);
invalidate();
}
std::bitset<entityx::MAX_COMPONENTS> Entity::component_mask() const {
return manager_->component_mask(id_);
}
EntityManager::EntityManager(EventManager &event_manager) : event_manager_(event_manager) {
}
EntityManager::~EntityManager() {
reset();
}
void EntityManager::reset() {
for (Entity entity : entities_for_debugging()) entity.destroy();
for (BasePool *pool : component_pools_) {
if (pool) delete pool;
}
component_pools_.clear();
entity_component_mask_.clear();
entity_version_.clear();
free_list_.clear();
index_counter_ = 0;
}
EntityCreatedEvent::~EntityCreatedEvent() {}
EntityDestroyedEvent::~EntityDestroyedEvent() {}
} // namespace entityx

987
external/entityx-1.1.2/entityx/Entity.h vendored Normal file
View File

@@ -0,0 +1,987 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#pragma once
#include <cstdint>
#include <tuple>
#include <new>
#include <cstdlib>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <iostream>
#include <iterator>
#include <list>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <type_traits>
#include "entityx/help/Pool.h"
#include "entityx/config.h"
#include "entityx/Event.h"
#include "entityx/help/NonCopyable.h"
namespace entityx {
typedef std::uint32_t uint32_t;
typedef std::uint64_t uint64_t;
class EntityManager;
template <typename C, typename EM = EntityManager>
class ComponentHandle;
/** A convenience handle around an Entity::Id.
*
* If an entity is destroyed, any copies will be invalidated. Use valid() to
* check for validity before using.
*
* Create entities with `EntityManager`:
*
* Entity entity = entity_manager->create();
*/
class Entity {
public:
struct Id {
Id() : id_(0) {}
explicit Id(uint64_t id) : id_(id) {}
Id(uint32_t index, uint32_t version) : id_(uint64_t(index) | uint64_t(version) << 32UL) {}
uint64_t id() const { return id_; }
bool operator == (const Id &other) const { return id_ == other.id_; }
bool operator != (const Id &other) const { return id_ != other.id_; }
bool operator < (const Id &other) const { return id_ < other.id_; }
uint32_t index() const { return id_ & 0xffffffffUL; }
uint32_t version() const { return id_ >> 32; }
private:
friend class EntityManager;
uint64_t id_;
};
/**
* Id of an invalid Entity.
*/
static const Id INVALID;
Entity() = default;
Entity(EntityManager *manager, Entity::Id id) : manager_(manager), id_(id) {}
Entity(const Entity &other) = default;
Entity &operator = (const Entity &other) = default;
/**
* Check if Entity handle is invalid.
*/
operator bool() const {
return valid();
}
bool operator == (const Entity &other) const {
return other.manager_ == manager_ && other.id_ == id_;
}
bool operator != (const Entity &other) const {
return !(other == *this);
}
bool operator < (const Entity &other) const {
return other.id_ < id_;
}
/**
* Is this Entity handle valid?
*
* In older versions of EntityX, there were no guarantees around entity
* validity if a previously allocated entity slot was reassigned. That is no
* longer the case: if a slot is reassigned, old Entity::Id's will be
* invalid.
*/
bool valid() const;
/**
* Invalidate Entity handle, disassociating it from an EntityManager and invalidating its ID.
*
* Note that this does *not* affect the underlying entity and its
* components. Use destroy() to destroy the associated Entity and components.
*/
void invalidate();
Id id() const { return id_; }
template <typename C, typename ... Args>
ComponentHandle<C> assign(Args && ... args);
template <typename C>
ComponentHandle<C> assign_from_copy(const C &component);
template <typename C, typename ... Args>
ComponentHandle<C> replace(Args && ... args);
template <typename C>
void remove();
template <typename C, typename = typename std::enable_if<!std::is_const<C>::value>::type>
ComponentHandle<C> component();
template <typename C, typename = typename std::enable_if<std::is_const<C>::value>::type>
const ComponentHandle<C, const EntityManager> component() const;
template <typename ... Components>
std::tuple<ComponentHandle<Components>...> components();
template <typename ... Components>
std::tuple<ComponentHandle<const Components, const EntityManager>...> components() const;
template <typename C>
bool has_component() const;
template <typename A, typename ... Args>
void unpack(ComponentHandle<A> &a, ComponentHandle<Args> & ... args);
/**
* Destroy and invalidate this Entity.
*/
void destroy();
std::bitset<entityx::MAX_COMPONENTS> component_mask() const;
private:
EntityManager *manager_ = nullptr;
Entity::Id id_ = INVALID;
};
/**
* A ComponentHandle<C> is a wrapper around an instance of a component.
*
* It provides safe access to components. The handle will be invalidated under
* the following conditions:
*
* - If a component is removed from its host entity.
* - If its host entity is destroyed.
*/
template <typename C, typename EM>
class ComponentHandle {
public:
typedef C ComponentType;
ComponentHandle() : manager_(nullptr) {}
bool valid() const;
operator bool() const;
C *operator -> ();
const C *operator -> () const;
C *get();
const C *get() const;
/**
* Remove the component from its entity and destroy it.
*/
void remove();
bool operator == (const ComponentHandle<C> &other) const {
return manager_ == other.manager_ && id_ == other.id_;
}
bool operator != (const ComponentHandle<C> &other) const {
return !(*this == other);
}
private:
friend class EntityManager;
ComponentHandle(EM *manager, Entity::Id id) :
manager_(manager), id_(id) {}
EM *manager_;
Entity::Id id_;
};
/**
* Base component class, only used for insertion into collections.
*
* Family is used for registration.
*/
struct BaseComponent {
public:
typedef size_t Family;
// NOTE: Component memory is *always* managed by the EntityManager.
// Use Entity::destroy() instead.
void operator delete(void *p) { fail(); }
void operator delete[](void *p) { fail(); }
protected:
static void fail() {
#if defined(_HAS_EXCEPTIONS) || defined(__EXCEPTIONS)
throw std::bad_alloc();
#else
std::abort();
#endif
}
static Family family_counter_;
};
/**
* Component implementations should inherit from this.
*
* Components MUST provide a no-argument constructor.
* Components SHOULD provide convenience constructors for initializing on assignment to an Entity::Id.
*
* This is a struct to imply that components should be data-only.
*
* Usage:
*
* struct Position : public Component<Position> {
* Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
*
* float x, y;
* };
*
* family() is used for registration.
*/
template <typename Derived>
struct Component : public BaseComponent {
public:
typedef ComponentHandle<Derived> Handle;
typedef ComponentHandle<const Derived, const EntityManager> ConstHandle;
private:
friend class EntityManager;
/// Used internally for registration.
static Family family();
};
/**
* Emitted when an entity is added to the system.
*/
struct EntityCreatedEvent : public Event<EntityCreatedEvent> {
explicit EntityCreatedEvent(Entity entity) : entity(entity) {}
virtual ~EntityCreatedEvent();
Entity entity;
};
/**
* Called just prior to an entity being destroyed.
*/
struct EntityDestroyedEvent : public Event<EntityDestroyedEvent> {
explicit EntityDestroyedEvent(Entity entity) : entity(entity) {}
virtual ~EntityDestroyedEvent();
Entity entity;
};
/**
* Emitted when any component is added to an entity.
*/
template <typename C>
struct ComponentAddedEvent : public Event<ComponentAddedEvent<C>> {
ComponentAddedEvent(Entity entity, ComponentHandle<C> component) :
entity(entity), component(component) {}
Entity entity;
ComponentHandle<C> component;
};
/**
* Emitted when any component is removed from an entity.
*/
template <typename C>
struct ComponentRemovedEvent : public Event<ComponentRemovedEvent<C>> {
ComponentRemovedEvent(Entity entity, ComponentHandle<C> component) :
entity(entity), component(component) {}
Entity entity;
ComponentHandle<C> component;
};
/**
* Manages Entity::Id creation and component assignment.
*/
class EntityManager : entityx::help::NonCopyable {
public:
typedef std::bitset<entityx::MAX_COMPONENTS> ComponentMask;
explicit EntityManager(EventManager &event_manager);
virtual ~EntityManager();
/// An iterator over a view of the entities in an EntityManager.
/// If All is true it will iterate over all valid entities and will ignore the entity mask.
template <class Delegate, bool All = false>
class ViewIterator : public std::iterator<std::input_iterator_tag, Entity::Id> {
public:
Delegate &operator ++() {
++i_;
next();
return *static_cast<Delegate*>(this);
}
bool operator == (const Delegate& rhs) const { return i_ == rhs.i_; }
bool operator != (const Delegate& rhs) const { return i_ != rhs.i_; }
Entity operator * () { return Entity(manager_, manager_->create_id(i_)); }
const Entity operator * () const { return Entity(manager_, manager_->create_id(i_)); }
protected:
ViewIterator(EntityManager *manager, uint32_t index)
: manager_(manager), i_(index), capacity_(manager_->capacity()), free_cursor_(~0UL) {
if (All) {
std::sort(manager_->free_list_.begin(), manager_->free_list_.end());
free_cursor_ = 0;
}
}
ViewIterator(EntityManager *manager, const ComponentMask mask, uint32_t index)
: manager_(manager), mask_(mask), i_(index), capacity_(manager_->capacity()), free_cursor_(~0UL) {
if (All) {
std::sort(manager_->free_list_.begin(), manager_->free_list_.end());
free_cursor_ = 0;
}
}
void next() {
while (i_ < capacity_ && !predicate()) {
++i_;
}
if (i_ < capacity_) {
Entity entity = manager_->get(manager_->create_id(i_));
static_cast<Delegate*>(this)->next_entity(entity);
}
}
inline bool predicate() {
return (All && valid_entity()) || (manager_->entity_component_mask_[i_] & mask_) == mask_;
}
inline bool valid_entity() {
const std::vector<uint32_t> &free_list = manager_->free_list_;
if (free_cursor_ < free_list.size() && free_list[free_cursor_] == i_) {
++free_cursor_;
return false;
}
return true;
}
EntityManager *manager_;
ComponentMask mask_;
uint32_t i_;
size_t capacity_;
size_t free_cursor_;
};
template <bool All>
class BaseView {
public:
class Iterator : public ViewIterator<Iterator, All> {
public:
Iterator(EntityManager *manager,
const ComponentMask mask,
uint32_t index) : ViewIterator<Iterator, All>(manager, mask, index) {
ViewIterator<Iterator, All>::next();
}
void next_entity(Entity &entity) {}
};
Iterator begin() { return Iterator(manager_, mask_, 0); }
Iterator end() { return Iterator(manager_, mask_, uint32_t(manager_->capacity())); }
const Iterator begin() const { return Iterator(manager_, mask_, 0); }
const Iterator end() const { return Iterator(manager_, mask_, manager_->capacity()); }
private:
friend class EntityManager;
explicit BaseView(EntityManager *manager) : manager_(manager) { mask_.set(); }
BaseView(EntityManager *manager, ComponentMask mask) :
manager_(manager), mask_(mask) {}
EntityManager *manager_;
ComponentMask mask_;
};
typedef BaseView<false> View;
typedef BaseView<true> DebugView;
template <typename ... Components>
class UnpackingView {
public:
struct Unpacker {
explicit Unpacker(ComponentHandle<Components> & ... handles) :
handles(std::tuple<ComponentHandle<Components> & ...>(handles...)) {}
void unpack(entityx::Entity &entity) const {
unpack_<0, Components...>(entity);
}
private:
template <int N, typename C>
void unpack_(entityx::Entity &entity) const {
std::get<N>(handles) = entity.component<C>();
}
template <int N, typename C0, typename C1, typename ... Cn>
void unpack_(entityx::Entity &entity) const {
std::get<N>(handles) = entity.component<C0>();
unpack_<N + 1, C1, Cn...>(entity);
}
std::tuple<ComponentHandle<Components> & ...> handles;
};
class Iterator : public ViewIterator<Iterator> {
public:
Iterator(EntityManager *manager,
const ComponentMask mask,
uint32_t index,
const Unpacker &unpacker) : ViewIterator<Iterator>(manager, mask, index), unpacker_(unpacker) {
ViewIterator<Iterator>::next();
}
void next_entity(Entity &entity) {
unpacker_.unpack(entity);
}
private:
const Unpacker &unpacker_;
};
Iterator begin() { return Iterator(manager_, mask_, 0, unpacker_); }
Iterator end() { return Iterator(manager_, mask_, manager_->capacity(), unpacker_); }
const Iterator begin() const { return Iterator(manager_, mask_, 0, unpacker_); }
const Iterator end() const { return Iterator(manager_, mask_, manager_->capacity(), unpacker_); }
private:
friend class EntityManager;
UnpackingView(EntityManager *manager, ComponentMask mask, ComponentHandle<Components> & ... handles) :
manager_(manager), mask_(mask), unpacker_(handles...) {}
EntityManager *manager_;
ComponentMask mask_;
Unpacker unpacker_;
};
/**
* Number of managed entities.
*/
size_t size() const { return entity_component_mask_.size() - free_list_.size(); }
/**
* Current entity capacity.
*/
size_t capacity() const { return entity_component_mask_.size(); }
/**
* Return true if the given entity ID is still valid.
*/
bool valid(Entity::Id id) const {
return id.index() < entity_version_.size() && entity_version_[id.index()] == id.version();
}
/**
* Create a new Entity::Id.
*
* Emits EntityCreatedEvent.
*/
Entity create() {
uint32_t index, version;
if (free_list_.empty()) {
index = index_counter_++;
accomodate_entity(index);
version = entity_version_[index] = 1;
} else {
index = free_list_.back();
free_list_.pop_back();
version = entity_version_[index];
}
Entity entity(this, Entity::Id(index, version));
event_manager_.emit<EntityCreatedEvent>(entity);
return entity;
}
/**
* Destroy an existing Entity::Id and its associated Components.
*
* Emits EntityDestroyedEvent.
*/
void destroy(Entity::Id entity) {
assert_valid(entity);
uint32_t index = entity.index();
auto mask = entity_component_mask_[entity.index()];
event_manager_.emit<EntityDestroyedEvent>(Entity(this, entity));
for (size_t i = 0; i < component_pools_.size(); i++) {
BasePool *pool = component_pools_[i];
if (pool && mask.test(i))
pool->destroy(index);
}
entity_component_mask_[index].reset();
entity_version_[index]++;
free_list_.push_back(index);
}
Entity get(Entity::Id id) {
assert_valid(id);
return Entity(this, id);
}
/**
* Create an Entity::Id for a slot.
*
* NOTE: Does *not* check for validity, but the Entity::Id constructor will
* fail if the ID is invalid.
*/
Entity::Id create_id(uint32_t index) const {
return Entity::Id(index, entity_version_[index]);
}
/**
* Assign a Component to an Entity::Id, passing through Component constructor arguments.
*
* Position &position = em.assign<Position>(e, x, y);
*
* @returns Smart pointer to newly created component.
*/
template <typename C, typename ... Args>
ComponentHandle<C> assign(Entity::Id id, Args && ... args) {
assert_valid(id);
const BaseComponent::Family family = component_family<C>();
assert(!entity_component_mask_[id.index()].test(family));
// Placement new into the component pool.
Pool<C> *pool = accomodate_component<C>();
new(pool->get(id.index())) C(std::forward<Args>(args) ...);
// Set the bit for this component.
entity_component_mask_[id.index()].set(family);
// Create and return handle.
ComponentHandle<C> component(this, id);
event_manager_.emit<ComponentAddedEvent<C>>(Entity(this, id), component);
return component;
}
/**
* Remove a Component from an Entity::Id
*
* Emits a ComponentRemovedEvent<C> event.
*/
template <typename C>
void remove(Entity::Id id) {
assert_valid(id);
const BaseComponent::Family family = component_family<C>();
const uint32_t index = id.index();
// Find the pool for this component family.
BasePool *pool = component_pools_[family];
ComponentHandle<C> component(this, id);
event_manager_.emit<ComponentRemovedEvent<C>>(Entity(this, id), component);
// Remove component bit.
entity_component_mask_[id.index()].reset(family);
// Call destructor.
pool->destroy(index);
}
/**
* Check if an Entity has a component.
*/
template <typename C>
bool has_component(Entity::Id id) const {
assert_valid(id);
size_t family = component_family<C>();
// We don't bother checking the component mask, as we return a nullptr anyway.
if (family >= component_pools_.size())
return false;
BasePool *pool = component_pools_[family];
if (!pool || !entity_component_mask_[id.index()][family])
return false;
return true;
}
/**
* Retrieve a Component assigned to an Entity::Id.
*
* @returns Pointer to an instance of C, or nullptr if the Entity::Id does not have that Component.
*/
template <typename C, typename = typename std::enable_if<!std::is_const<C>::value>::type>
ComponentHandle<C> component(Entity::Id id) {
assert_valid(id);
size_t family = component_family<C>();
// We don't bother checking the component mask, as we return a nullptr anyway.
if (family >= component_pools_.size())
return ComponentHandle<C>();
BasePool *pool = component_pools_[family];
if (!pool || !entity_component_mask_[id.index()][family])
return ComponentHandle<C>();
return ComponentHandle<C>(this, id);
}
/**
* Retrieve a Component assigned to an Entity::Id.
*
* @returns Component instance, or nullptr if the Entity::Id does not have that Component.
*/
template <typename C, typename = typename std::enable_if<std::is_const<C>::value>::type>
const ComponentHandle<C, const EntityManager> component(Entity::Id id) const {
assert_valid(id);
size_t family = component_family<C>();
// We don't bother checking the component mask, as we return a nullptr anyway.
if (family >= component_pools_.size())
return ComponentHandle<C, const EntityManager>();
BasePool *pool = component_pools_[family];
if (!pool || !entity_component_mask_[id.index()][family])
return ComponentHandle<C, const EntityManager>();
return ComponentHandle<C, const EntityManager>(this, id);
}
template <typename ... Components>
std::tuple<ComponentHandle<Components>...> components(Entity::Id id) {
return std::make_tuple(component<Components>(id)...);
}
template <typename ... Components>
std::tuple<ComponentHandle<const Components, const EntityManager>...> components(Entity::Id id) const {
return std::make_tuple(component<const Components>(id)...);
}
/**
* Find Entities that have all of the specified Components.
*
* @code
* for (Entity entity : entity_manager.entities_with_components<Position, Direction>()) {
* ComponentHandle<Position> position = entity.component<Position>();
* ComponentHandle<Direction> direction = entity.component<Direction>();
*
* ...
* }
* @endcode
*/
template <typename ... Components>
View entities_with_components() {
auto mask = component_mask<Components ...>();
return View(this, mask);
}
/**
* Find Entities that have all of the specified Components and assign them
* to the given parameters.
*
* @code
* ComponentHandle<Position> position;
* ComponentHandle<Direction> direction;
* for (Entity entity : entity_manager.entities_with_components(position, direction)) {
* // Use position and component here.
* }
* @endcode
*/
template <typename ... Components>
UnpackingView<Components...> entities_with_components(ComponentHandle<Components> & ... components) {
auto mask = component_mask<Components...>();
return UnpackingView<Components...>(this, mask, components...);
}
/**
* Iterate over all *valid* entities (ie. not in the free list). Not fast,
* so should only be used for debugging.
*
* @code
* for (Entity entity : entity_manager.entities_for_debugging()) {}
*
* @return An iterator view over all valid entities.
*/
DebugView entities_for_debugging() {
return DebugView(this);
}
template <typename C>
void unpack(Entity::Id id, ComponentHandle<C> &a) {
assert_valid(id);
a = component<C>(id);
}
/**
* Unpack components directly into pointers.
*
* Components missing from the entity will be set to nullptr.
*
* Useful for fast bulk iterations.
*
* ComponentHandle<Position> p;
* ComponentHandle<Direction> d;
* unpack<Position, Direction>(e, p, d);
*/
template <typename A, typename ... Args>
void unpack(Entity::Id id, ComponentHandle<A> &a, ComponentHandle<Args> & ... args) {
assert_valid(id);
a = component<A>(id);
unpack<Args ...>(id, args ...);
}
/**
* Destroy all entities and reset the EntityManager.
*/
void reset();
// Retrieve the component family for a type.
template <typename C>
static BaseComponent::Family component_family() {
return Component<typename std::remove_const<C>::type>::family();
}
private:
friend class Entity;
template <typename C, typename EM>
friend class ComponentHandle;
inline void assert_valid(Entity::Id id) const {
assert(id.index() < entity_component_mask_.size() && "Entity::Id ID outside entity vector range");
assert(entity_version_[id.index()] == id.version() && "Attempt to access Entity via a stale Entity::Id");
}
template <typename C>
C *get_component_ptr(Entity::Id id) {
assert(valid(id));
BasePool *pool = component_pools_[component_family<C>()];
assert(pool);
return static_cast<C*>(pool->get(id.index()));
}
template <typename C>
const C *get_component_ptr(Entity::Id id) const {
assert_valid(id);
BasePool *pool = component_pools_[component_family<C>()];
assert(pool);
return static_cast<const C*>(pool->get(id.index()));
}
ComponentMask component_mask(Entity::Id id) {
assert_valid(id);
return entity_component_mask_.at(id.index());
}
template <typename C>
ComponentMask component_mask() {
ComponentMask mask;
mask.set(component_family<C>());
return mask;
}
template <typename C1, typename C2, typename ... Components>
ComponentMask component_mask() {
return component_mask<C1>() | component_mask<C2, Components ...>();
}
template <typename C>
ComponentMask component_mask(const ComponentHandle<C> &c) {
return component_mask<C>();
}
template <typename C1, typename ... Components>
ComponentMask component_mask(const ComponentHandle<C1> &c1, const ComponentHandle<Components> &... args) {
return component_mask<C1, Components ...>();
}
inline void accomodate_entity(uint32_t index) {
if (entity_component_mask_.size() <= index) {
entity_component_mask_.resize(index + 1);
entity_version_.resize(index + 1);
for (BasePool *pool : component_pools_)
if (pool) pool->expand(index + 1);
}
}
template <typename C>
Pool<C> *accomodate_component() {
BaseComponent::Family family = component_family<C>();
if (component_pools_.size() <= family) {
component_pools_.resize(family + 1, nullptr);
}
if (!component_pools_[family]) {
Pool<C> *pool = new Pool<C>();
pool->expand(index_counter_);
component_pools_[family] = pool;
}
return static_cast<Pool<C>*>(component_pools_[family]);
}
uint32_t index_counter_ = 0;
EventManager &event_manager_;
// Each element in component_pools_ corresponds to a Pool for a Component.
// The index into the vector is the Component::family().
std::vector<BasePool*> component_pools_;
// Bitmask of components associated with each entity. Index into the vector is the Entity::Id.
std::vector<ComponentMask> entity_component_mask_;
// Vector of entity version numbers. Incremented each time an entity is destroyed
std::vector<uint32_t> entity_version_;
// List of available entity slots.
std::vector<uint32_t> free_list_;
};
template <typename C>
BaseComponent::Family Component<C>::family() {
static Family family = family_counter_++;
assert(family < entityx::MAX_COMPONENTS);
return family;
}
template <typename C, typename ... Args>
ComponentHandle<C> Entity::assign(Args && ... args) {
assert(valid());
return manager_->assign<C>(id_, std::forward<Args>(args) ...);
}
template <typename C>
ComponentHandle<C> Entity::assign_from_copy(const C &component) {
assert(valid());
return manager_->assign<C>(id_, std::forward<const C &>(component));
}
template <typename C, typename ... Args>
ComponentHandle<C> Entity::replace(Args && ... args) {
assert(valid());
auto handle = component<C>();
if (handle) {
*(handle.get()) = C(std::forward<Args>(args) ...);
} else {
handle = manager_->assign<C>(id_, std::forward<Args>(args) ...);
}
return handle;
}
template <typename C>
void Entity::remove() {
assert(valid() && has_component<C>());
manager_->remove<C>(id_);
}
template <typename C, typename>
ComponentHandle<C> Entity::component() {
assert(valid());
return manager_->component<C>(id_);
}
template <typename C, typename>
const ComponentHandle<C, const EntityManager> Entity::component() const {
assert(valid());
return const_cast<const EntityManager*>(manager_)->component<const C>(id_);
}
template <typename ... Components>
std::tuple<ComponentHandle<Components>...> Entity::components() {
assert(valid());
return manager_->components<Components...>(id_);
}
template <typename ... Components>
std::tuple<ComponentHandle<const Components, const EntityManager>...> Entity::components() const {
assert(valid());
return const_cast<const EntityManager*>(manager_)->components<const Components...>(id_);
}
template <typename C>
bool Entity::has_component() const {
assert(valid());
return manager_->has_component<C>(id_);
}
template <typename A, typename ... Args>
void Entity::unpack(ComponentHandle<A> &a, ComponentHandle<Args> & ... args) {
assert(valid());
manager_->unpack(id_, a, args ...);
}
inline bool Entity::valid() const {
return manager_ && manager_->valid(id_);
}
inline std::ostream &operator << (std::ostream &out, const Entity::Id &id) {
out << "Entity::Id(" << id.index() << "." << id.version() << ")";
return out;
}
inline std::ostream &operator << (std::ostream &out, const Entity &entity) {
out << "Entity(" << entity.id() << ")";
return out;
}
template <typename C, typename EM>
inline ComponentHandle<C, EM>::operator bool() const {
return valid();
}
template <typename C, typename EM>
inline bool ComponentHandle<C, EM>::valid() const {
return manager_ && manager_->valid(id_) && manager_->template has_component<C>(id_);
}
template <typename C, typename EM>
inline C *ComponentHandle<C, EM>::operator -> () {
assert(valid());
return manager_->template get_component_ptr<C>(id_);
}
template <typename C, typename EM>
inline const C *ComponentHandle<C, EM>::operator -> () const {
assert(valid());
return manager_->template get_component_ptr<C>(id_);
}
template <typename C, typename EM>
inline C *ComponentHandle<C, EM>::get() {
assert(valid());
return manager_->template get_component_ptr<C>(id_);
}
template <typename C, typename EM>
inline const C *ComponentHandle<C, EM>::get() const {
assert(valid());
return manager_->template get_component_ptr<C>(id_);
}
template <typename C, typename EM>
inline void ComponentHandle<C, EM>::remove() {
assert(valid());
manager_->template remove<C>(id_);
}
} // namespace entityx

View File

@@ -0,0 +1,606 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#define CATCH_CONFIG_MAIN
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include <set>
#include <map>
#include "entityx/3rdparty/catch.hpp"
#include "entityx/entityx.h"
// using namespace std;
using namespace entityx;
using std::ostream;
using std::vector;
using std::set;
using std::map;
using std::pair;
using std::string;
template <typename T>
int size(const T &t) {
int n = 0;
for (auto i : t) {
++n;
(void)i; // Unused on purpose, suppress warning
}
return n;
}
struct Position {
Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
bool operator==(const Position &other) const {
return x == other.x && y == other.y;
}
float x, y;
};
ostream &operator<<(ostream &out, const Position &position) {
out << "Position(" << position.x << ", " << position.y << ")";
return out;
}
struct Direction {
Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
bool operator==(const Direction &other) const {
return x == other.x && y == other.y;
}
float x, y;
};
ostream &operator<<(ostream &out, const Direction &direction) {
out << "Direction(" << direction.x << ", " << direction.y << ")";
return out;
}
struct Tag : Component<Tag> {
explicit Tag(string tag) : tag(tag) {}
bool operator==(const Tag &other) const { return tag == other.tag; }
string tag;
};
ostream &operator<<(ostream &out, const Tag &tag) {
out << "Tag(" << tag.tag << ")";
return out;
}
struct EntityManagerFixture {
EntityManagerFixture() : em(ev) {}
EventManager ev;
EntityManager em;
};
TEST_CASE_METHOD(EntityManagerFixture, "TestCreateEntity") {
REQUIRE(em.size() == 0UL);
Entity e2;
REQUIRE(!(e2.valid()));
Entity e = em.create();
REQUIRE(e.valid());
REQUIRE(em.size() == 1UL);
e2 = e;
REQUIRE(e2.valid());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAsBoolean") {
REQUIRE(em.size() == 0UL);
Entity e = em.create();
REQUIRE(e.valid());
REQUIRE(em.size() == 1UL);
REQUIRE(!(!e));
e.destroy();
REQUIRE(em.size() == 0UL);
REQUIRE(!e);
Entity e2; // Not initialized
REQUIRE(!e2);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityReuse") {
Entity e1 = em.create();
Entity e2 = e1;
auto id = e1.id();
REQUIRE(e1.valid());
REQUIRE(e2.valid());
e1.destroy();
REQUIRE(!e1.valid());
REQUIRE(!e2.valid());
Entity e3 = em.create();
// It is assumed that the allocation will reuse the same entity id, though
// the version will change.
auto new_id = e3.id();
REQUIRE(new_id != id);
REQUIRE((new_id.id() & 0xffffffffUL) == (id.id() & 0xffffffffUL));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentConstruction") {
auto e = em.create();
auto p = e.assign<Position>(1, 2);
auto cp = e.component<Position>();
REQUIRE(p == cp);
REQUIRE(1.0 == Approx(cp->x));
REQUIRE(2.0 == Approx(cp->y));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestDestroyEntity") {
Entity e = em.create();
Entity f = em.create();
e.assign<Position>();
f.assign<Position>();
e.assign<Direction>();
f.assign<Direction>();
REQUIRE(e.valid());
REQUIRE(f.valid());
REQUIRE(static_cast<bool>(e.component<Position>()));
REQUIRE(static_cast<bool>(e.component<Direction>()));
REQUIRE(static_cast<bool>(f.component<Position>()));
REQUIRE(static_cast<bool>(f.component<Direction>()));
e.destroy();
REQUIRE(!(e.valid()));
REQUIRE(f.valid());
REQUIRE(static_cast<bool>(f.component<Position>()));
REQUIRE(static_cast<bool>(f.component<Direction>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponent") {
Entity e = em.create();
Entity f = em.create();
Entity g = em.create();
e.assign<Position>();
e.assign<Direction>();
f.assign<Position>();
g.assign<Position>();
REQUIRE(3 == size(em.entities_with_components<Position>()));
REQUIRE(1 == size(em.entities_with_components<Direction>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithIntersectionOfComponents") {
vector<Entity> entities;
for (int i = 0; i < 150; ++i) {
Entity e = em.create();
entities.push_back(e);
if (i % 2 == 0) e.assign<Position>();
if (i % 3 == 0) e.assign<Direction>();
}
REQUIRE(50 == size(em.entities_with_components<Direction>()));
REQUIRE(75 == size(em.entities_with_components<Position>()));
REQUIRE(25 == size(em.entities_with_components<Direction, Position>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponentAndUnpacking") {
vector<Entity::Id> entities;
Entity e = em.create();
Entity f = em.create();
Entity g = em.create();
std::vector<std::pair<ComponentHandle<Position>, ComponentHandle<Direction>>> position_directions;
position_directions.push_back(std::make_pair(
e.assign<Position>(1.0f, 2.0f), e.assign<Direction>(3.0f, 4.0f)));
position_directions.push_back(std::make_pair(
f.assign<Position>(7.0f, 8.0f), f.assign<Direction>(9.0f, 10.0f)));
auto thetag = f.assign<Tag>("tag");
g.assign<Position>(5.0f, 6.0f);
int i = 0;
ComponentHandle<Position> position;
REQUIRE(3 == size(em.entities_with_components(position)));
ComponentHandle<Direction> direction;
for (auto unused_entity : em.entities_with_components(position, direction)) {
(void)unused_entity;
REQUIRE(position);
REQUIRE(direction);
auto pd = position_directions.at(i);
REQUIRE(position == pd.first);
REQUIRE(direction == pd.second);
++i;
}
REQUIRE(2 == i);
ComponentHandle<Tag> tag;
i = 0;
for (auto unused_entity :
em.entities_with_components(position, direction, tag)) {
(void)unused_entity;
REQUIRE(static_cast<bool>(position));
REQUIRE(static_cast<bool>(direction));
REQUIRE(static_cast<bool>(tag));
auto pd = position_directions.at(1);
REQUIRE(position == pd.first);
REQUIRE(direction == pd.second);
REQUIRE(tag == thetag);
i++;
}
REQUIRE(1 == i);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestIterateAllEntitiesSkipsDestroyed") {
Entity a = em.create();
Entity b = em.create();
Entity c = em.create();
b.destroy();
auto it = em.entities_for_debugging().begin();
REQUIRE(a.id() == (*it).id());
++it;
REQUIRE(c.id() == (*it).id());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestUnpack") {
Entity e = em.create();
auto p = e.assign<Position>(1.0, 2.0);
auto d = e.assign<Direction>(3.0, 4.0);
auto t = e.assign<Tag>("tag");
ComponentHandle<Position> up;
ComponentHandle<Direction> ud;
ComponentHandle<Tag> ut;
e.unpack(up);
REQUIRE(p == up);
e.unpack(up, ud);
REQUIRE(p == up);
REQUIRE(d == ud);
e.unpack(up, ud, ut);
REQUIRE(p == up);
REQUIRE(d == ud);
REQUIRE(t == ut);
}
// gcc 4.7.2 does not allow this struct to be declared locally inside the
// TEST_CASE_METHOD.EntityManagerFixture, " //" TEST_CASE_METHOD(EntityManagerFixture, "TestUnpackNullMissing") {
// Entity e = em.create();
// auto p = e.assign<Position>();
// std::shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef),
// NullDeleter());
// std::shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef),
// NullDeleter());
// e.unpack<Position, Direction>(up, ud);
// REQUIRE(p == up);
// REQUIRE(std::shared_ptr<Direction>() == ud);
// }
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentIdsDiffer") {
REQUIRE(EntityManager::component_family<Position>() != EntityManager::component_family<Direction>());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityCreatedEvent") {
struct EntityCreatedEventReceiver
: public Receiver<EntityCreatedEventReceiver> {
void receive(const EntityCreatedEvent &event) {
created.push_back(event.entity);
}
vector<Entity> created;
};
EntityCreatedEventReceiver receiver;
ev.subscribe<EntityCreatedEvent>(receiver);
REQUIRE(0UL == receiver.created.size());
for (int i = 0; i < 10; ++i) {
em.create();
}
REQUIRE(10UL == receiver.created.size());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyedEvent") {
struct EntityDestroyedEventReceiver
: public Receiver<EntityDestroyedEventReceiver> {
void receive(const EntityDestroyedEvent &event) {
destroyed.push_back(event.entity);
}
vector<Entity> destroyed;
};
EntityDestroyedEventReceiver receiver;
ev.subscribe<EntityDestroyedEvent>(receiver);
REQUIRE(0UL == receiver.destroyed.size());
vector<Entity> entities;
for (int i = 0; i < 10; ++i) {
entities.push_back(em.create());
}
REQUIRE(0UL == receiver.destroyed.size());
for (auto e : entities) {
e.destroy();
}
REQUIRE(10UL == receiver.destroyed.size());
REQUIRE(entities == receiver.destroyed);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAddedEvent") {
struct ComponentAddedEventReceiver
: public Receiver<ComponentAddedEventReceiver> {
ComponentAddedEventReceiver()
: position_events(0), direction_events(0) {}
void receive(const ComponentAddedEvent<Position> &event) {
auto p = event.component;
float n = static_cast<float>(position_events);
REQUIRE(p->x == n);
REQUIRE(p->y == n);
position_events++;
}
void receive(const ComponentAddedEvent<Direction> &event) {
auto p = event.component;
float n = static_cast<float>(direction_events);
REQUIRE(p->x == -n);
REQUIRE(p->y == -n);
direction_events++;
}
int position_events;
int direction_events;
};
ComponentAddedEventReceiver receiver;
ev.subscribe<ComponentAddedEvent<Position>>(receiver);
ev.subscribe<ComponentAddedEvent<Direction>>(receiver);
REQUIRE(ComponentAddedEvent<Position>::family() !=
ComponentAddedEvent<Direction>::family());
REQUIRE(0 == receiver.position_events);
REQUIRE(0 == receiver.direction_events);
for (int i = 0; i < 10; ++i) {
Entity e = em.create();
e.assign<Position>(static_cast<float>(i), static_cast<float>(i));
e.assign<Direction>(static_cast<float>(-i), static_cast<float>(-i));
}
REQUIRE(10 == receiver.position_events);
REQUIRE(10 == receiver.direction_events);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentRemovedEvent") {
struct ComponentRemovedReceiver : public Receiver<ComponentRemovedReceiver> {
void receive(const ComponentRemovedEvent<Direction> &event) {
removed = event.component;
}
ComponentHandle<Direction> removed;
};
ComponentRemovedReceiver receiver;
ev.subscribe<ComponentRemovedEvent<Direction>>(receiver);
REQUIRE(!(receiver.removed));
Entity e = em.create();
ComponentHandle<Direction> p = e.assign<Direction>(1.0, 2.0);
e.remove<Direction>();
REQUIRE(receiver.removed == p);
REQUIRE(!(e.component<Direction>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAssignment") {
Entity a, b;
a = em.create();
REQUIRE(a != b);
b = a;
REQUIRE(a == b);
a.invalidate();
REQUIRE(a != b);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyAll") {
Entity a = em.create(), b = em.create();
em.reset();
REQUIRE(!(a.valid()));
REQUIRE(!(b.valid()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyHole") {
std::vector<Entity> entities;
auto count = [this]()->int {
auto e = em.entities_with_components<Position>();
return std::count_if(e.begin(), e.end(),
[](const Entity &) { return true; });
};
for (int i = 0; i < 5000; i++) {
auto e = em.create();
e.assign<Position>();
entities.push_back(e);
}
REQUIRE(count() == 5000);
entities[2500].destroy();
REQUIRE(count() == 4999);
}
// TODO(alec): Disable this on OSX - it doesn't seem to be possible to catch it?!?
// TEST_CASE_METHOD(EntityManagerFixture, "DeleteComponentThrowsBadAlloc") {
// Position *position = new Position();
// REQUIRE_THROWS_AS(delete position, std::bad_alloc);
// }
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenEntityDestroyed") {
Entity a = em.create();
ComponentHandle<Position> position = a.assign<Position>(1, 2);
REQUIRE(position);
REQUIRE(position->x == 1);
REQUIRE(position->y == 2);
a.destroy();
REQUIRE(!position);
}
struct CopyVerifier : Component<CopyVerifier> {
CopyVerifier() : copied(false) {}
CopyVerifier(const CopyVerifier &other) {
copied = other.copied + 1;
}
int copied;
};
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAssignmentFromCopy") {
Entity a = em.create();
CopyVerifier original;
ComponentHandle<CopyVerifier> copy = a.assign_from_copy(original);
REQUIRE(copy);
REQUIRE(copy->copied == 1);
a.destroy();
REQUIRE(!copy);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenComponentDestroyed") {
Entity a = em.create();
ComponentHandle<Position> position = a.assign<Position>(1, 2);
REQUIRE(position);
REQUIRE(position->x == 1);
REQUIRE(position->y == 2);
a.remove<Position>();
REQUIRE(!position);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestDeleteEntityWithNoComponents") {
Entity a = em.create();
a.assign<Position>(1, 2);
Entity b = em.create();
b.destroy();
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdSet") {
Entity a = em.create();
Entity b = em.create();
Entity c = em.create();
set<Entity> entitySet;
REQUIRE(entitySet.insert(a).second);
REQUIRE(entitySet.insert(b).second);
REQUIRE(entitySet.insert(c).second);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdMap") {
Entity a = em.create();
Entity b = em.create();
Entity c = em.create();
map<Entity, int> entityMap;
REQUIRE(entityMap.insert(pair<Entity, int>(a, 1)).second);
REQUIRE(entityMap.insert(pair<Entity, int>(b, 2)).second);
REQUIRE(entityMap.insert(pair<Entity, int>(c, 3)).second);
REQUIRE(entityMap[a] == 1);
REQUIRE(entityMap[b] == 2);
REQUIRE(entityMap[c] == 3);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityComponentsFromTuple") {
Entity e = em.create();
e.assign<Position>(1, 2);
e.assign<Direction>(3, 4);
std::tuple<ComponentHandle<Position>, ComponentHandle<Direction>> components = e.components<Position, Direction>();
REQUIRE(std::get<0>(components)->x == 1);
REQUIRE(std::get<0>(components)->y == 2);
REQUIRE(std::get<1>(components)->x == 3);
REQUIRE(std::get<1>(components)->y == 4);
}
TEST_CASE("TestComponentDestructorCalledWhenManagerDestroyed") {
struct Freed {
explicit Freed(bool &yes) : yes(yes) {}
~Freed() { yes = true; }
bool &yes;
};
struct Test : Component<Test> {
explicit Test(bool &yes) : freed(yes) {}
Freed freed;
};
bool freed = false;
{
EntityX e;
auto test = e.entities.create();
test.assign<Test>(freed);
}
REQUIRE(freed == true);
}
TEST_CASE("TestComponentDestructorCalledWhenEntityDestroyed") {
struct Freed {
explicit Freed(bool &yes) : yes(yes) {}
~Freed() { yes = true; }
bool &yes;
};
struct Test : Component<Test> {
explicit Test(bool &yes) : freed(yes) {}
Freed freed;
};
bool freed = false;
EntityX e;
auto test = e.entities.create();
test.assign<Test>(freed);
REQUIRE(freed == false);
test.destroy();
REQUIRE(freed == true);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentsRemovedFromReusedEntities") {
Entity a = em.create();
Entity::Id aid = a.id();
a.assign<Position>(1, 2);
a.destroy();
Entity b = em.create();
Entity::Id bid = b.id();
REQUIRE(aid.index() == bid.index());
REQUIRE(!b.has_component<Position>());
b.assign<Position>(3, 4);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestConstComponentsNotInstantiatedTwice") {
Entity a = em.create();
a.assign<Position>(1, 2);
const Entity b = a;
REQUIRE(a.component<Position>().valid());
REQUIRE(b.component<const Position>().valid());
REQUIRE(b.component<const Position>()->x == 1);
REQUIRE(b.component<const Position>()->y == 2);
}

26
external/entityx-1.1.2/entityx/Event.cc vendored Normal file
View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#include "entityx/Event.h"
namespace entityx {
BaseEvent::Family BaseEvent::family_counter_ = 0;
BaseEvent::~BaseEvent() {
}
EventManager::EventManager() {
}
EventManager::~EventManager() {
}
} // namespace entityx

216
external/entityx-1.1.2/entityx/Event.h vendored Normal file
View File

@@ -0,0 +1,216 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#pragma once
#include <cstdint>
#include <cstddef>
#include <vector>
#include <list>
#include <unordered_map>
#include <memory>
#include <utility>
#include "entityx/config.h"
#include "entityx/3rdparty/simplesignal.h"
#include "entityx/help/NonCopyable.h"
namespace entityx {
/// Used internally by the EventManager.
class BaseEvent {
public:
typedef std::size_t Family;
virtual ~BaseEvent();
protected:
static Family family_counter_;
};
typedef Simple::Signal<void (const void*)> EventSignal;
typedef std::shared_ptr<EventSignal> EventSignalPtr;
typedef std::weak_ptr<EventSignal> EventSignalWeakPtr;
/**
* Event types should subclass from this.
*
* struct Explosion : public Event<Explosion> {
* Explosion(int damage) : damage(damage) {}
* int damage;
* };
*/
template <typename Derived>
class Event : public BaseEvent {
public:
/// Used internally for registration.
static Family family() {
static Family family = family_counter_++;
return family;
}
};
class BaseReceiver {
public:
virtual ~BaseReceiver() {
for (auto connection : connections_) {
auto &ptr = connection.second.first;
if (!ptr.expired()) {
ptr.lock()->disconnect(connection.second.second);
}
}
}
// Return number of signals connected to this receiver.
std::size_t connected_signals() const {
std::size_t size = 0;
for (auto connection : connections_) {
if (!connection.second.first.expired()) {
size++;
}
}
return size;
}
private:
friend class EventManager;
std::unordered_map<BaseEvent::Family, std::pair<EventSignalWeakPtr, std::size_t>> connections_;
};
template <typename Derived>
class Receiver : public BaseReceiver {
public:
virtual ~Receiver() {}
};
/**
* Handles event subscription and delivery.
*
* Subscriptions are automatically removed when receivers are destroyed..
*/
class EventManager : entityx::help::NonCopyable {
public:
EventManager();
virtual ~EventManager();
/**
* Subscribe an object to receive events of type E.
*
* Receivers must be subclasses of Receiver and must implement a receive() method accepting the given event type.
*
* eg.
*
* struct ExplosionReceiver : public Receiver<ExplosionReceiver> {
* void receive(const Explosion &explosion) {
* }
* };
*
* ExplosionReceiver receiver;
* em.subscribe<Explosion>(receiver);
*/
template <typename E, typename Receiver>
void subscribe(Receiver &receiver) {
void (Receiver::*receive)(const E &) = &Receiver::receive;
auto sig = signal_for(Event<E>::family());
auto wrapper = EventCallbackWrapper<E>(std::bind(receive, &receiver, std::placeholders::_1));
auto connection = sig->connect(wrapper);
BaseReceiver &base = receiver;
base.connections_.insert(std::make_pair(Event<E>::family(), std::make_pair(EventSignalWeakPtr(sig), connection)));
}
/**
* Unsubscribe an object in order to not receive events of type E anymore.
*
* Receivers must have subscribed for event E before unsubscribing from event E.
*
*/
template <typename E, typename Receiver>
void unsubscribe(Receiver &receiver) {
BaseReceiver &base = receiver;
//Assert that it has been subscribed before
assert(base.connections_.find(Event<E>::family()) != base.connections_.end());
auto pair = base.connections_[Event<E>::family()];
auto connection = pair.second;
auto &ptr = pair.first;
if (!ptr.expired()) {
ptr.lock()->disconnect(connection);
}
base.connections_.erase(Event<E>::family());
}
template <typename E>
void emit(const E &event) {
auto sig = signal_for(Event<E>::family());
sig->emit(&event);
}
/**
* Emit an already constructed event.
*/
template <typename E>
void emit(std::unique_ptr<E> event) {
auto sig = signal_for(Event<E>::family());
sig->emit(event.get());
}
/**
* Emit an event to receivers.
*
* This method constructs a new event object of type E with the provided arguments, then delivers it to all receivers.
*
* eg.
*
* std::shared_ptr<EventManager> em = new EventManager();
* em->emit<Explosion>(10);
*
*/
template <typename E, typename ... Args>
void emit(Args && ... args) {
// Using 'E event(std::forward...)' causes VS to fail with an internal error. Hack around it.
E event = E(std::forward<Args>(args) ...);
auto sig = signal_for(std::size_t(Event<E>::family()));
sig->emit(&event);
}
std::size_t connected_receivers() const {
std::size_t size = 0;
for (EventSignalPtr handler : handlers_) {
if (handler) size += handler->size();
}
return size;
}
private:
EventSignalPtr &signal_for(std::size_t id) {
if (id >= handlers_.size())
handlers_.resize(id + 1);
if (!handlers_[id])
handlers_[id] = std::make_shared<EventSignal>();
return handlers_[id];
}
// Functor used as an event signal callback that casts to E.
template <typename E>
struct EventCallbackWrapper {
EventCallbackWrapper(std::function<void(const E &)> callback) : callback(callback) {}
void operator()(const void *event) { callback(*(static_cast<const E*>(event))); }
std::function<void(const E &)> callback;
};
std::vector<EventSignalPtr> handlers_;
};
} // namespace entityx

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#define CATCH_CONFIG_MAIN
#include <string>
#include <vector>
#include "entityx/3rdparty/catch.hpp"
#include "entityx/Event.h"
using entityx::EventManager;
using entityx::Event;
using entityx::Receiver;
struct Explosion {
explicit Explosion(int damage) : damage(damage) {}
int damage;
};
struct Collision {
explicit Collision(int damage) : damage(damage) {}
int damage;
};
struct ExplosionSystem : public Receiver<ExplosionSystem> {
void receive(const Explosion &explosion) {
damage_received += explosion.damage;
}
void receive(const Collision &collision) {
damage_received += collision.damage;
}
int damage_received = 0;
};
TEST_CASE("TestEmitReceive") {
EventManager em;
ExplosionSystem explosion_system;
em.subscribe<Explosion>(explosion_system);
em.subscribe<Collision>(explosion_system);
REQUIRE(0 == explosion_system.damage_received);
em.emit<Explosion>(10);
REQUIRE(10 == explosion_system.damage_received);
em.emit<Collision>(10);
REQUIRE(20 == explosion_system.damage_received);
}
TEST_CASE("TestUntypedEmitReceive") {
EventManager em;
ExplosionSystem explosion_system;
em.subscribe<Explosion>(explosion_system);
REQUIRE(0 == explosion_system.damage_received);
Explosion explosion(10);
em.emit(explosion);
REQUIRE(10 == explosion_system.damage_received);
}
TEST_CASE("TestReceiverExpired") {
EventManager em;
{
ExplosionSystem explosion_system;
em.subscribe<Explosion>(explosion_system);
em.emit<Explosion>(10);
REQUIRE(10 == explosion_system.damage_received);
REQUIRE(1 == explosion_system.connected_signals());
REQUIRE(1 == em.connected_receivers());
}
REQUIRE(0 == em.connected_receivers());
}
TEST_CASE("TestSenderExpired") {
ExplosionSystem explosion_system;
{
EventManager em;
em.subscribe<Explosion>(explosion_system);
em.emit<Explosion>(10);
REQUIRE(10 == explosion_system.damage_received);
REQUIRE(1 == explosion_system.connected_signals());
REQUIRE(1 == em.connected_receivers());
}
REQUIRE(0 == explosion_system.connected_signals());
}
TEST_CASE("TestUnsubscription") {
ExplosionSystem explosion_system;
{
EventManager em;
em.subscribe<Explosion>(explosion_system);
REQUIRE(explosion_system.damage_received == 0);
em.emit<Explosion>(1);
REQUIRE(explosion_system.damage_received == 1);
em.unsubscribe<Explosion>(explosion_system);
em.emit<Explosion>(1);
REQUIRE(explosion_system.damage_received == 1);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#include "entityx/System.h"
namespace entityx {
BaseSystem::Family BaseSystem::family_counter_;
BaseSystem::~BaseSystem() {
}
void SystemManager::update_all(TimeDelta dt) {
assert(initialized_ && "SystemManager::configure() not called");
for (auto &pair : systems_) {
pair.second->update(entity_manager_, event_manager_, dt);
}
}
void SystemManager::configure() {
for (auto &pair : systems_) {
pair.second->configure(event_manager_);
}
initialized_ = true;
}
} // namespace entityx

172
external/entityx-1.1.2/entityx/System.h vendored Normal file
View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#pragma once
#include <cstdint>
#include <unordered_map>
#include <utility>
#include <cassert>
#include "entityx/config.h"
#include "entityx/Entity.h"
#include "entityx/Event.h"
#include "entityx/help/NonCopyable.h"
namespace entityx {
class SystemManager;
/**
* Base System class. Generally should not be directly used, instead see System<Derived>.
*/
class BaseSystem : entityx::help::NonCopyable {
public:
typedef size_t Family;
virtual ~BaseSystem();
/**
* Called once all Systems have been added to the SystemManager.
*
* Typically used to set up event handlers.
*/
virtual void configure(EventManager &events) {}
/**
* Apply System behavior.
*
* Called every game step.
*/
virtual void update(EntityManager &entities, EventManager &events, TimeDelta dt) = 0;
static Family family_counter_;
protected:
};
/**
* Use this class when implementing Systems.
*
* struct MovementSystem : public System<MovementSystem> {
* void update(EntityManager &entities, EventManager &events, TimeDelta dt) {
* // Do stuff to/with entities...
* }
* }
*/
template <typename Derived>
class System : public BaseSystem {
public:
virtual ~System() {}
private:
friend class SystemManager;
static Family family() {
static Family family = family_counter_++;
return family;
}
};
class SystemManager : entityx::help::NonCopyable {
public:
SystemManager(EntityManager &entity_manager,
EventManager &event_manager) :
entity_manager_(entity_manager),
event_manager_(event_manager) {}
/**
* Add a System to the SystemManager.
*
* Must be called before Systems can be used.
*
* eg.
* std::shared_ptr<MovementSystem> movement = entityx::make_shared<MovementSystem>();
* system.add(movement);
*/
template <typename S>
void add(std::shared_ptr<S> system) {
systems_.insert(std::make_pair(S::family(), system));
}
/**
* Add a System to the SystemManager.
*
* Must be called before Systems can be used.
*
* eg.
* auto movement = system.add<MovementSystem>();
*/
template <typename S, typename ... Args>
std::shared_ptr<S> add(Args && ... args) {
std::shared_ptr<S> s(new S(std::forward<Args>(args) ...));
add(s);
return s;
}
/**
* Retrieve the registered System instance, if any.
*
* std::shared_ptr<CollisionSystem> collisions = systems.system<CollisionSystem>();
*
* @return System instance or empty shared_std::shared_ptr<S>.
*/
template <typename S>
std::shared_ptr<S> system() {
auto it = systems_.find(S::family());
assert(it != systems_.end());
return it == systems_.end()
? std::shared_ptr<S>()
: std::shared_ptr<S>(std::static_pointer_cast<S>(it->second));
}
/**
* Call the System::update() method for a registered system.
*/
template <typename S>
void update(TimeDelta dt) {
assert(initialized_ && "SystemManager::configure() not called");
std::shared_ptr<S> s = system<S>();
s->update(entity_manager_, event_manager_, dt);
}
/**
* Call System::update() on all registered systems.
*
* The order which the registered systems are updated is arbitrary but consistent,
* meaning the order which they will be updated cannot be specified, but that order
* will stay the same as long no systems are added or removed.
*
* If the order in which systems update is important, use SystemManager::update()
* to manually specify the update order. EntityX does not yet support a way of
* specifying priority for update_all().
*/
void update_all(TimeDelta dt);
/**
* Configure the system. Call after adding all Systems.
*
* This is typically used to set up event handlers.
*/
void configure();
private:
bool initialized_ = false;
EntityManager &entity_manager_;
EventManager &event_manager_;
std::unordered_map<BaseSystem::Family, std::shared_ptr<BaseSystem>> systems_;
};
} // namespace entityx

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#define CATCH_CONFIG_MAIN
#include <string>
#include <vector>
#include "entityx/3rdparty/catch.hpp"
#include "entityx/System.h"
#include "entityx/quick.h"
// using namespace std;
using namespace entityx;
using std::string;
struct Position : Component<Position> {
explicit Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
float x, y;
};
struct Direction : Component<Direction> {
explicit Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
float x, y;
};
struct Counter : Component<Counter> {
explicit Counter(int counter = 0) : counter(counter) {}
int counter;
};
class MovementSystem : public System<MovementSystem> {
public:
explicit MovementSystem(string label = "") : label(label) {}
void update(EntityManager &es, EventManager &events, TimeDelta) override {
EntityManager::View entities =
es.entities_with_components<Position, Direction>();
ComponentHandle<Position> position;
ComponentHandle<Direction> direction;
for (auto entity : entities) {
entity.unpack<Position, Direction>(position, direction);
position->x += direction->x;
position->y += direction->y;
}
}
string label;
};
class CounterSystem : public System<CounterSystem> {
public:
void update(EntityManager &es, EventManager &events, TimeDelta) override {
EntityManager::View entities =
es.entities_with_components<Counter>();
Counter::Handle counter;
for (auto entity : entities) {
entity.unpack<Counter>(counter);
counter->counter++;
}
}
};
class EntitiesFixture : public EntityX {
public:
std::vector<Entity> created_entities;
EntitiesFixture() {
for (int i = 0; i < 150; ++i) {
Entity e = entities.create();
created_entities.push_back(e);
if (i % 2 == 0) e.assign<Position>(1, 2);
if (i % 3 == 0) e.assign<Direction>(1, 1);
e.assign<Counter>(0);
}
}
};
TEST_CASE_METHOD(EntitiesFixture, "TestConstructSystemWithArgs") {
systems.add<MovementSystem>("movement");
systems.configure();
REQUIRE("movement" == systems.system<MovementSystem>()->label);
}
TEST_CASE_METHOD(EntitiesFixture, "TestApplySystem") {
systems.add<MovementSystem>();
systems.configure();
systems.update<MovementSystem>(0.0);
ComponentHandle<Position> position;
ComponentHandle<Direction> direction;
for (auto entity : created_entities) {
entity.unpack<Position, Direction>(position, direction);
if (position && direction) {
REQUIRE(2.0 == Approx(position->x));
REQUIRE(3.0 == Approx(position->y));
} else if (position) {
REQUIRE(1.0 == Approx(position->x));
REQUIRE(2.0 == Approx(position->y));
}
}
}
TEST_CASE_METHOD(EntitiesFixture, "TestApplyAllSystems") {
systems.add<MovementSystem>();
systems.add<CounterSystem>();
systems.configure();
systems.update_all(0.0);
Position::Handle position;
Direction::Handle direction;
Counter::Handle counter;
for (auto entity : created_entities) {
entity.unpack<Position, Direction, Counter>(position, direction, counter);
if (position && direction) {
REQUIRE(2.0 == Approx(position->x));
REQUIRE(3.0 == Approx(position->y));
} else if (position) {
REQUIRE(1.0 == Approx(position->x));
REQUIRE(2.0 == Approx(position->y));
}
REQUIRE(1 == counter->counter);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
#include <cstddef>
namespace entityx {
static const size_t MAX_COMPONENTS = @ENTITYX_MAX_COMPONENTS@;
typedef @ENTITYX_DT_TYPE@ TimeDelta;
} // namespace entityx

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2013 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#pragma once
#include "entityx/System.h"
#include "entityx/Event.h"
#include "entityx/Entity.h"
namespace entityx {
namespace deps {
/**
* An entityx::System for declaring component dependencies.
*
* eg. To declare that a `Physics` component must always be paired with `Position`
* and `Direction` components:
*
* system_manager->add<Dependency<Physics, Position, Direction>>();
*/
template <typename C, typename ... Deps>
class Dependency : public System<Dependency<C, Deps...>>, public Receiver<Dependency<C, Deps...>> {
public:
void receive(const ComponentAddedEvent<C> &event) {
assign<Deps...>(event.entity);
}
virtual void configure(EventManager &events) override {
events.subscribe<ComponentAddedEvent<C>>(*this);
}
virtual void update(EntityManager &entities, EventManager &events, TimeDelta dt) override {}
private:
template <typename D>
void assign(Entity entity) {
if (!entity.component<D>()) entity.assign<D>();
}
template <typename D, typename D1, typename ... Ds>
void assign(Entity entity) {
assign<D>(entity);
assign<D1, Ds...>(entity);
}
};
} // namespace deps
} // namespace entityx

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2013 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#define CATCH_CONFIG_MAIN
#include "entityx/3rdparty/catch.hpp"
#include "entityx/deps/Dependencies.h"
#include "entityx/quick.h"
namespace deps = entityx::deps;
struct A : public entityx::Component<A> {};
struct B : public entityx::Component<B> {
explicit B(bool b = false) : b(b) {}
bool b;
};
struct C : public entityx::Component<C> {};
TEST_CASE_METHOD(entityx::EntityX, "TestSingleDependency") {
systems.add<deps::Dependency<A, B>>();
systems.configure();
entityx::Entity e = entities.create();
REQUIRE(!static_cast<bool>(e.component<A>()));
REQUIRE(!static_cast<bool>(e.component<B>()));
e.assign<A>();
REQUIRE(static_cast<bool>(e.component<A>()));
REQUIRE(static_cast<bool>(e.component<B>()));
}
TEST_CASE_METHOD(entityx::EntityX, "TestMultipleDependencies") {
systems.add<deps::Dependency<A, B, C>>();
systems.configure();
entityx::Entity e = entities.create();
REQUIRE(!static_cast<bool>(e.component<A>()));
REQUIRE(!static_cast<bool>(e.component<B>()));
REQUIRE(!static_cast<bool>(e.component<C>()));
e.assign<A>();
REQUIRE(static_cast<bool>(e.component<A>()));
REQUIRE(static_cast<bool>(e.component<B>()));
REQUIRE(static_cast<bool>(e.component<C>()));
}
TEST_CASE_METHOD(entityx::EntityX, "TestDependencyDoesNotRecreateComponent") {
systems.add<deps::Dependency<A, B>>();
systems.configure();
entityx::Entity e = entities.create();
e.assign<B>(true);
REQUIRE(e.component<B>()->b);
e.assign<A>();
REQUIRE(e.component<B>()->b);
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include "entityx/config.h"
#include "entityx/Event.h"
#include "entityx/Entity.h"
#include "entityx/System.h"
#include "entityx/quick.h"

View File

@@ -0,0 +1,20 @@
// Inspired heavily by boost::noncopyable
#pragma once
namespace entityx {
namespace help {
class NonCopyable {
protected:
NonCopyable() = default;
~NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator = (const NonCopyable &) = delete;
};
} // namespace help
} // namespace entityx

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#include "entityx/help/Pool.h"
namespace entityx {
BasePool::~BasePool() {
for (char *ptr : blocks_) {
delete[] ptr;
}
}
} // namespace entityx

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#pragma once
#include <cstddef>
#include <cassert>
#include <vector>
namespace entityx {
/**
* Provides a resizable, semi-contiguous pool of memory for constructing
* objects in. Pointers into the pool will be invalided only when the pool is
* destroyed.
*
* The semi-contiguous nature aims to provide cache-friendly iteration.
*
* Lookups are O(1).
* Appends are amortized O(1).
*/
class BasePool {
public:
explicit BasePool(std::size_t element_size, std::size_t chunk_size = 8192)
: element_size_(element_size), chunk_size_(chunk_size), capacity_(0) {}
virtual ~BasePool();
std::size_t size() const { return size_; }
std::size_t capacity() const { return capacity_; }
std::size_t chunks() const { return blocks_.size(); }
/// Ensure at least n elements will fit in the pool.
inline void expand(std::size_t n) {
if (n >= size_) {
if (n >= capacity_) reserve(n);
size_ = n;
}
}
inline void reserve(std::size_t n) {
while (capacity_ < n) {
char *chunk = new char[element_size_ * chunk_size_];
blocks_.push_back(chunk);
capacity_ += chunk_size_;
}
}
inline void *get(std::size_t n) {
assert(n < size_);
return blocks_[n / chunk_size_] + (n % chunk_size_) * element_size_;
}
inline const void *get(std::size_t n) const {
assert(n < size_);
return blocks_[n / chunk_size_] + (n % chunk_size_) * element_size_;
}
virtual void destroy(std::size_t n) = 0;
protected:
std::vector<char *> blocks_;
std::size_t element_size_;
std::size_t chunk_size_;
std::size_t size_ = 0;
std::size_t capacity_;
};
/**
* Implementation of BasePool that provides type-"safe" deconstruction of
* elements in the pool.
*/
template <typename T, std::size_t ChunkSize = 8192>
class Pool : public BasePool {
public:
Pool() : BasePool(sizeof(T), ChunkSize) {}
virtual ~Pool() {
// Component destructors *must* be called by owner.
}
virtual void destroy(std::size_t n) override {
assert(n < size_);
T *ptr = static_cast<T*>(get(n));
ptr->~T();
}
};
} // namespace entityx

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#define CATCH_CONFIG_MAIN
#include <vector>
#include "entityx/3rdparty/catch.hpp"
#include "entityx/help/Pool.h"
struct Position {
explicit Position(int *ptr = nullptr) : ptr(ptr) {
if (ptr) (*ptr)++;
}
~Position() {
if (ptr) (*ptr)++;
}
float x, y;
int *ptr;
};
TEST_CASE("TestPoolReserve") {
entityx::Pool<Position, 8> pool;
REQUIRE(0 == pool.capacity());
REQUIRE(0 == pool.chunks());
pool.reserve(8);
REQUIRE(0 == pool.size());
REQUIRE(8 == pool.capacity());
REQUIRE(1 == pool.chunks());
pool.reserve(16);
REQUIRE(0 == pool.size());
REQUIRE(16 == pool.capacity());
REQUIRE(2 == pool.chunks());
}
TEST_CASE("TestPoolPointers") {
entityx::Pool<Position, 8> pool;
std::vector<char*> ptrs;
for (int i = 0; i < 4; i++) {
pool.expand(i * 8 + 8);
// NOTE: This is an attempt to ensure non-contiguous allocations from
// arena allocators.
ptrs.push_back(new char[8 * sizeof(Position)]);
}
char *p0 = static_cast<char*>(pool.get(0));
char *p7 = static_cast<char*>(pool.get(7));
char *p8 = static_cast<char*>(pool.get(8));
char *p16 = static_cast<char*>(pool.get(16));
char *p24 = static_cast<char*>(pool.get(24));
void *expected_p7 = p0 + 7 * sizeof(Position);
REQUIRE(expected_p7 == static_cast<void*>(p7));
void *extrapolated_p8 = p0 + 8 * sizeof(Position);
REQUIRE(extrapolated_p8 != static_cast<void*>(p8));
void *extrapolated_p16 = p8 + 8 * sizeof(Position);
REQUIRE(extrapolated_p16 != static_cast<void*>(p16));
void *extrapolated_p24 = p16 + 8 * sizeof(Position);
REQUIRE(extrapolated_p24 != static_cast<void*>(p24));
}
TEST_CASE("TestDeconstruct") {
entityx::Pool<Position, 8> pool;
pool.expand(8);
void *p0 = pool.get(0);
int counter = 0;
new(p0) Position(&counter);
REQUIRE(1 == counter);
pool.destroy(0);
REQUIRE(2 == counter);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2013 Antony Woods <antony@teamwoods.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Antony Woods <antony@teamwoods.org>
*/
#include "entityx/help/Timer.h"
namespace entityx {
namespace help {
Timer::Timer() {
_start = std::chrono::system_clock::now();
}
Timer::~Timer() {
}
void Timer::restart() {
_start = std::chrono::system_clock::now();
}
double Timer::elapsed() {
return std::chrono::duration<double>(std::chrono::system_clock::now() - _start).count();
}
} // namespace help
} // namespace entityx

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2013 Antony Woods <antony@teamwoods.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Antony Woods <antony@teamwoods.org>
*/
#pragma once
#include <chrono>
namespace entityx {
namespace help {
class Timer {
public:
Timer();
~Timer();
void restart();
double elapsed();
private:
std::chrono::time_point<std::chrono::system_clock> _start;
};
} // namespace help
} // namespace entityx

33
external/entityx-1.1.2/entityx/quick.h vendored Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2014 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#pragma once
#include "entityx/Entity.h"
#include "entityx/Event.h"
#include "entityx/System.h"
#include "entityx/config.h"
namespace entityx {
/**
* A convenience class for instantiating an EventManager, EntityManager and
* SystemManager.
*/
class EntityX {
public:
EntityX() : entities(events), systems(entities, events) {}
EventManager events;
EntityManager entities;
SystemManager systems;
};
} // namespace entityx

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#pragma once
#include <unordered_set>
#include <string>
#include "entityx/Entity.h"
namespace entityx {
namespace tags {
/**
* Allow entities to be tagged with strings.
*
* entity.assign<TagsComponent>("tag1", "tag2");
*
* ComponentPtr<TagsComponent> tags;
* for (Entity entity : entity_manager.entities_with_components(tags))
*/
class TagsComponent : public Component<TagsComponent> {
public:
/**
* Construct a new TagsComponent with the given tags.
*
* eg. TagsComponent tags("a", "b", "c");
*/
template <typename ... Args>
TagsComponent(const std::string &tag, const Args & ... tags) {
set_tags(tag, tags ...);
}
std::unordered_set<std::string> tags;
private:
template <typename ... Args>
void set_tags(const std::string &tag1, const std::string &tag2, const Args & ... tags) {
this->tags.insert(tag1);
set_tags(tag2, tags ...);
}
void set_tags(const std::string &tag) {
tags.insert(tag);
}
};
} // namespace tags
} // namespace entityx

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#define CATCH_CONFIG_MAIN
#include <string>
#include "entityx/3rdparty/catch.hpp"
#include "entityx/tags/TagsComponent.h"
using namespace std;
using namespace entityx;
using namespace entityx::tags;
struct Position : public Component<Position> {};
template <typename T>
int size(const T &t) {
int n = 0;
for (auto i : t) {
++n;
(void)i; // Unused on purpose, suppress warning
}
return n;
}
TEST_CASE("TestVariadicConstruction", "TagsComponentTest") {
auto tags = TagsComponent("player", "indestructible");
unordered_set<string> expected;
expected.insert("player");
expected.insert("indestructible");
REQUIRE(expected == tags.tags);
}

Binary file not shown.

View File

@@ -0,0 +1,429 @@
/**
* This is an example of using EntityX.
*
* It is an SFML2 application that spawns 100 random circles on a 2D plane
* moving in random directions. If two circles collide they will explode and
* emit particles.
*
* This illustrates a bunch of EC/EntityX concepts:
*
* - Separation of data via components.
* - Separation of logic via systems.
* - Use of events (colliding bodies trigger a CollisionEvent).
*
* Compile with:
*
* c++ -I.. -O3 -std=c++11 -Wall -lsfml-system -lsfml-window -lsfml-graphics -lentityx example.cc -o example
*/
#include <cmath>
#include <unordered_set>
#include <sstream>
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
#include <iostream>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <entityx/entityx.h>
using std::cerr;
using std::cout;
using std::endl;
namespace ex = entityx;
namespace std {
template <>
struct hash<ex::Entity> {
std::size_t operator()(const ex::Entity& k) const { return k.id().id(); }
};
}
float r(int a, float b = 0) {
return static_cast<float>(std::rand() % (a * 1000) + b * 1000) / 1000.0;
}
struct Body {
Body(const sf::Vector2f &position, const sf::Vector2f &direction, float rotationd = 0.0)
: position(position), direction(direction), rotationd(rotationd) {}
sf::Vector2f position;
sf::Vector2f direction;
float rotation = 0.0, rotationd;
};
struct Renderable {
explicit Renderable(std::unique_ptr<sf::Shape> shape) : shape(std::move(shape)) {}
std::unique_ptr<sf::Shape> shape;
};
struct Particle {
explicit Particle(sf::Color colour, float radius, float duration)
: colour(colour), radius(radius), alpha(colour.a), d(colour.a / duration) {}
sf::Color colour;
float radius, alpha, d;
};
struct Collideable {
explicit Collideable(float radius) : radius(radius) {}
float radius;
};
// Emitted when two entities collide.
struct CollisionEvent {
CollisionEvent(ex::Entity left, ex::Entity right) : left(left), right(right) {}
ex::Entity left, right;
};
class SpawnSystem : public ex::System<SpawnSystem> {
public:
explicit SpawnSystem(sf::RenderTarget &target, int count) : size(target.getSize()), count(count) {}
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
int c = 0;
ex::ComponentHandle<Collideable> collideable;
for (ex::Entity entity : es.entities_with_components(collideable)) c++;
for (int i = 0; i < count - c; i++) {
ex::Entity entity = es.create();
// Mark as collideable (explosion particles will not be collideable).
collideable = entity.assign<Collideable>(r(10, 5));
// "Physical" attributes.
entity.assign<Body>(
sf::Vector2f(r(size.x), r(size.y)),
sf::Vector2f(r(100, -50), r(100, -50)));
// Shape to apply to entity.
std::unique_ptr<sf::Shape> shape(new sf::CircleShape(collideable->radius));
shape->setFillColor(sf::Color(r(128, 127), r(128, 127), r(128, 127)));
shape->setOrigin(collideable->radius, collideable->radius);
entity.assign<Renderable>(std::move(shape));
}
}
private:
sf::Vector2u size;
int count;
};
// Updates a body's position and rotation.
struct BodySystem : public ex::System<BodySystem> {
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
ex::ComponentHandle<Body> body;
for (ex::Entity entity : es.entities_with_components(body)) {
body->position += body->direction * static_cast<float>(dt);
body->rotation += body->rotationd * dt;
}
};
};
// Bounce bodies off the edge of the screen.
class BounceSystem : public ex::System<BounceSystem> {
public:
explicit BounceSystem(sf::RenderTarget &target) : size(target.getSize()) {}
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
ex::ComponentHandle<Body> body;
for (ex::Entity entity : es.entities_with_components(body)) {
if (body->position.x + body->direction.x < 0 ||
body->position.x + body->direction.x >= size.x)
body->direction.x = -body->direction.x;
if (body->position.y + body->direction.y < 0 ||
body->position.y + body->direction.y >= size.y)
body->direction.y = -body->direction.y;
}
}
private:
sf::Vector2u size;
};
// Determines if two Collideable bodies have collided. If they have it emits a
// CollisionEvent. This is used by ExplosionSystem to create explosion
// particles, but it could be used by a SoundSystem to play an explosion
// sound, etc..
//
// Uses a fairly rudimentary 2D partition system, but performs reasonably well.
class CollisionSystem : public ex::System<CollisionSystem> {
static const int PARTITIONS = 200;
struct Candidate {
sf::Vector2f position;
float radius;
ex::Entity entity;
};
public:
explicit CollisionSystem(sf::RenderTarget &target) : size(target.getSize()) {
size.x = size.x / PARTITIONS + 1;
size.y = size.y / PARTITIONS + 1;
}
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
reset();
collect(es);
collide(events);
};
private:
std::vector<std::vector<Candidate>> grid;
sf::Vector2u size;
void reset() {
grid.clear();
grid.resize(size.x * size.y);
}
void collect(ex::EntityManager &entities) {
ex::ComponentHandle<Body> body;
ex::ComponentHandle<Collideable> collideable;
for (ex::Entity entity : entities.entities_with_components(body, collideable)) {
unsigned int
left = static_cast<int>(body->position.x - collideable->radius) / PARTITIONS,
top = static_cast<int>(body->position.y - collideable->radius) / PARTITIONS,
right = static_cast<int>(body->position.x + collideable->radius) / PARTITIONS,
bottom = static_cast<int>(body->position.y + collideable->radius) / PARTITIONS;
Candidate candidate {body->position, collideable->radius, entity};
unsigned int slots[4] = {
left + top * size.x,
right + top * size.x,
left + bottom * size.x,
right + bottom * size.x,
};
grid[slots[0]].push_back(candidate);
if (slots[0] != slots[1]) grid[slots[1]].push_back(candidate);
if (slots[1] != slots[2]) grid[slots[2]].push_back(candidate);
if (slots[2] != slots[3]) grid[slots[3]].push_back(candidate);
}
}
void collide(ex::EventManager &events) {
for (const std::vector<Candidate> &candidates : grid) {
for (const Candidate &left : candidates) {
for (const Candidate &right : candidates) {
if (left.entity == right.entity) continue;
if (collided(left, right))
events.emit<CollisionEvent>(left.entity, right.entity);
}
}
}
}
float length(const sf::Vector2f &v) {
return std::sqrt(v.x * v.x + v.y * v.y);
}
bool collided(const Candidate &left, const Candidate &right) {
return length(left.position - right.position) < left.radius + right.radius;
}
};
class ParticleSystem : public ex::System<ParticleSystem> {
public:
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
ex::ComponentHandle<Particle> particle;
for (ex::Entity entity : es.entities_with_components(particle)) {
particle->alpha -= particle->d * dt;
if (particle->alpha <= 0) {
entity.destroy();
} else {
particle->colour.a = particle->alpha;
}
}
}
};
class ParticleRenderSystem : public ex::System<ParticleRenderSystem> {
public:
explicit ParticleRenderSystem(sf::RenderTarget &target) : target(target) {}
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
sf::VertexArray vertices(sf::Quads);
ex::ComponentHandle<Particle> particle;
ex::ComponentHandle<Body> body;
for (ex::Entity entity : es.entities_with_components(body, particle)) {
float r = particle->radius;
vertices.append(sf::Vertex(body->position + sf::Vector2f(-r, -r), particle->colour));
vertices.append(sf::Vertex(body->position + sf::Vector2f(r, -r), particle->colour));
vertices.append(sf::Vertex(body->position + sf::Vector2f(r, r), particle->colour));
vertices.append(sf::Vertex(body->position + sf::Vector2f(-r, r), particle->colour));
}
target.draw(vertices);
}
private:
sf::RenderTarget &target;
};
// For any two colliding bodies, destroys the bodies and emits a bunch of bodgy explosion particles.
class ExplosionSystem : public ex::System<ExplosionSystem>, public ex::Receiver<ExplosionSystem> {
public:
void configure(ex::EventManager &events) override {
events.subscribe<CollisionEvent>(*this);
}
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
for (ex::Entity entity : collided) {
emit_particles(es, entity);
entity.destroy();
}
collided.clear();
}
void emit_particles(ex::EntityManager &es, ex::Entity entity) {
ex::ComponentHandle<Body> body = entity.component<Body>();
ex::ComponentHandle<Renderable> renderable = entity.component<Renderable>();
ex::ComponentHandle<Collideable> collideable = entity.component<Collideable>();
sf::Color colour = renderable->shape->getFillColor();
colour.a = 200;
float area = (M_PI * collideable->radius * collideable->radius) / 3.0;
for (int i = 0; i < area; i++) {
ex::Entity particle = es.create();
float rotationd = r(720, 180);
if (std::rand() % 2 == 0) rotationd = -rotationd;
float offset = r(collideable->radius, 1);
float angle = r(360) * M_PI / 180.0;
particle.assign<Body>(
body->position + sf::Vector2f(offset * cos(angle), offset * sin(angle)),
body->direction + sf::Vector2f(offset * 2 * cos(angle), offset * 2 * sin(angle)),
rotationd);
float radius = r(3, 1);
particle.assign<Particle>(colour, radius, radius / 2);
}
}
void receive(const CollisionEvent &collision) {
// Events are immutable, so we can't destroy the entities here. We defer
// the work until the update loop.
collided.insert(collision.left);
collided.insert(collision.right);
}
private:
std::unordered_set<ex::Entity> collided;
};
// Render all Renderable entities and draw some informational text.
class RenderSystem :public ex::System<RenderSystem> {
public:
explicit RenderSystem(sf::RenderTarget &target, sf::Font &font) : target(target) {
text.setFont(font);
text.setPosition(sf::Vector2f(2, 2));
text.setCharacterSize(18);
text.setColor(sf::Color::White);
}
void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
ex::ComponentHandle<Body> body;
ex::ComponentHandle<Renderable> renderable;
for (ex::Entity entity : es.entities_with_components(body, renderable)) {
renderable->shape->setPosition(body->position);
renderable->shape->setRotation(body->rotation);
target.draw(*renderable->shape.get());
}
last_update += dt;
frame_count++;
if (last_update >= 0.5) {
std::ostringstream out;
const double fps = frame_count / last_update;
out << es.size() << " entities (" << static_cast<int>(fps) << " fps)";
text.setString(out.str());
last_update = 0.0;
frame_count = 0.0;
}
target.draw(text);
}
private:
double last_update = 0.0;
double frame_count = 0.0;
sf::RenderTarget &target;
sf::Text text;
};
class Application : public ex::EntityX {
public:
explicit Application(sf::RenderTarget &target, sf::Font &font) {
systems.add<SpawnSystem>(target, 500);
systems.add<BodySystem>();
systems.add<BounceSystem>(target);
systems.add<CollisionSystem>(target);
systems.add<ExplosionSystem>();
systems.add<ParticleSystem>();
systems.add<RenderSystem>(target, font);
systems.add<ParticleRenderSystem>(target);
systems.configure();
}
void update(ex::TimeDelta dt) {
systems.update<SpawnSystem>(dt);
systems.update<BodySystem>(dt);
systems.update<BounceSystem>(dt);
systems.update<CollisionSystem>(dt);
systems.update<ExplosionSystem>(dt);
systems.update<ParticleSystem>(dt);
systems.update<RenderSystem>(dt);
systems.update<ParticleRenderSystem>(dt);
}
};
int main() {
std::srand(std::time(nullptr));
sf::RenderWindow window(sf::VideoMode::getDesktopMode(), "EntityX Example", sf::Style::Fullscreen);
sf::Font font;
if (!font.loadFromFile("LiberationSans-Regular.ttf")) {
cerr << "error: failed to load LiberationSans-Regular.ttf" << endl;
return 1;
}
Application app(window, font);
sf::Clock clock;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
case sf::Event::KeyPressed:
window.close();
break;
default:
break;
}
}
window.clear();
sf::Time elapsed = clock.restart();
app.update(elapsed.asSeconds());
window.display();
}
}

View File

@@ -0,0 +1,5 @@
#!/bin/bash -e
cmake -DCMAKE_BUILD_TYPE=Debug -DENTITYX_BUILD_TESTING=1
make VERBOSE=1
make test || cat Testing/Temporary/LastTest.log