nadako / ash-haxe Goto Github PK
View Code? Open in Web Editor NEWPort of Ash entity framework to Haxe
License: Other
Port of Ash entity framework to Haxe
License: Other
typedefs don't yield typesafe code. This essentially means each iterator "node" is a Dynamic object which is slow. I recommend using private class HasNext<T>
instead of typedef.
I've tried to compile Ash with "-dce full" option (flash target):
Argument count mismatch on ash.core::ComponentMatchingFamily()
I've added @:keep meta to ComponentMatchingFamily, but exceptions is still here:
TypeError: Error #1006: _getComponents is not a function.
at ash.core::ComponentMatchingFamily/init()
at ash.core::ComponentMatchingFamily()
I haven't learned the macro magic yet, but guess there is a way to add keep meta to generated code.
I'm pulling my hair out over this one. Has anyone experienced slow downs in ClassMap.keys() on CPP targets? I have a level with about 180 entities in it. When I reload the level, I remove all level-based entities and then recreate them. This process takes longer and longer each time. For example, the first time it takes ~56ms, and the 20th time it takes ~5000ms. The accelerating nature of the time is disconcerting.
I've tracked the issue to ClassMap.keys(), or more specifically its use of StringMap.keys(). It is called from ComponentMatchingFamily.addIfMatch(). The Haxe profiler confirms that on the 20th reload 93% of the time is taken up by calls to StringMap.keys(). It's not entirely consistent, as most calls take <1ms, and then suddenly there will be a spike. There are never more than 3 keys in the map. The profiler indicates the time in StringMap.keys is coming from its use of GC::new.
It sounds like garbage collection is kicking in periodically (and more frequently than 30 seconds), but I can't figure out why the time cost is accelerating. I've confirmed that I have 180 entities each time, and each entity has about 6 components. There is no slowdown on the Flash target. Does anyone have any thoughts on this, or what I can do to track it further?
Hello,
I just ran :
haxe test.hxml
And it prints the following :
/home/gogoprog/.haxelibs/hamcrest/2,0,1/org/hamcrest/collection/IsHashContaining.hx:36: characters 26-34 : Warning : Usage of this typedef is deprecated
test/ash/core/SystemTest.hx:218: characters 33-45 : Unknown identifier : sameInstance
test/ash/core/SystemTest.hx:224: characters 33-45 : Unknown identifier : sameInstance
test/ash/core/SystemTest.hx:169: characters 49-61 : Unknown identifier : sameInstance
test/ash/core/SystemTest.hx:196: characters 34-46 : Unknown identifier : sameInstance
test/ash/core/AshAndFamilyIntegrationTest.hx:48: characters 37-49 : Unknown identifier : sameInstance
...
I don't know if it this linked to the latest version.
By the way using the framework seems to be fine, just the tests are failing.
Shouldn't the haxelib version not include the asteroids example?
I was looking at the code for FixedTickProvider and it just outputs a signal every frame no matter what the time adjustment or frametimer says. I replaced the code from that file with the code from FrameTickProvider and changed the dispatch function to
private function dispatchTick(event:Event):Void
{
var temp:Float = previousTime;
var frameTime:Float = ( Lib.getTimer() - temp ) / 1000;
while (frameTime > maximumFrameTime) {
previousTime = Lib.getTimer();
signal.dispatch(frameTime * timeAdjustment);
frameTime -= maximumFrameTime;
}
}
Now the second argument to the constructor defines how many frame-independent-seconds will elapse between signals.
new FixedTickProvider(container, 0.5);
will dispatch a signal twice per second
new FixedTickProvider(container, 5);
will dispatch a signal once every five seconds
I'd be willing to make a pull request if this is the intended functionality of the class but I admit I don't know how
If you create a new EntityState for an FSM, the components are added to the entity one at a time, and in arbitrary order. This has led to some issues with my use optional components and the nodeAdded signal. For example, here's a node:
class ImageNode extends Node<ImageNode>
{
public var position:Position;
public var image:Image;
}
I subscribe to the ImageNode list's nodeAdded signal. When a new ImageNode is added, I can create the display object that is actually responsible for rendering the Image on the screen. Here's a normal entity created:
var e = new Entity();
var pos = new Position(10,10);
e.add(new Layer(30));
e.add(pos);
e.add(new Image("alive.png"));
ash.addEntity(e);
This addition is detected by my event handler and a display object is created. The Layer component is optional; if one is attached to the entity, the display object will assign itself to the layer encapsulated by the Layer component. In this case above, this works, because all the components get added to the entity before the entity is added to the engine. All the components are guaranteed to be there before nodeAdded signals are dispatched.
And here's an example of an FSM state that modifies the entity, when fsm.changeState("dead") is called:
fsm.createState("dead")
.add(Layer).withInstance(new Layer(20))
.add(Position).withInstance(pos)
.add(Image).withInstance(new Image("dead.png"));
Layer and Image are new instances so the old versions of these components are removed and new ones added by the changeState method. This happens one component at a time and in arbitrary order. So sometimes the Layer object is present when the nodeAdded signal is made, and sometimes it is not, because Image was added first. If either components were added in the order specified, or a they were bulk added such that all components got created before any NodeList signals could be dispatched, this would not be an issue.
Since this is probably an issue in Richard's code as well, I don't know that you want to address it, but I thought I'd mention it to you. Alternatives right now seem to be adding the Layer component to the node (making it mandatory), detect the late-addition of the Layer component and adjust the layer then, or stop using nodeAdded for new node detection, instead using a system to loop through all nodes lacking a Display component, and construct the display on the spot.
Targeting Neko, every project using Ash-HaXe fails at start-up with:
Called from app/Main.hx line 107
Called from ash/core/Engine.hx line 66
Called from ash/signals/SignalBase.hx line 59
Uncaught exception - Invalid field access : _id_
I have had some success targeting HTML5, but not 100%. It compiles, but at run-time there is usually a blank stage. I am not proficient enough in HTML5 development to figure out what is happening, but the cpu usage is heavy and I can't seem to pause javascript execution.
I have no idea if these two issues are related, but I have a feeling they are.
The system 'add' functions in EngineState do not allow the System priority to be specified.
https://github.com/nadako/Ash-Haxe/blob/master/src/ash/fsm/EngineStateMachine.hx
It because currently we can't set subdir
for lib-from-git. It will be possible in the next version of Haxelib but now is not. I propose to move the haxelib.json
to the root and specify the src
attribute.
Oops, accidental submit. Will resubmit.
I have the following situation:
var dataSystem:System;
var objectSystem:System;
var cameraSystem:System;
var playState:EngineState = new EngineState()
.addInstance(dataSystem)
.addInstance(objectSystem)
.addInstance(cameraSystem);
engineFSM.switchToState("The Above State");
When switching to the EngineState, the systems are added in any arbitrary order due to the For Each loop here: https://github.com/nadako/Ash-Haxe/blob/master/src/ash/fsm/EngineStateMachine.hx#L114
This breaks things for me because both objectSystem and cameraSystem both depend on dataSystem to be full initialized, and that cannot happen until dataSystem is added to an engine. Because of this, I am of the opinion that that For Each loop should be a regular For loop, that way the order systems are added to a state are preserved.
And on that note, looking at the code, this For Each should probably be changed as well due to similar reasoning: https://github.com/nadako/Ash-Haxe/blob/master/src/ash/fsm/EngineStateMachine.hx#L99
Thanks for porting Ash to haxe!
Just a suggestion. Have you considered using macros to generate nodes, so the user doesnt have to create lots of Node classes?
Not thought through all the problems with this but something like engine.getNodeList({pos:Position, disp:Display}); or something like that?
Checked https://lib.haxe.org/ and searched using the term 'ecs' and ash-haxe did not appear in the list of libraries.
Please add 'ecs' tag to haxelib package.
Hey Nadako! First, let me say thanks for your work here! I am very excited to implement this in my haxe game. I have HTML and Flash targets working right away, but the MacOS target was giving me some trouble. Here was my initial error:
FrameTickProvider.cpp:39: error: integer constant is too large for 'long' type
I noticed someone else having trouble with this type of error and Nicolas said to add ".0" on the end of the number. So for line 27:
public function new(displayObject:DisplayObject, maximumFrameTime:Float = 9999999999999999)
I changed it to :
public function new(displayObject:DisplayObject, maximumFrameTime:Float = 9999999999999999.0)
And everything compiled perfectly!
I thought I would pass this change along for anyone else who might be having troubles.
Best,
Ken Rogers
In the frame tick provider, on this line, it basically caps the tick time if it's more than the maximum.
Instead of doing this, because of the way euler integration works, wouldn't it make more sense to write something like:
while (frameTime > maximumFrameTime) {
signal.dispatch(frameTime * timeAdjustment);
frameTime -= maximumFrameTime;
}
This would allow someone to pick a low maximum time (eg. I know I have fast-moving bullets and collision problems, so I pick something like 20ms) and get consistent gameplay.
Does that make sense?
Ever tried this? If you do this, the next/prev pointer would be strictly typed, this could result in better performance, even though code might bloat due to Generic duplication.
Inlining ListIteratingSystem would be good as well, though I'm not sure if it's possible.
public function add<T>(component:T, componentClass:Class<Dynamic> = null):Entity
{
if (componentClass == null)
componentClass = #if flash untyped component.constructor; #else Type.getClass(component); #end
if (components.exists(componentClass))
remove(componentClass);
components.set(componentClass, component);
componentAdded.dispatch(this, componentClass);
return this;
}
Flash AS3 target can use Object(unknown).constructor to quickly get class property. Actually, i think JS also might have something similar. Just a better optimization since the proxy method Type.getClass may have overhead since they aren't inlined with static method call extern.
Of course, it's also possible to switch Haxe and AS3 codebases interchangably (ie. use AS3's version of Ash over the Haxe codebase), since the API methods are the same. In this way, i code some systems in Haxe taking some advantage of inlining, but still run native AS3 Ash codebase over the compiled haxe SWC to optimize for native AS3 flash environment. However, i do somewhat prefer the Haxe macro generation benefits, which would be lost with the native AS3 codebase. So, there are pros and cons to both.
A sort of comparison, just for the note.
Ash-Haxe | hx-ash |
---|---|
ash package | |
+ optimised ClassMap | - not optimised ClassMap |
ash.core package | |
- no entity id | + entity id |
- no @optional for nodes | + @optional for nodes |
ash.fsm package | |
+ hasState() method | - no hasState() |
? createInstance() | ? createEmptyInstance() |
+ java compile fix | |
ash.tools package | |
? createInstance() | ? createEmptyInstance() |
asteroids example | |
+ in development | - stopped |
tests | |
different for both |
I love this library, but I dislike how nodes are defined: by extending a Node with a class parameter type of itself. This notation makes my head swirl. :) Without getting into the discussion of node macros, this legal Haxe trick makes it confusing write methods that accept a node as a parameter, which generally look like Engine.getNodeList
:
public function getNodeList<TNode:Node<TNode>>(nodeClass:Class<TNode>):NodeList<TNode>
Since Node is an autobuilding macro, couldn't the macro generate the next and previous fields dynamically, so that all nodes just extend Node and we can eliminate the type parameters?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.