Introduction
Yesterday, I gave a presentation at the meeting of the Indianapolis Java Users' Group. The presentation, "Serious Game Development in Java," gave some background about how I transitioned from game hobbyist to serious game development researcher, and I talked about my two successful Java-based serious games: Morgan's Raid and Equations Squared.A portion of the attendees at the IndyJUG meeting |
Morgan's Raid
Morgan's Raid was written using Slick2D, which had been my go-to library for Java game development. However, the original developer and maintainer, Kevin Glass, has moved on from the project, and it seems to be struggling now. Kevin did a good job keeping the libraries in sync with the native libraries of lwjgl, and with him off the project, I would not recommend starting new projects in Slick2D. Kevin has moved on to libGDX, which looks like an interesting project, although I prefer PlayN, as described in the next section.Here is a brief description of the other libraries we used in Morgan's Raid. For brevity, I'm not including their transitive dependencies, but note that managing transitive dependencies on this project is precisely why I was blown away by Maven, as described in the next section.
- Apache Commons CLI: robust handling of command-line arguments, which we used to easily modify runtime behavior, e.g. fullscreen vs. windowed mode
- Apache Commons Configuration: robust handling of application configuration, such as animation speed, default volume, etc.
- EasyMock: mocking library for test-driven development
- Guava: broad and robust library that simplifies Java development, making particular use of the
Lists
,Maps
, andObjects
classes. - Joda-Time: all the time calculations are handled with this fantastic library, and if you're doing any time-based logic, you should be using it too.
- SnakeYAML: parser for YAML, which we used to describe the cinematic scenes in the game
- CruiseControl: though not a library, we used this for continuous integration
I have several posts on this blog about the design and development of Morgan's Raid. If you're new, here are some of the most descriptive:
Equations Squared
When I began work on this project, I knew I wanted to develop an HTML5+Javascript solution with the least possible amount of pain. I evaluated several possibilities and settled on PlayN. This amazing library allows cross-compilation of the same codebase to desktop Java, HTML5+Javascript (via GWT), Android, and iOS (and Flash, kind of, but its support has not been great).
PlayN relies upon Maven to manage dependencies and project configuration. It took me some time to make sense out of how it was working, but now it's hard to imagine going back to manual dependency management. In Morgan's Raid, for example, when I wanted to add a new library, I had to manually download the binaries, and all the binaries of its transitive dependencies, put them into my project's lib folder, configure the build path, configure native libraries if necessary, and then hope that all the different libraries would work together. To upgrade any library to a new release, which happened to some of our core libraries during development, I had to repeat this process by hand. By contrast, to add, say, Mockito to Equations Squared, I just added this to my pom file:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
That last bit, the scope, is really fascinating: it says that the project should use Mockito only when running unit tests. Scopes aren't needed for most of the libraries I use, but this example shows how simple a process it is to specify them. Also, need to update to a new release? Just update that version tag and Maven takes care of the rest.
Speaking of Mockito, it has become my favorite mock object library for Java. The API design is elegant and allows for readable code with minimal boilerplate. Here's a sample unit test—the same one I showed in my IndyJUG presentation—that demonstrates Mockito. By way of explanation, this code builds a token list containing "-" and "1", parses it into the expression "-1", then creates a visitor object and hands it to the expression. The expected behavior is that the visitor visits two nodes in the parse tree: the unary negation and the value 1. Note that
mock
and verify
are static calls to the Mockito library.@Test
public void testVisitorHitsNegationAndInteger() {
tokens = ImmutableList.of(
SymbolToken.MINUS,
IntegerToken.create(1));
Expression e = parseTokens();
Expression.Visitor visitor = mock(Expression.Visitor.class);
e.accept(visitor);
verify(visitor).visit(UnaryOperation.NEGATE);
verify(visitor).visit(1);
}
Equations Squared uses the pythagoras, React, and TriplePlay libraries from ThreeRings. Pythagoras is a collection of geometry utilities that is well described on its Web site. React brings functional reactive idioms and the slots/signals idiom to Java, and that merits a small example. My GameView class exposes a signal with a method like this:
public SignalView onGameOver() {...}
Any agent in the system that needs to know when the game end condition is met can connect a slot that is notified when the signal is emitted, for example:
game.onGameOver().connect(new UnitSlot() {
@Override
public void onEmit() {
displayListOfBadgesAndDemerits();
}
});
Signals can have type parameters as well, though here I am using the simplest form. React also provides convenient value objects that emit signals when values change:
Value<Integer> v = Value.create();
...
v.connect(new ValueView.Listener() {
public void onChange(Integer newValue, Integer oldValue) {
// Handle change here.
}
});
As you can see, what I'm doing is using React to provide convenient, quick, efficient reification of the observer design pattern. No extra boilerplate required here, no fat interfaces and adapter classes: just hook up slots and signals and get going.
Where PlayN provides a low-level API for game development, TriplePlay provides many of the niceties one needs to get games up and running, such as handling screen transitions, layer animations, and UI widgets. One of the reasons I love TriplePlay (and PlayN) is that the designer takes care to support fluent programming idioms. This is a direction I have been taking much of my own development as well. Consider this example from Equations Squared that handles popup notifications:
tweenTranslation(popup.layer())
.from(325, 320)
.to(325, 300)
.in(0.9f)
.easeOut()
.then()
.tweenAlpha(popup.layer())
.to(0)
.in(0.3f)
.easeOut()
.then()
.action(deleteLayerAction);
The code reads exactly as one would explain the animation sequence. When source code is as short and expressive as it needs to be to convey an idea, that's a good program. Working with some of the fluent styling API in TriplePlay takes a little getting used to, especially if one comes from a push-button background as in Swing. However, I really felt like my own ability to express myself fluently in Java increased after learning to use TriplePlay.
For more on Equations Squared, check out The Story of Equations Squared.
Acknowledgements
I want to thank Michael Dowden for inviting me to present, and to all the IndyJUG community for their warm welcome. The event was graciously hosted by E-gineering, and I could tell by their amazing facilities that they are a company who takes their work seriously and respects their employees. Check out this centrally-located kitchen!The E-gineering Kitchen |