|
|
|||||
Simon Morris's BlogGetting AnimatedPosted by javakiddy on March 08, 2007 at 07:13 AM | Comments (13)Well, given feedback from around the web it seems a lot of people liked my recent little foray into the realm of Swing eye candy, which is both very flattering, and also rather worrying. Do so many of you really value GUI bling? I continued to play about with the code after I last posted it, and the final (for now!) bling'd up version can be WebStart'd from the link below. I must warn you, it takes eye candy to new depths of pointlessness! But these little experiments were never about testing the appropriateness or otherwise of various effects in a user facing GUI. Instead I was attempting to assess how easy it is to pull off such effects using bog-standard AWT/Swing API calls. I found Swing highly compliant, which was reassuring. Instead the bulk of the head-scratching went not on getting Swing to 'swing', but on the custom PLaF and the movement mechanics behind the transitions. The movement mechanics in particular required a lot of boilerplate code. In the end I wrote a mini API for tracking movement over time, and it was this utility API which proved to be far far more interesting than the main Swing code itself. So, despite the presence of rival animation solutions, I found myself drawn towards tinkering with this mini library, and eventually went back to the drawing board, rebuilding the code from scratch to be more universal and tighter in design. The rest of this article will look at the results via a little trial animation I crafted.
Creating the FramesThe rocket demo above is a simple little looped animation. A cartoon rocket takes off, hovers, the engine cuts out, and it comes tumbling back down to the ground. Key snippets of the code are presented below, in stages. First off we need to set up some animation images for our rocket. I've already (elsewhere) loaded nine images into a // Sequence in which the frames loop: 1,2,1,2... ArrayFrames The first block creates the rocket with its engine thrusters running, specifically a loop of frames running 1,2,1,2,1,2... with a 150 millisecond gap between frames. The second has the engine cut out by showing a random frame from an array of possibilities. The third is another loop sequence, showing the rocket tumbling over and over, as is the fourth and final block, which shows a slow two cell animation of the crashed rocket. You'll note that the
Creating the MovementNow let us move on to the code which actually builds the animation: iac = new InterpolatedAnimationController Firstly we create a new controller. This particular specimen will stretch our animation out over a given duration (in milliseconds.) Having glued all the bits of our animation together, it is this controller class we will use to access it. The type parameter matches the type of our Into this controller we add paths (where to go), velocities (how to get there), and frames (what to show). Each of these has a weight, which determines how much of the overall animation it affects. In the Take off block the weights for all three types of data are the same, 0.30 (double.) Given that I've contrived the code such that all weights total 1, this will mean the opening part of the animation will occupy 30% of the total time. Weights don't have to add up to 1. The controller scales each portion of the animation across the total of all weights for that type of data, so I could have used integer values instead (still represented by doubles) or whatever scheme I felt comfortable with. In the Take off block of code you'll see we provide a straight 'up in the air' path, from (0,250) to (0,0), using a velocity which gradually increases then decreases over the course of the movement, while showing the frames we previously set up for the rocket booster animation. Because several of the standard velocities do not require any configuration parameters and are re-entrant, I made them singletons in an abstract Moving on to the next block: Hover... uses a drunken line which meanders around the (0,0) point, which is a useful method of getting our little rocket to sway and bob as it hangs in the air. The extra parameters in the constructor are to constrain the overall extent of the vertical and horizontal drift (how far) and the drift increment each frame (how fast.) We don't need a velocity for this, so we use a uniform acceleration just to pad out that part of the animation, keeping the paths and velocities aligned. Note something interesting about the frames: firstly we have two sets of frames, the first shows the booster as normal while the second shows it starting to fail, but the total weight of the frames is 0.05 over the weight of the other portions of the animation — more on this in a moment. And so, on to the next block: Tumbling uses a straight line from (0,0) to (0,250) with a cute little velocity called You'll note that the tumbling animation is 0.05 short of the weight for this portion of the animation. This is because I've allowed the previous This demonstrates an important part of the API: the three constituent components, paths, velocities and frames, are all independent of each other. In the above example we grouped them into portions to make the code easier to read, but each of them are independently interpolated across the animation timeline. For example, if we commented out all the And finally... the Crashed portion simply animates over two frames as the rocket lies prone at (0,250). Nice and simple.
Getting It On ScreenSo, we've set up our animation. Now we need to actually run it:
// When we want to start the animation we need to seed
// it with the current time, and reset any state data the
// paths/velocities/frames may have.
iac.reset();
:
:
// This code is called on a timer, such as
// java.util.TimerTask.run() or
// javax.swing.Timer.actionPerformed()
if(!iac.isFinished())
{ // Get the current animation co-ords and frame image.
Point p=iac.getCoordinates();
BufferedImage im=iac.getFrame();
// ... Draw your stuff here ...
}
else
{ // Animation has finished. Normally we might call
// Timer.stop() here to unschedule our timer task, but
// instead we'll simply reset the animation to replay it.
iac.reset();
}
The animation controller code should be easy to understand without having to walk through it. You'll see here why the controller needed a type parameter which matched the Of course the ConclusionWell, I had a lot of fun messing about with all that code, and the resulting API is functional and lightweight enough to employ in my own personal ongoing Swing Bling adventures. Beyond that... who knows? Is anyone bothered enough to want to see the source code? Is this idea strong enough to merit an Open Source project of its own? I don't know... I didn't build it as a serious rival to any other animation API, I just wanted to have a little fun. And, after all, isn't 'fun' what 'Swing Bling' is supposed to be all about? :) Bookmark blog post: CommentsComments are listed in date ascending order (oldest first) | Post Comment
| |||||
|
|