Giter Site home page Giter Site logo

noodles's Introduction

Noodles

ECS (Entity-Component-System) Framework

如面条般顺滑(?)。

Demo code in Demo/demo.sln (vs2019).

How to use

  1. Include Noodles/implement.h.

  2. Define your Components and Systems.

    using namespace Noodles;
    struct Component1;
    struct Component2;
    struct GobalComponent;
    struct System1
    {
    
    	//Defines system's priority layout. It could be ignored. 
    	//Default Priority is TickPriority::Normal
    	TickPriority tick_layout();
    
    	//Defines system's priority. It could be ignored. 
    	//Default Priority is TickPriority::Normal
    	TickPriority tick_priority();
    
    	//Defines system's priority with specific system. It could be ignored.
    	//Use TypeLayout::create<X>() to create typelayout for specific system.
    	//Default Order is TickOrder Undefine.
    	// conflig_type_count is a size[3] which represent system conflig, gobal component conflig and component conflig
    	TickOrder tick_order(const TypeLayout& layout, const TypeInfo* conflig_type, size_t* conflig_type_count);
    
    	//Define operator() function with special Filter.
    	void operator()(Filter<const Component1>&, Context&);
    
    };
    
    // lambda or normal function are also an acceptable system.
  3. Define and set up ecs context

    ContextImplement imp;
    // Setting up minimum duration with each cycle. Default value is 16ms
    imp.set_duration(duration_ms{16});
    
    // Setting up reserve threads for other uses. Default value is 0;
    imp.set_thread_reserved(2);
  4. Create Components and Systems

    Entity entity = imp.create_entity();
    Component1& ref1 = imp.create_component<Component1>(entity, /*contructional parameter*/);
    Component2& ref2 = imp.create_component<Component2>(entity, /*contructional parameter*/);
    GobalComponent& ref2 = imp.create_gobal_component<Component2>(entity, /*contructional parameter*/);
    System1& sys1 = imp.create_system<System1>(/*contructional parameter*/);
    //Callable object
    imp.create_system([](Filter<Component1>& f){}, TickPriority::Normal, TickPriority::Normal);
  5. Have Fun!

    try{
    	imp.loop();
    }catch(const Error::SystemOrderConflig&)
    {
    	//Handle system order conflig
    }catch(const Error::SystemOrderRecursion&)
    {
    	//Handle system order recursion
    }

For Dynamic Link Library

  • in DLL:

     // Include interface
     #include "Noodles/interface.h"
     // Define Components or Systems
     
     // using namespace, optional
     using namespace Noodles;
     extern "C" {
     	void __declspec(dllexport) init(Context*);
     }
     void __declspec(dllexport) init(Context*)
     {
     	// Createing Systems and Components
     }
  • in EXE

     ContextImplement imp;
     /*
     Do some thing here
     */
     auto handle = LoadLibrary(...);
     auto init = (void(*)(PO::ECS::Context*))GetProcAddress(handle, "init");
     init(imp);
     imp.loop();
     //You must free library after imp.loop() to make sure that every data has been destructed.
     FreeLibrary(handle);

Special Filter

  • EntityFilter

    Access components form specific Entity only if it has those components.

     void s1::operator()(EntityFilter<const Component1, Component2>& f)
     {
     	Entity entity;
     	f(entity, [](const Component1&, Component2&){});
     }
  • Filter

    Access components form all Entity only if it has those components.

     void s1::operator()(Filter<const Component1, Component2>& f)
     {
     	for(auto ite : f)
     	{
     		auto& [comp1, comp2] = ite;
     		Entity entity = ite.entity();
     		...
     	}
     }
  • EventViewer

    Provide event to next frame for other systems to access.

     void s1::operator()(EventViewer<int>& f)
     {
     	f.push(1);
     	for(auto ite = f.begin(); ite != f.end(); ++ite)
     	{
     		const int& event = *ite;
     	}
     }
  • Context

    Access ecs context.

     void s1::operator()(PO::ECS::Context& f)
     {
         //Jump out of the loop
         f.exit();
    
         // insert asynchronous work
         f.insert_asynchronous_work([](Context& c){
             // continue to apply this work
             return true;
         });
     }
  • SystemFilter

    Access other system.

     void s1::operator()(SystemFilter<System2>& f, SystemFilter<const System2>& f2)
     {
     	// make sure that systems exist
     	if(f && f2)
     	{
     		System2* sys = f.operator->();
     		const System2* sys2 = f2.operator->();
     	}
     }
  • GobalFilter

    Access gobal component.

     void s1::operator()(GobalFilter<GobalComponent>& g1, GobalFilter<const GobalComponent2>& g2)
     {
     	// make sure that gobal components exist
     	if(g1 && g2)
     	{
     		GobalComponent* gc1 = g1.operator->();
     		const GobalComponent2* gc2 = g2.operator->();
     	}
     }

System Parallelized

Framework uses the type of special filter from system's operator() funtion to detect read-write-property of specific data type, and those read-write-property will be used to make a order graphic, which control the order of system calling and whether two system will be parallelized or not.

Filter Type Read-Write-Property
Filter<A>orEntityFilter<A> Write To ComponentA
Filter<const A>orEntityFilter<const A> Read From ComponentA
SystemWrapper<A> Write To SystemA
SystemWrapper<const A> Read From SystemA
GobalFilter<A> Write To GobalComponentA
GobalFilter<const A> Read From GobalComponentA
EntityViewer<A> None

Framework uses the following step to decide system order of two specific system.

  1. Compares system layout priority.

    System layout priority comes from function TickPriority s1::tick_layout(). TickPriority has following 5 default enum value:

    Enum Value
    HighHigh -120
    High -60
    Normal 0
    Low 60
    LowLow 120

    Or you can define your custom priority like static_cast<TickPriority>(int8_t{8}). But be attention that enum TickPriority is a int8_t type value, which mean that the range of TickPriority is [-128, 127].

    System with lower priority will be called Before system with higher priority. In the same time, two systems with difference priority will never be parallelized.

    If have the same priority, jump to step 2.

  2. Calculate system's read-write-property

    If system s1 needs to write to s2, like void s1::operator(SystemWrapper<s2>), means s1 may be called Before s2, while if system s1 needs to read from s2, like void s1::operator(const SystemWrapper<s2>), means s1 may be called After s2.

    If two systems need to write to each other, an exception of Error::SystemOrderConflig may be threw.

    In fact, if those situations occurr, framework will jume to step 4 to calculate user-define priority. If undefine, framework will use the reault from this step, else will use the result of user-define priority.

    If thos situations is not occurred, jump to step 3.

  3. Calculate read-write-property of same data type.

    Data type is sperated into following 3 independent channels, sorted by calculating order.

    System > GobalComponent > Component

    If s1 needs to write to DataTypeA, and s2 needs to read from DataTypeA, means s1 may be called Before s2.

    If s1 needs to write to DataTypeA, read from DataTypeB, and s2 needs to write to DataTypeB, read from DataTypeA, or both of s1 and s2 need to write to s3, an exception of Error::SystemOrderConflig may be threw.

    Just like step 2, if those situations occurr, framework will jume to step 4 to calculate user-define priority.

    But event channle has little different. If s1 needs to write to EventA and s2 needs to write to EventB, s1 and s2 will not be call at the same time.

    If thos situations is not occurred, those two system may be parallelized.

  4. User-define priority

    System priority just like system layout priority but it came from TickPriority s1::tick_priority(). If is the same, framework will call following two functions to get the order.

    auto t1 = TickOrder s1::tick_order(TypeInfo::create<s2>(), conflig_type.data(), conflig_type_length.data());
    auto t2 = TickOrder s2::tick_order(TypeInfo::create<s1>(),  conflig_type.data(), conflig_type_length.data());

    If t1 and t2 is TickOrder::Undefine, will not overwrite order.

    If t1 and t2 is conflig, an exception of Error::SystemOrderConflig will be threw.

    Else, overwrite order.

After system order is beening decide, an order graphic will be made. But if s1 before s2, s2 before s3 and s3 before s1, an exception of Error::SystemOrderRecursion will be threw.

With the help of order graphic, systems can be Parallelized.

More flexible mutual condition determination.

Like filter Filter<A, B, C> and Filter<A, B, D> in different systems. And there is no Entity with ComponentGroup like <A, B, C, D>. Those two systems can be parallelized.

PS: 英语不太好的我实在是尽力了。。。

noodles's People

Contributors

bleedingchips avatar

Watchers

 avatar

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.