James Williams
LinkedInMastodonGithub

Animating Sprites with PlayN and Trident

Yesterday I posted a teaser photo of what I was working on: a zombie model in the open source 3D modeling application Blender. No, I didn't create it. What's a programmer who doesn't model to do?

Zombie model in Blender

OpenGameArt has lots of open source and Creative Commons-licensed material for those of you who like me, aren't artists. The assets used in this post are available here. If you were working from just the model, I've skipped the steps of rendering the frames and compiling them into an image file. In this short post, we'll use the Java animation library Trident to animate a set of sprite images in PlayN.

In brief, Trident allows you to animate properties and transition through different states. These can be integral or float numeric values, colors, or custom properties. For a more detailed view, specifically of the JavaScript version, you can check out the examples in my book (shameless plug, linky above) or learn about the library straight from the creator mouth Trident creator Kirill Grouchnikov is currently a UI engineer for the Android team at Google. The snippet below shows the code needed to create a timeline lasting 5 seconds per run that will repeatedly cycle through 36 frames. sheet is an instance of the SpriteSheet class I created for this project.

Timeline timeline = new Timeline(sheet);
timeline.addPropertyToInterpolate("currentFrame", 0, 36);
timeline.setDuration(5000);
timeline.playLoop(Timeline.RepeatBehavior.LOOP);

After loading the image showing all the zombie sprites, the SpriteSheet class separates them into Image.Regions that could be used later for the animation. This isn't specifically required but makes things a little simpler. The setCurrentFrame function satisfies the contract that Trident expects. Lastly, based on the frame number that Trident set, the paint function draws the proper frame.

void makeSubImages(Image image) {
    regions = new ArrayList();
    int row = 0;                            // only using top row of sprite sheet
    for(int i = 0; i<36; i++) {
        Image.Region region = image.subImage(i*128, row*128, 128, 128);
        regions.add(region);
    }
}

public void setCurrentFrame(int currentFrame) {
    //log().debug(""+currentFrame);
    this.currentFrame = currentFrame;
}

void paint(float alpha) {
    layer.surface().clear();
    layer.surface().drawImage(regions.get(currentFrame), 0, 0);
}

Now for the bad news

Desktop Java is the only working target at present. Trident uses a Runnable to do it work, which is one of the restricted interfaces in GWT, so the project will not work as written. But as mentioned before, there is a JavaScript port called trident-js. Once could probably easily wrap it with some JSNI functions and be in business in short order. Though there is support for Android, it seems that unlike the Java Desktop version, Android's implementation doesn't have a handler to interpolate over integers. So it will build but crashes for not finding a proper handler. It's a somewhat straightforward fix but something I'll leave for another day.

Check out the code here.