Monday, November 19, 2012

Haxe NME Follow-up

A few weeks ago, I began working on a Haxe NME version of the NEO Scavenger encounter editor. I endeavored to share my first impressions of NME, but I had two more weeks of experimenting with it, so I wanted to share what I learned. What follows, then, is a brief discussion of each of the highlights from that experiment.

Encounter Editor Screenshot


Speed

The main reason I chose to rewrite the NEO Scavenger editor in Haxe NME was because the Flash version had performance issues. The recent increase in data caused loading to timeout and crash. And what's more, even before this loading failure, I was reluctant to use the tool due to its sluggishness. Faced with rewriting lots of code no matter what, I decided to try Haxe NME.

I'm happy to say that Haxe NME showed its strength as a high performance framework. I haven't done any proper comparisons between NME and Flash, but the editor is quite responsive at 2560x1600 resolution. This was while drawing hundreds of boxes, hundreds of bitmaps, and thousands of triangles simultaneously, all with scaling (no bitmap rotations, though). 

The Windows target also loaded (SQL/URL-based) data much faster than the old Flash version. Though, the NME Flash target still needed optimization to complete data loading before the 15 second timeout.

UI

Form fields are still a weakness with NME on Windows targets. I was able to hack together some quick and dirty UI elements as stopgaps for buttons, toggle buttons, and drop-down boxes. But not having Flash's built-in UI elements was an obstacle.

TextFields

TextFields work on Windows targets, but are extremely limited. They lack support for getting the current caret position, selected text, and copy/paste. Flash targets work fine, however.

Performance can be an issue, though. I was able to get about 2-3 TextField elements per encounter node (about 2200 total) without any noticeable slow-down on my machine. However, much beyond that, and the framerate seemed to drop. I ultimately opted to hide TextFields on all nodes, and reveal them as-needed to keep framerates high.

A closer look at some of the editable fields I have in each node/connector.
Copy/Paste

Copy/paste, itself, turns out to be a general issue in Windows targets. I wasn't able to get access to the clipboard with vanilla NME, and the systools library that I tried wouldn't compile for NME Windows targets. Copy/paste on Flash targets appeared to work fine, though.

Object/Dynamic Weirdness

Haxe uses a type called "Dynamic" to handle generic objects (instead of Flash's Object class). On the Windows target, I was able to use .get() to access dynamically-named properties in a URLVariables object. But when compiling on Flash, the compiler didn't like that method. What's more, the traditional Flash method of Object["propertyName"] threw compiler errors. In the end, I had to use Reflect.getProperty(). Not too much of a pain, just unexpected.

URLRequest and 413 Error

Using URLRequest on Flash targets worked as expected, but caused errors when tried on Windows targets. I kept receiving 413 "request entity too large" errors when trying to execute the request. As it turned out, I needed to initialize the .data field of the URLRequest object to quell the 413 error. I'm not sure why a null data field would cause a "request entity too large" error, which is why it took me a while to figure out. I ended up finding the solution by looking up libcurl issues of a similar nature, since that's the library used by NME.

Key Handling

Flash and Windows targets for NME seem to trigger different key codes for ascii characters. I ended up writing special handling code to convert all key codes to uppercase so each platform interpreted results consistently.

Mouse Handling

I ran into issues getting any mouse scroll wheel events to fire on Windows. I didn't pursue this very far, though, as I could work around it, and had bigger fish to fry.

Extensibility

There were one or two instances where I started digging into NME source code itself, to see if I could work around problems I was having (such as enabling unexposed TextField properties). However, I was unable to navigate the matryoshka-doll-like layers of code to the source I needed.

Specifically, I was trying to expose a field in TextField, like caretIndex, so I could hack together a copy/paste stopgap on Windows. I decided to look at how existing fields were exposed, like .numLines. When I dug into the TextField.hx class, I found that Windows targets used the neash.text.TextField implementation.

However, the neash.text.TextField code redirected .numLines to:
Loader.load("nme_text_field_get_num_lines", 1);

Loader.load uses cpp.Lib on Windows targets, so looking into that, I found that cpp.Lib.load used:
__global__.__loadprim(lib,prim,nargs);

which was loading a primitive from a DLL. I wasn't clear where to go from here, though. It seemed like there should be some sort of table or Rosetta stone for mapping strings like "nme_text_field_get_num_lines" to a block of code somewhere, but no amount of searching seemed to reveal it.

I was pretty frustrated by my inability to get to the bottom of that question. And ultimately, had to switch targets to Flash to get the TextField behavior I needed. Though, that in itself is a pretty strong endorsement for Haxe NME.

Cross-Platform

Being able to change a drop-down from "Windows" to "Flash" was almost all that I needed to do to get around the aforementioned TextField issues. That, and some tweaking in the way I accessed dynamically-named fields of a Dynamic object, had me well on my way to continuing work with almost no interruption. Literally, a couple weeks' worth of development on the Windows target was ported to the Flash target within about two hours. Pretty impressive.

Updates

It's also important to point out that NME is updating fairly frequently. Even in the couple months between the version of NME I first downloaded, and one I recently downloaded, there was significant improvement. In fact, several bugs I was encountering were solved by an update to NME 3,4,4.

Conclusion

I'm still pretty impressed with what I'm finding in NME. It has gaps here and there, and it can be tricky to find support. But I was surprised how far I could take it, given it's relative infancy compared to platforms like Flash. I suspect that if I were making a purely game-like app, without the need for text editing, it would do all that I need without issue. I think that the only feature NEO Scavenger uses which I've not yet tested is the audio playback.
Hopefully, some of the info I've shared above helps other intrepid developers in overcoming similar obstacles. And it'd be great if this info was enough to embolden a few new developers to put Haxe NME through its paces. I truly feel that it's a powerful tool, and just a few more concurrent users could be all that's needed to sustain further growth.

By the way, if you're interested in a more chronological account of my explorations, you can find out more about the editor overhaul in these Blue Bottle Games posts:


Thanks for reading, and see you next time!

8 comments:

  1. I can only thank you for sharing all that work with us, you really are a nice person :). Haxe wasn't even on my radar, but now I'll be looking at it further down the line now. Tx tx tx!

    ReplyDelete
  2. Great article! I'm experimenting a lot with this kind of data administration tools. Right now, I'm into Waxe for creating desktop style applications for data admin tools. Lots of controls still to be implemented, but the most used ones are there. NME is great, and having the flash api power combined with full-grown cross-target UI solutions would really make it shine!

    ReplyDelete
  3. Glad to hear the post was helpful! I'm definitely planning on trying Haxe NME for my next game prototype and/or project. I suspect it will be smoother sailing for a pure game, and may have even further updated by the time I get there.

    @cambiatablog - Waxe was something I briefly looked into, but I wasn't yet familiar enough with NME to understand how they would work together. However, it might make more sense to me if I tried again now.

    In any case, a unified, cross-platform GUI would be pretty awesome, as you point out!

    ReplyDelete
  4. Thanks!

    This is very helpful as we continue to improve NME's support for application development.

    TextFields need more love, and I agree that copy-and-paste support on Windows would be great.

    Reflect.getProperty is a normal way to access dynamic properties in Haxe development. You can use square brackets with Flash if you have the "untyped" keyword, but I'd recommend Reflect so it stays cross-platform.

    After your previous post, I went to the source and discovered it did expect "data" to be a ByteArray, not null, when using the POST request method in C++. I added the fix two weeks ago, so it should be in our next release: http://code.google.com/p/nekonme/source/detail?r=2182

    I apologize there is not more documentation for the way that Haxe and C++ interact under the hood. Architecturally, calls to "nme.Loader" use the "cpp.Lib.load" method to bind with symbols defined in the NME native binary, which has its source files under "/project" within the NME directory. We precompile the binaries before a release, but when you use NME from the source, you can run "nme rebuild windows" (or another target) to compile.

    To make things a little simpler (because C++ symbols can come from about anywhere), we have the binding symbols defined under "/project/common/ExternalInterface.cpp"

    The reason why you were not able to find "nme_text_field_get_num_lines" was that DisplayObject, TextField (and maybe a couple other) objects have their bindings generated by a macro inside ExternalInterface.cpp since they have so many properties.

    It's been taking longer to get NME 3.4.5 out the door since Hugh and I have both been making big changes.

    ReplyDelete
  5. Hey Joshua,

    Thanks for following up! No need to apologize for lack of documentation. There's an inherent complexity in what NME is trying to do, and I think there are bound to be some dark corners.

    Besides, many of the deeper AS3 hurdles I've run into were not solved by Adobe's documentation, but rather the extensive community around AS3. I suspect NME will have such a support network before long.

    And thanks for pointing out ExternalInterface.cpp! I had a sneaking suspicion that there could be some string-cobbling being done somewhere. I just didn't know where to look!

    ReplyDelete
  6. Hi,
    Most of the text properties are coming through the TEXT_PROP macro in ExternalInterface.cpp - that is why your text search fails.
    I used to have some clipboard stuff from 'Scrap' - see the old code at
    http://code.google.com/p/nekonme/source/search?q=scrap&origq=scrap&btnG=Search+Trunk
    But this got lost somewhere.

    ReplyDelete
  7. Thanks for the tips! That macro was the missing link for me. It seems logical now. I just didn't know where to start looking.

    And Scrap seems like a useful place to start if I decide to tackle Windows copy/paste again in the future.

    ReplyDelete