Giter VIP home page Giter VIP logo

tiny-ecs's Introduction

tiny-ecs

NOTE

Although there have been almost no commits in several years, this project is not abandoned. tiny-ecs is, for most intents and purposes, finished, and no bugs have been brought to my attention in a while. New issues on GitHub will be addressed.

Build StatusLicense

Tiny-ecs is an Entity Component System for Lua that's simple, flexible, and useful. Because of Lua's tabular nature, Entity Component Systems are a natural choice for simulating large and complex systems. For more explanation on Entity Component Systems, here is some basic info.

Tiny-ecs also works well with object-oriented programming in Lua because Systems and Entities do not use metatables. This means you can subclass your Systems and Entities and use existing Lua class frameworks with tiny-ecs, no problem. For an example on how to use tiny-ecs with object-oriented Lua, take a look at the demo branch, specifically the systems and entities subdirectories.

Overview

Tiny-ecs has four important types: Worlds, Filters, Systems, and Entities. Entities, however, can be any Lua table, and Filters are just functions that take an Entity as a parameter.

Entities

Entities are simply Lua tables of data that gets processed by Systems. Entities should contain primarily data rather than code, as it is the System's job to do logic on data. Henceforth, a key-value pair in an Entity will be referred to as a Component.

Worlds

Worlds are the outermost containers in tiny-ecs that contain both Systems and Entities. In typical use, only one World is used at a time.

Systems

Systems in tiny-ecs describe how to update Entities. Systems select certain Entities using a Filter, and then only update those select Entities. Some Systems don't update Entities, and instead just act as function callbacks every update. Tiny-ecs provides functions for creating Systems easily, as well as creating Systems that can be used in an object oriented fashion.

Filters

Filters are used to select Entities. Filters can be any Lua function, but tiny-ecs provides some functions for generating common ones, like selecting only Entities that have all required components.

Example

local tiny = require("tiny")

local talkingSystem = tiny.processingSystem()
talkingSystem.filter = tiny.requireAll("name", "mass", "phrase")
function talkingSystem:process(e, dt)
    e.mass = e.mass + dt * 3
    print(("%s who weighs %d pounds, says %q."):format(e.name, e.mass, e.phrase))
end

local joe = {
    name = "Joe",
    phrase = "I'm a plumber.",
    mass = 150,
    hairColor = "brown"
}

local world = tiny.world(talkingSystem, joe)

for i = 1, 20 do
    world:update(1)
end

Use It

Copy paste tiny.lua into your source folder. For stability and consistent API, please use a tagged release or use LuaRocks.

Luarocks

Tiny-ecs is also on LuaRocks and can be installed with luarocks install tiny-ecs.

Demo

Check out the demo, a game originally written for Ludum Dare 32 with the theme 'An Unconventional Weapon'. The demo uses LÖVE, an amazing game framework for Lua.

Testing

Tiny-ecs uses busted for testing. Install and run busted from the command line to test.

Documentation

See API here. For the most up-to-date documentation, read the source code, or generate the HTML locally with LDoc. See the original forum thread here.

tiny-ecs's People

Contributors

bakpakin avatar firoxer avatar tst2005 avatar wqferr avatar yangruihan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tiny-ecs's Issues

Feathure request: support for empty components

Hi. Lib looks good, but, I think, empty components(flags) as strings in tables would be good for readability.
For example:

player = {
    position = Vector(100, 100),
    velocity = Vector(0, 0),
   'is_handles_input',
}

doesn't works for now, we need type is_handles_input = true or something like this, but it is looks not good, because it is not actually a boolean value.
I tried to understand how it works in source and change, but I failed D:

setSystemIndex before update - attempt to compare nil with number

When calling setSystemIndex on a world that hasn't been updated yet, this error is raised. Is this intended behaviour?

Here's a minimal example.

The error:

stack traceback:
        ./tiny-ecs/tiny.lua:834: in function 'tiny-ecs.setSystemIndex'
        main.lua:7: in main chunk
        [C]: in ?

main.lua:

local world = tiny.world()
local systemA = tiny.system()
local systemB = tiny.system()
world:addSystem(systemA)
world:addSystem(systemB)
world:setSystemIndex(systemA, 1)

README example error

...

function talkingSystem:process(e, dt)
    e.mass = e.mass + dt * 3
    print(("%s who weighs %d pounds, says %q."):format(e.name, e.mass, e.phrase)
end

...

print(("%s who weighs %d pounds, says %q."):format(e.name, e.mass, e.phrase) missing right parentheses.

Tiny-ecs somehow interfering with Love2D rendering.

Hey! Sorry for blowing up the issues page like this. This is likely something that is my fault but I honestly cannot figure it out. After you solved my problem in issue 11 I ran into another one. My render system is running but love.graphics.print is not working. When I do NOT pass the delta time into the update function (just the filter) the graphics call works. When I do pass the delta time in it doesn't. Even through the system is running as indicated by my print statements I threw in there to debug. Any suggestions? I re-opened the last issue because it contains relevant info.

#11

Thank you for your time! I have been playing with this all night trying to figure it out.

Entity’s properties caching

> t = require 'tiny'
> e = {name='Ivan'}
> s = t.processingSystem()
> function s:process(e) print(e.name) end
> s.filter = t.requireAll('name')
> w = t.world(e, s)
> w:update()
Ivan
> e.name = nil
> w:update()
nil

Is that expected behaviour? As a workaround I am currently re-adding changed entity to the world, but maybe there is a better (i. e. automatic) way?

Thank you.

System constructor and deconstructors

It would be nice if tiny had system constructors that are called immediately when a system is added to or removed from the world. It would also be nice if system.world was added immediately, not after first update.

local system = tiny.system()

function system:onAdd()
end

function system:onRemove()
end

function system:onAddEntity()
end

function system:onRemoveEntity()
end

A few feature requests / questions

Hi, thanks for making such a great library! I've been seeing if it's a good fit for my game, and found a few features I think would be useful to have. It may be that there's already ways to do some of these that I've missed, but I'd be interested in hearing your comments.

Systems that handle multiple entity collections

I have a few cases where a system needs to work on two distinct sets of entities. It would be great if something like this were possible:

testsystem.filter = {
    items = tiny.requireAll("ComponentA"),
    otherItems = tiny.requireAll("ComponentB")
}

and then inside the system, you'd have system.entities.items and system.entities.otherItems available. Of course this would really only work and make sense when using a regular tiny.system() and not a processingSystem(), so then it's up to the developer to implement their own update() method and iterate over entities etc.

Being able to easily fetch collections of entities

Maybe related to the thing above, but having an easy way to do something like this would be really useful:

someEnts = world.getEntities(tiny.requireAll("SomeComponent"))

I notice there's the world.entities collection, and you already have the tiny.filter() method, so this could probably be achieved already simply by looping over them and filtering, but I'm not sure how well this would perform with large entity collections -- but this feature would be more useful for things like debugging anyway probably. If you needed multiple collections inside of a system, then maintaining the separate lists like above would be better.

Thanks!

system:update gives entity table keyed by the entries, forcing use of (slow!) pairs()

The inverted table given to system update is really weird and I couldn't see any rationale for it.

pairs() can't be compiled by LuaJIT, so whenever you call it you incur a performance penalty by throwing things back to interpreter mode - I'm not normally too stingy about this (no big deal in low-frequency stuff), but for something that will happen as often as entity updates for the various systems a game has it's a bit of a red flag.

Can the table be inverted such that it will work with ipairs? What's with the values (true, true, true...)?

Docs on systems have a few issues

The docs say something like:

 function system:preProcess(entities, dt) -- Called before iteration.
 function system:process(entities, dt) -- Process each entity.
 function system:postProcess(entity, dt) -- Called after iteration.

Really should be:

 function system:preProcess(dt) -- Called before iteration.
 function system:process(entity, dt) -- Process each entity.
 function system:postProcess(dt) -- Called after iteration.

Missing document

There is no document about system.entities nor world.entities. Also, there should description about return value of filter function:

If the filter function returns false, the entity is not included in the system. Otherwise, yes.

`init.lua` does not work properly

The image below exemplifies the issue.

The require call in (1) shows the cloned directory name is not the issue.

issue-img

The require call in (2) fails not because it can't find the init.lua file, but due to an error inside it. The call to str.match with the file arguments is incorrect. ... contains two values: first the string used in require (which corresponds to the directory cloned directory, in this case), and the relative path of the init file.

A simple solution is to rewrite the init.lua file so as to use the first argument as it is.

image

I have already created a pull request and linked it to this issue.

Sorry

My bad, wrong repository :(

Add TMX files

Hi!

I'm studyng this project because it's the only one i can see that has
ecs + bump + sti

Can you please add the TMX files that renders the game levels?

And

Perhaps update to latest libs? bump and sti?

Thanks for your job and thanks for share it!
:)

removeSystem is missing from metatable

As the title says, you forgot to re-add removeSystem in your refactoring. I added it back in locally and it works fine.

worldMetaTable = {
    __index = {
        add = tiny.add,
        addEntity = tiny.addEntity,
        addSystem = tiny.addSystem,
        remove = tiny.remove,
        removeEntity = tiny.removeEntity,
        removeSystem = tiny.removeSystem, -- oops!
        update = tiny.update,
        clearEntities = tiny.clearEntities,
        clearSystems = tiny.clearSystems,
        getEntityCount = tiny.getEntityCount,
        getSystemCount = tiny.getSystemCount,
        getSystemIndex = tiny.getSystemIndex,
        setSystemIndex = tiny.setSystemIndex
    }
}

Strange tiny.requireAll

While tiny.requireAll works, it is hard to understand it. Why does it use loadstring?
Here is an equvalent function:

local function requireAll(...)
    local required = {...}
    return function (_, entity)
        for i=1,#required do
            if entity[required[i]]==nil then
                return false  -- false means "don't include"
            end
        end
        return true
    end
end

Delta time var passed in as function every other frame

Hello! I am playing around with Love2D and decided to use Tiny-ecs with my project. All was going ok until I tried to use the delta time var. I try to use it for my player movement but every other frame it is a function for some reason.

My code:

world = {}
world = tiny.world(inputSystem, renderSystem, movementSystem, entity, entity2 )

function love.update(_dt)
world:update(_dt)
end

function InputSystem:process(_e, _dt)
print(_dt)
end

-- This code results in --
function: 0x403c4c20
0.016999974999635
function: 0x403c4c20
0.01636593999865
function: 0x403c4c20
0.024445032999211
function: 0x403c4c20
0.0088829689993872
function: 0x403c4c20
0.018819653996616
function: 0x403c4c20
0.014247577004426
function: 0x403c4c20

So when I try to move my player multiplying by delta time I get an error every other frame because I am trying to multiply by a function. Am I doing something wrong? Thanks!

Actual Full Source: http://pastebin.com/zkQQfA0v

Include a filter function with a rejection list by default

It's pretty common (in my experience) to need a system to process all entities that match a given set of components unless it's got specific other components. i.e. I don't want my raycasting system to bother processing whatever the player is possessing, or it'll result in always hitting itself. I think it'd be nice to include something like this, so I wouldn't have to copy/paste/rewrite this function every time I start a new project.

Here's the function I've been using

-- Usage: system.filter = require_all_unless({"accept", "these"}, {"unless", "it", "has_these"})
local function require_all_unless(components, reject)
    local len = #components
    local rlen = #reject
    assert(type(components) == "table")
    assert(type(reject) == "table")
    return function(_, e)
        local c
        for i = 1, len do
            c = components[i]
            if type(c) == 'function' then
                if not c(_, e) then
                    return false
                end
            elseif e[c] == nil then
                return false
            end
        end
        -- same thing, but reversed.
        for i = 1, rlen do
            c = reject[i]
            if type(c) == 'function' then
                if c(_, e) then
                    return false
                end
            elseif e[c] ~= nil then
                return false
            end
        end
        return true
    end
end

add, change, then delete

refer to tiny.lua Line 643-747
I think the correct order should be: add, change, then delete because if I do this:

  • Add a entity
  • Remove it immediately without call to world:refresh

This will NOT trigger a system's onRemove callback, which does not make sense.
Also, I think it is better not to use filter in the "add entities" procedure, but to add entities into entitiesToChange even if they are newly added.
This requires changes to:

  • tiny_manageEntities, of course
  • tiny.addEntity

bad argument #2 to 'loadstring' when using with Playdate SDK

Hello! I'm trying to use tiny-ecs with the Playdate SDK. However every call to tiny.require* fails with:

tiny.lua:126: bad argument #⁠2 to 'loadstring' (number expected, got no value)

This works fine when using the non-playdate version of lua. I imagine that somehow load refers to something else in this environment. Doesn't seem to be a problem with tiny-ecs itself, but since I'm new to lua and I'm having trouble debugging this, I'm posting here with the hope that someone else had the same problem.

Maybe the maintainer of this library has an idea of what to check/debug. Feel free to close if this is obviously not related to tiny-ecs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.