Giter Site home page Giter Site logo

ash-haxe's Introduction

This is a Haxe port of the awesome Ash entity component framework by Richard Lord. It leverages Haxe's great cross-platform portability and runs on Flash, JavaScript, C++, Android, iOS and so on. Also it uses much static typing features of Haxe, allowing more mistakes to be detected at compile time instead of runtime than in original ActionScript 3 version.

Check out original Ash website for great articles on entity frameworks and game development.

TODO:

  • Port serialization stuff. This is kind of tricky because original Ash uses reflection and we are trying to avoid it, so we gotta be smart about macros.
  • Refine access control for private classes and fields. Original Ash used internal class/field feature of AS3, in Haxe we need to use ACL metadata.
  • Review generacted code on performance, add inlines (especially important to inline iterators)

Author: Dan Korostelev [email protected]

ash-haxe's People

Contributors

13rac1 avatar dimumurray avatar gogoprog avatar jamiltron avatar markknol avatar nadako avatar scriptorum 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

ash-haxe's Issues

When using Ash-HaXe with NME, neko target crashes with run-time error. HTML5 target succeeds occasionally.

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.

MacOS target fails to compile asteroids

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

Consider using untyped component.constructor for Entity.add() for Flash target

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.

Limiting Frame Ticks

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?

ClassMap.keys() slows down over time

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?

"haxelib git" not work

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.

Implementing Generic for Node class?

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.

Changed EntityState components not added in order

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.

Macro Nodes

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?

Tests failed with haxe 3.3.0

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.

Burp extends Node<Burp>

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?

"dead code elimination" (-dce full)

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.

FixedTickProvider incomplete?

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

Comparison between different ash ports

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

For Each in EngineStateMachine breaks Dependencies

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!

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.