Coder Social home page Coder Social logo

ash's People

Contributors

hyakugei avatar lynxerzhang avatar neilmanuell avatar nielswijers avatar richardlord avatar zackpierce 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  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

ash's Issues

removed entity from game still exist in game node lists ?

dont know if it's bug but its pissing me off.

to reproduce:

create entity.
add component A to entity
add entity to game

create node A with component A
create node B with component B

create nodeRemoved handler on nodeList A. in handler add component B to entity
create nodeAdded handler on nodeList B.

remove entity from game


result

  1. nodeAdded handler in nodeList B is fireing after removed entity form game but it should not - entity is no longer part of game and none of nodeList should be updateted after its removed.
  2. entity is no loger in game, but still exist in some nodelists

NodeList's nodeAdded dispatched again and again.

The NodeList’s nodeAdded signal was dispatched when adding an component to Node’s entity

//in "addToGame"...
objs = game.getNodeList(ObjectNode);
objs.nodeAdded(addObjectToWorld);
//in "addObjectToWorld"...
function addObjectToWorld(node:ObjectNode)
{
var skin:SkinComponent = new SkinComponent();
//just add a SkinComponent to the Entity, but this will make the "nodeAdded" signal dispatched
// and "addObjectToWorld" was called again. and again....
node.entity.add(skin);
}

In Family.as, found:

internal function addIfMatch( entity : Entity ) : void{
if( !entities[entity] ){
...
nodes.add( node );
entities[entity] = node;
...
}
}

the NodeList.add is executed before store node in entities, which means the nodeAdded is dispatched before store node in entities. and the condition "!entities[entity]" will always be true...

I moved the codeline "entities[entity] = node;" before "nodes.add(node);", looks work fine. but i don't know whether it's will break Richardlord's design.

EngineStateMachine changing states during Engine update

Hi,

Shouldn't the engine stop updating the systems if inside a system's update function, a state change was requested?
Right now it doesn't and that triggers the update functions on wrong systems because the SystemList is updated on the spot.
Am I using the EngineStateMachine wrong?
Who should order a state change?

Thanks,
Irinel

System.priority can be Number

I think that priority type can be changed to Number. Not much things it can help but I see no reason for it to be int.
Ooh, it can help while putting new system between priority 1 and 2 :)

User input events queue

Hi!

The thing that bothers me is that while all the systems operate within main update loop, user input events such as clicks and key hits are happening out of it. This means that a click, for example, can happen between the updates. Or even if a user is fast enough, he can manage to hit and release the mouse multipe times, still between the updates. And if a user input handling system is checking inside it's update() if the button is pressed, such clicks will be simply lost.

clicks

It would be great to have some util that collects such events into a queue and then allows systems to handle them correctly.

Please start adding version tags

Ash is already stable, so version tags would help users track bug fixes and enhancements.

Also, this implementation is the reference for ports, so a version tag would help the ports declare compatibility levels.

ComponentMatchingFamily.componentAddedToEntity check added component

Hi Richard,
I have noticed that the componentAddedToEntity() function has no check is added component required by this family's NodeList as it made in componentRemovedFromEntity.

public function componentAddedToEntity( entity : Entity, componentClass : Class ) : void
{
    addIfMatch( entity );
}

public function componentRemovedFromEntity( entity : Entity, componentClass : Class ) : void
{
    if( components[componentClass] )
    {
        removeIfMatch( entity );
    }
}

Maybe you skipped this check consciously because it can cause some issues?
Or function componentAddedToEntity can look like:

public function componentAddedToEntity( entity : Entity, componentClass : Class ) : void
{
    if( components[componentClass] )
    {
        addIfMatch( entity );
    }
}

Clear Next/Previous when removing Entity from EntityList

I ran into an issue where I was Object Pooling Entity objects and when re-adding them to the game, a runtime error would occur.

I solved it by adding:

entity.next = null;
entity.previous = null;

at the end of

internal function remove( entity : Entity ) : void {} in EntityList

This fix seems to make sense since i can't think of a reason why an Entity removed from the list should still point to other entities in the list.

Add ability to get all componentes on an Entity

In my application when I dispose an entity I remove all it's components and return them to the object pool. To do so I needed to implement

public function getComponents():Dictionary
{
return components;
}

Not sure if there is a better way to do this.

Avoid Vector/Array push() method

As any method with variable parameter list length allocates an Array each time the method is called memory can be saved by instead of:

array.push(object);

using:

array[array.length] = object;

This will circumvent the problem.

Robotlegs Ash Extension fails with latest robotlegs

In Ash Extension,

new SwiftSuspendersEngine( context.injector) line fails because context.injector returns IInjector whereas SwiftSuspendersEngine waits for org.swiftsuspenders.Injector.

I solved by casting IInjector to Injector
context.injector.map( Engine ).toValue( new SwiftSuspendersEngine( context.injector as Injector) );

Thank you

Entity.clone() - possible error if component registered with BaseClass

line no 154:
copy.add( newComponent );
assumes that all components being cloned are referenced against their Class, not a BaseClass

ie:
copy.add( newComponent, ComponentBaseClass );

I don't think that this method is used within the framework, so any errors probably won't occur very often.

I shall write a test to prove this, then write a patch

SignalBase.removeAll()

Hi!

I think there is small issue in SignalBase.removeAll() method.

This is how removeAll method looks now

        public function removeAll() : void
        {
            while( head )
            {
                var node : ListenerNode = head;
                head = head.next;
                delete nodes[ node.listener ];
                listenerNodePool.dispose( node );
                node.previous = null;
                node.next = null;
            }
            ...

And this is ListenerNodePool.dispose( node : ListenerNode ) method

        internal function dispose( node : ListenerNode ):void
        {
            node.listener = null;
            node.once = false;
            node.next = null;
            node.previous = tail;
            tail = node;
        }

I think setting node.previous should be removed from removeAll() because it could break link between list elements inside ListenerNodePool. And it is not necessary to set node.next to null in removeAll(), it is already null.

Scripting solutions with ASH.

Hey, I've done so far around 6 games based purely on Ash and cant imagine to work anymore without it :)

But I've ran into some kind of wall that holds me from proceeding further - namely its scripting of complex events in game. Let me give you some examples:

Situation: collision system informs me that player hit a bullet.
Possible complications:

  1. player was hit by stun dart and he shold not move for X frames/time elapsed
  2. player was hit and lost all of his health, he should have disabled movement capabilities, play animation of dying, then when its done, screen should shake, and he would be moved to another tile, then animation of reconstructing him will play, and he will be able to move again.
  3. player controls will randomly change for x frames/time.
  4. any other "script" we can think of.

What im strongly against is making complex if/else statements based on switches as it will be more and more problematic to handle with time. Im also against using components as flags to disable/enable specific behaviours as that can get quite messy easily - not to mention number of systems that will be created can be immense.

Should we enable developers to extend Ash to use more sophisticated rules for adding entities to node lists?

The issue

Ash creates node lists based purely on whether an entity contains the components that are properties of the node class of the list. This is a very pure approach to an entity architecture. For most games it is all that's needed, but some more complex alternatives have been proposed or requested. These alternatives include

  • Excluded component - exclude an entity from a node list if the entity has a particular component.
  • Optional component - a component property in the node that is not required, but is set if present on the entity.
  • Extended component - use inheritance to make a component that extends another, and recognise that the extending component is of the type of the base class when that base type is required by a node.

Reasons against

Having built and shipped a complete game using Ash, I found no need for these suggestions. In many ways they contravene the essence of the entity architecture, where systems care only about the presence of the components they use, not caring about the presence or absence of other components, and where components are simple value objects.

In my experience, when these options are requested they are often the result of poor choices when specifying components and entities. This is not always the case, but it often is. If we include these options within Ash, are we encouraging developers to follow poor patterns.

Further, all three of these proposals can be circumvented -

  • When processing a node, use node.entity.has( ExcludeComponent ) and don't process the node if this is the case.
  • When processing a node, use node.entity.get( OptionalComponent ) and if the response is not null use the returned component when processing the node.
  • Rather than extending the base component, create a second component that has the new features that are not part of the first component. Add both types to the entity.

Finally, I would like to optimise the management of node-lists in the current families, which will likely mean using bitmap masks to much more rapidly ascertain whether an entity should be added to a particular node-list. This will likely not be possible with the advanced options for node-list management mentioned above.

Reasons for

There's one good reason in favour - developers are asking for this. If the requests do not result from poor choices, then the idea of pushing this more complex entity filtering out of the systems and into the node-list management is sound and reflects the idea that systems should simply process lists of matching nodes and not have to filter that list.

Possible solutions

If we do implement this, I would like to find a way to allow greater flexibility in the creation of node lists without encumbering the core implementation with the added complexity and processing of those possibilities. This requires a way to extend Ash to add these options without requiring developers to touch the core code.

The core of node-list management is the Family class (this should perhaps have been named NodeListManager). Some options are

  1. Add a configuration to the Game class to specify the Family class to use for all NodeLists in the game. Make Family itself an interface, and set the current implementation as the default.
  2. Allow nodes to specify the Family class to be used when managing their lists (a static property?) and, as above, make Family an interface and set the current implementation as the default.
  3. Create a Family class that uses small strategy classes to manage the node-lists and allow modification of those strategies.

Also read

This pull request from wrobel221

Experimenting with different data structures types

I've been creating some data structures like in Ash within Haxe ( i intend to put it up later), to support strictly typed Generic lists. I intend for Families to be able to support their own data structures (which can be strictly-typed) , unlike what we have now in Ash which requires type-coercsion per node iteration. I thought this wasn't possible, but somehow I managed to create Generic MixList classes (ie. singly/doubly-linked lists to whose data itself is the node, with built-in typed .next/.prev pointers) without having to rely on Macros, just Generics. ( I remembered trying to do this once in Haxe with generics, but couldn't get it to work...but now it does!). This differs a lot from most linked list implementations you have now in Haxe, which requires the Node<T> to contain the data T. Now, this iisn't ncesary and all you need now is to have your own custom node data class implement an interface like MyOwnDataNode implements IDLMixNode<MyOwnDataNode> with the required next/prev:MyOwnDataNode pointers, and create a list like "new DLList<MyOwnDataNode>()", and you're done! So, it's possible to use Haxe to help generate out strictlydata- typed linked-lists, or other data structure types like pools and such, without having to repeat oneself.

Albeit, I still dislike Haxe for several as3 lacks, namely it's for-loop which is restricted to purely numeric iterations. A while loop alternative can be dangerous if one forgets to add boiler-plate endloop code during continue or at the end of the iteration block. It also has quirks with adding extra code to non-empty/extended Flash instance constructors. So, I let Haxe be used purely for the framework itself and some common components, and leave the implementation elsewhere to the relavant target engines. (AS3/JS/etc.)

DLL review in NodeList/NodePool and Signals:

I noticed that your doubly-linked list implementations doesn't set .next and .prev pointers of a removed node to be null, because it's possible that the .next pointer is still required during an update loop to iterate dynamically to the next valid node instance to be processed. As a result, when it comes to pooling nodes, one would set the .prev pointer to a cacheTail pointer, and defer the .next collection as a process of joining up the nodes back to the main pool header only after the game has finished updating. This isn't too bad especially in cases where certain Families might want to do specific reset methods for those given node components, if pooling was assumed to be done in those circumstances.

Q1: Is it possible to do this after a Family has finished updating the list? From what I see, the only case of invalidation occurs during the update of each individual list, so, it's possible to do direct removal of other nodes from other families' lists so long as there's no iteration through those lists during the game update loop. Am I right to hold that assumption? It's possible to set the(lock) flag over each family itself, however, Systems have no access to Families directly, only to a node list implementation , thus, to simply rely on a the game's gameUpdating state is used for simplicity, albeit that's a conservative approach. Logically, I think it's possible to have the "lock" flag be stored in the nodeList itself, so each Family can check whether the nodeList's lock flag was activated, or not. It would be the responsibility of the Systems therefore to mark each dll data structure as lock vs unlocked, very much like how you handle your BaseSignal. (Which i also ported to Haxe btw so you now have Generic typed Signal1/2/3<T,U,V> combinations, and the listener list uses a DLMixList<ListenerNode> class ).

Since removed nodes have no reference to the Family that holds it, a signal must be used to notify of removal to the Family that handles the pool. Albeit, I find the signals approach a bit heavy-handed, since I can't foresee any other situations where a nodeRemoved/nodeAdded signal might prove useful, (Except possibly nodeRemoved can help in pooling externally like what we have now). Another possible way is to have each linked list have their own internal pool, and lock flag, so the linked list will be responsible for creating the node and adding it, with the addNode():T factory method returning the node so it can be edited from the outside.) Usage of such a data structure requires knowledge on the Systems programmer's part to manually lock the list, unlock it, and release it, something which may not be too ideal for end-users.

Q2: Are there any possible needs of a nodeRemoved/nodeAdded signal outside of what we have now?

SSL consideration:

If a singly-linked list implementation was used, immediate removal during an update loop (where any node can be removed from any position) won't work as it'll possibly cause problems with the .next pointer canceling off. Even saving out the .next beforehand using a var next:Node = n.next at the beginning of a for loop won't work in cases where n.next was killed halfway during the process of that iteration, such that next:Node shouldn't have had existed anymore. If a singly-linked list was used in this way, than each node MUST always be processed whether it's dead or not, so each node would therefore require a "dead" boolean flag to mark nodes to be ignored, and a local prev:Node variable during each iteration is recorded to link up any given prev node to the next node, ignoring the dead node in the middle, which returns back to an object pool.
This was what I had done in my previous framework, which I tightly coupled 3d entities to the Alternativa3D7 engine, which also adopted a singly linked list display-list structure, where i wanted to avoid the overhead of iterative removeChild() calls but simply bundled the whole process together. Killing an entity simply means setting the dead flag to true, but that would mean other processes had to check for that flag, which wasn't too ideal.

The advantage of singly linked list nodes is the ability to save memory with just 1 pointer per node. It also makes the process of unlinking nodes easy if one uses a "dead" flag per node approach, albeit that would mean each loop iteration per node must check for the "dead" flag, so all nodes must still be processed no matter what and can't be canceled off halfway. This is unlike a 0(1) doubly-linked list node removal has more "if" considerations per remove operation but has the benefit of shortening the list during the update loop itself.

VectorIndex consideration:

A vector index is a vector that holds a certain data-type of object containing a "public var index:int" value so one can record the index of that object within the vector. Removal is as simple as checking the "index:int" value of the object to be removed, and perform a pop/pop-back operation accordingly, without necessarily having to resize the Vector unless one so wishes. Such a Vector buffer can be of a fixed size as well. I find this data structure is very good for going through a list which is assumed to NOT change that list's each iteration (ie. no removal/re-ordering and such during the loop). Thus, it makes a good option for RenderNodes, since such a loop would not require removal/re-ordering during that process.

The dreaded hash:

Hashing is often done in desperate situations . The fact that a data structure must hash an entity to avoid repeated additions ((though i admit a Family bitmask would be another example). The fact that an entity must be hashed in order to reference a given node from a given data structure. It might be possible to have FamilyKey: family:Family, key:* singly-linked mix list within an Entity to avoid a hash lookup, so removing an Entity could be as single as asking each Family to removeEntityByKey(key:*), the key most likely being type-coereced to the given node item being used for direct (usually (0)1)) removal depending on the data structure type. Assuming no new systems/families are being added during the game itself (ie. they are all pre-registered beforehand), some stuff like the entity list and hash within the Game itself can also be removed.

Systems:

I find a doubly-linked mix list for Systems isn't too ideal, as quickly switching between differnet GameStates that adopt different System combinations in FSM manner, won't work. Possibly, a TraditionalLinkedList/TraditionalNode<T:System> approach should be used instead, or just multiple Vector><System>.,to allow seamless switching of states without having to modify (and re-sort) the current System list. Often, games tend to have transitions between levels, and ingame machinma cutscenes where certain systems no longer come into play, while some systems (like the RenderingSystem), is still being used.

NodeList.forEach

Hi Richard,
how about to add forEach() method to the NodeList?

public function forEach( callback : Function ) : void
{
    for( var node : Node = head; node; node = node.next )
    {
        callback( node );
    }
}

simple example of use in a System:

override public function addToEngine( engine:Engine ):void
{
    nodeList = engine.getNodeList( MyNode );
    nodeList.forEach( handleNodeAdded );
    nodeList.nodeAdded.add( handleNodeAdded );
    nodeList.nodeRemoved.add( handleNodeRemoved );
}

override public function removeFromEngine( engine:Engine ):void
{
    nodeList.nodeAdded.remove( handleNodeAdded );
    nodeList.nodeRemoved.remove( handleNodeRemoved );
    nodeList.forEach( handleNodeRemoved );
    nodeList = null;
}

Maybe somebody will like it to use in the update function.
Of course for() iteration will be a bit faster than forEach() but we can live this decision to a developer.

override public function update( time:Number ):void
{
    nodeList.forEach( processNode );
}

private function processNode( node:MyNode ):void
{

}

Sometimes we need to pass additional params like a "time" in the update func.
Here is the extended implementation:

public function forEach( callback : Function, ...params ) : void
{
    if ( head )
    {
        params.unshift( null );
        for( var node : Node = head; node; node = node.next )
        {
            params[0] = node;
            callback.apply( null, params );
        }
    }
}

Now update function can look like:

override public function update( time:Number ):void
{
    nodeList.forEach( processNode, time );

    // nodeList.forEach( processNode ); - will work fine too
}

private function processNode( node:MyNode, time:Number ):void
{

}

Also can be an implementation with checking a value returned by callback.apply( null, params ); and if this value == false break forEach loop :)

entity.asNode();

It would be nice if it was possible to get an entity as a particular type. So for example:

var e : Entity = creator.createPlayer();
var p : PlayerNode = e.asNode(PlayerNode);
var c : CollisionNode = e.asNode(CollisionNode);

This would save having to pull out each component individually from the entity before using it as you could pull it out as a node then use it as normal.

If the entity doesn't have all the components required to satisfy that node then it should return null.

It may be nice if the method returned * rather than Node so that continual casting is not required just like the get() method.

systems communication

Hi Richard, and thank you for the great framework, it is awesome!
But I have two question:

  1. what are the reasons to do not let systems to communicate to each other directly?
    In examples it is a little bit wired that CollisionSystem adds and removes game objects, it is better to let SpaceSheepAiSystem to decide to die or not on asteroid hit, but to make so CollisionSystem need a way to inform AI
  2. some of systems can handle things that do not depend on game loop, why not to remove update loop from game class and to make game loop a separate system and let other systems to add listener for tick signal of a GameLoopSystem?
    for example space sheep AI system may only handle signals from CollisionSystem and KeyboardInputSystem, and don't need constant updates

Removing entity that was removed before causes entity list change.

Hi!
Here is code that illustrates issue.

`package {
import flash.display.Sprite;

import ash.core.Engine;
import ash.core.Entity;

public class AshTest extends Sprite {

    public var engine:Engine = new Engine();

    public function AshTest() {

        var a:Entity = new Entity("a");
        var b:Entity = new Entity("b");
        var c:Entity = new Entity("c");
        var d:Entity = new Entity("d");

        engine.addEntity(a);
        engine.addEntity(b);
        engine.addEntity(c);
        engine.addEntity(d);

        print();    //a, b, c, d

        engine.removeEntity(b);
        engine.removeEntity(c);

        print();    //a, d  <-- ok

        engine.removeEntity(b);     //<- delete b again. problem here

        print();    //a, c, d <-- error

    }

    private function print():void {
        var result:Array = [];
        var entites:Vector.<Entity> = engine.entities;
        for (var i:int = 0; i < entites.length; i++) {
            result[result.length] = entites[i].name;
        }
        trace(result.join(", "));
    }

}

}`

It's self-evident that programmer should not remove entities that are already removed. But it would be better if engine will work correctly in this situation.

EntityList.removeAll()'s little bug

internal function removeAll() : void
{
while( entity )
{
var entity : Entity = entity;
head = head.next;
entity.previous = null;
entity.next = null;
}
tail = null;

}

right codes:
internal function removeAll() : void
{
while( head )
{
var entity : Entity = entity;
head = head.next;
entity.previous = null;
entity.next = null;
}
tail = null;
}

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.