Remember my logic gate simulator, Logicly? Well, after two years of interest from students, educators, computer architecture geeks, and a whole lot of people visiting from Hacker News, Reddit, and StumbleUpon (among many other smaller sources), I’ve decided this app has a proper audience. I’ve been working hard the last month or so rewriting it in Flex 4. Yes, working for oneself has advantages, like being able to convince your “boss” more easily when a rewrite is necessary. Of course, when budget time comes around (read that as “when I have to do some freelance work to pay the bills”), I might grumble that I didn’t get as much done as I hoped, but I still think breaking apart that prototype code and doing it right will be totally worth it. Without further ado, check out Logicly!
Since @troygilbert asked nicely, I thought I’d do a little informal writeup on how I have things set up for this project. It’ll probably be kind of high level, with a few mini-dives, if I think something might be particularly interesting.
Logicly is built with Flex 4. Everything is a Spark component with an MXML-based skin, except for the flexwires part which seemed a pain to rewrite. This is my first time properly building custom Flex 4 components, and I enjoyed the process quite a bit. I especially enjoyed being able to export FXG from Creative Suite applications to use in skins. The learning curve for me, an experienced Flex 3 developer, was mostly non-existent. I don’t remember anything tripping me up.
My current framework of choice is RobotLegs. The dependency injection is a joy to have around, and I like that it felt like PureMVC, except, you know, properly made for Flash. I still don’t know when I’d be likely to port my app to another language and want to use the “same” framework, but to each his or her own.
In the process of building Logicly, I realized that the RobotLegs Flex examples all use the main application as the
contextView for detecting when components are created and stuff. The problem is that when you create something on the PopUpManager, you don’t automatically get a mediator because the PopUpManager is attached to something higher up the display list than the application. Instead, I passed in a reference to
systemManager, and everything worked awesomely. A quick tweet about it got some attention, and a variety of shout-outs from the community. Sometimes it’s fun to shake things up a little.
Logicly is made up of several Flash Builder projects. There are two main Flex projects. One for the web version of Logicly and one for the desktop version. Then, there are a variety of Library projects where all the real magic happens. From a technical perspective, I’m not sure if I actually needed to do so much separation, but I think it definitely helped me to keep things loosely coupled in some places, which is a win in my book.
Let’s take a look at the various projects:
CommonLibraries contains the code that I reuse among all the other projects. My own personal library of utility functions is in there (math and drawing stuff mostly), along with the open source projects I use in Logicly, in either source and SWC form. For the SWCs, I just put them in a folder and use MXMLC’s include-libraries compiler argument to compile in everything from that folder.
Simulation handles signal propagatation among circuits. There is absolutely nothing visual in this project. It’s purely a model of the running circuit. In the prototype, the data was passed around through connections made in flexwires. That was a little too complex to maintain, in my opinion, and I like the fact that I could potentially reuse this library to view the simulation a different way in the future, or maybe to run sub-circuits in separate simulations.
Rather than using Events, I use Robert Penner’s as3-signals to pass signal changes throughout the simulation. It’s mainly for performance reasons. I’ve seen some crazy stuff built with Logicly’s prototype and the signal propagation could definitely become a bottleneck in the right hands. I do not use signals anywhere else. I was tempted to use signals to communicate within RobotLegs, but it didn’t feel very important at the time, so I just stuck with the default events.
CircuitViews has all the Flex components I built for various types of circuits you can drag into the editor, plus their skins. I mainly separated this one from the next project, Editor, to keep compile times speedy. I’m not sure if it actually has any real impact of compile time, though. Honestly, it just seemed like a good idea at the time. At the very least, since Editor is a pretty big project on it’s own, it’s nice to pull some stuff out to make it easier to navigate that source tree.
Editor is the powerhouse of Logicly. This is the biggest project, and it contains the majority of the Flex UI along with the related RobotLegs code. Basically, it’s everything that is shared among the desktop and web versions of the app.
I tried to find as much common ground as possible to avoid duplicate code. This led to some interesting things like a service in the web app that rather than saving or loading files, displays modal windows on Flex’s PopUpManager (to tell the user that these features are disabled and to ask them to sign up for updates about the desktop app). There are other ways to do this. For instance, I could respond to the save event in the desktop app with the same Command I have now, but have a different Command respond in the web app that opens the pop ups directly, but I liked the idea of implementing an interface to create a fake service and piggy-backing the
mapSingletonOf()dependency injection often used for services.
Yeah, sometimes I make weird architecture choices on a whim. Don’t be so serious all the time.
LogiclyDesktop is the currently-unreleased desktop app. It has a subclass of the RobotLegs Context I created, but didn’t instantiate, in the Editor project. It mainly handles some desktop-specific events and views. Currently, the plan is to allow LogiclyDesktop to support multiple documents in separate windows, each with its own RobotLegs Context (so that I can reuse all the same code without changes that would probably require major refactoring). There will be some minor communication among documents, but mainly only for window management (to figure out when the app should close, for example).
LogiclyWeb is, obviously, the web app. Like LogiclyDesktop, it has a subclassed Context with some customizations for differing behavior. I’m also using some monkey-patching in this project to override some views I created in the Editor project.
Since you cannot save a document in the web app, closing an edited document works a bit differently. In the desktop app, it asks you to Save, Discard Changes, or Cancel. To avoid confusion, the web app only asks to Discard or Cancel. I suppose I could have modified the component to add a boolean that omits the Save button, but the message changes too, and I didn’t want to write the invalidation and commitProperties() stuff. Actually, no, I think I chose monkey-patching because I wanted to see how RobotLegs handled it. The result worked, so I left it there.
Since this is my first real project using Flex 4, it was also my first time getting down and dirty with the new skinning system. I love it. Everything is so customizable, and I was able to spend much less time on certain parts of the UI. In Flex 3, I probably would have needed to build a whole new component for some Panels because I wanted a wildly different layout. Not so in Flex 4, thanks to the Spark skinning architecture.
Right now, most of the skins I made are variations on the default Spark skins. I plan to make more interesting tweaks down the road to make Logicly have a more unique look and feel, but that wasn’t a priority yet. For now, I mainly just cared about the colors and fixing a couple things in Spark that I think are horribly ugly (like those increment and decrement buttons on scrollbars… yikes!).
By the way, I really don’t mind the copy-paste nature of skins in Flex 4. I know you hardcore developers get an uncontrollable twitch when you can’t inherit or reuse something, but it all felt pretty natural to me. Skins aren’t code… even if they are. For the Creative Suite exported FXG, I didn’t even feel the need to clean it up all that much. Well, except for the million and a half extra namespaces each CS application throws in there. What’s up with that useless garbage?
Git works great for me. I’m not a big branch user, so all those features aren’t too special to me, but nothing beats the ease of typing in git init and starting a local repo. For Logicly, everything is still local on my machine, but I do use Github for flexwires, a related open source project I built along with Logicly. Eventually, I might upgrade my plan to put Logicly in a private repo on Github in order to have an offsite backup of the repo.
Yeah, about that…. I don’t really do unit testing. I know, that means I’m a horrible person. However, I’m inching closer and closer. My code today is much more testable than it was a year ago, and I actively try to make sure the right stuff can be tested on that magical day when I decide to write some tests.
I use an Ant script to build Logicly. It’s a pretty standard Ant script. Although I do actually use
<exec/> with the executables rather than using the Flex Ant tasks. That’s just the way I’ve always done it. I think I ran into some trouble early on (as in a few years ago) with the Ant tasks and gave up. The only magic in there is the fact that I increment a build number and use a filter token to inject the version string into the source code (it is displayed in the context menu).
Most of my development is from Flash Builder, so I don’t run the build script all that often. I could improve it, but that’s another case where the importance of doing so is rather low, so what’s there now is good enough.
So that’s my mostly unedited stream of consciousness explanation of Logicly’s project structure, code, and other stuff, with some thoughts on Flex 4 thrown in, since it was new to me. If I were working on Logicly full-time, it would have taken about a month to build. I switched to other projects over the course of development, and I generally took it easy throughout. My work on Logicly is a break from game development. I needed to recharge those batteries a little. My upcoming plans for Logicly include at least one more preview build, and then the release of the AIR app. Then it’s something game-related again, unless I decide to pick up some freelance projects.