LibGDX: Scene2d Demo with Java Universal Tween Engine

So I have modified my original demo somewhat to implement the Asteroid and ship pulse animations to use the Java Universal Tween Engine. I have also fixed some stuff and added some new features.

I put the star field behind the menu layer.

Game Test

I used Hiero to convert an existing free font to bring it into the application. BTW you can declare multiple fonts in the uiskin.json file i.e.

com.badlogic.gdx.graphics.g2d.BitmapFont:
{ default-font: { file: digital-7_60.fnt },
large-font: { file: digital-7_70.fnt }
},

I had to generate a second json file to hold a different sized default font because you can’t specify the font name for TextButton and I wanted a bigger size to match a phone touch-screen real-estate.

Taking a nod from the Tween engine I have created a proper callback action. This will take a count (<0 for infinite) and duration and repeatedly execute a call-back method. I could not use the completion handler in LibGDX actions as it resets the handler reference every time it get's called.

I have created two new action types TimelineAction and TweenAction. These encapsulate the running and management of the tweens. They are associated with an Actor and will animate it until complete. They manage the release of the tween back into the pool
(more about this further on).

The movement and rotation of the asteroids is implemented using the Timeline feature from the tween engine. Note the COMPLETE event callback to send an event which will trigger the removal of the element from the view.

int durationMove = (int) ((Math.random() * MAX_DURATION + MIN_DURATION) * 1000);
int rotate = (int) (Math.random() * 1440)-720;

Timeline timeline = Timeline.createParallel()
		.beginParallel()
		.push(Tween.to(this, ActorAccessor.POSITION_XY, durationMove).target(x, 0 - this.height).ease(Linear.INOUT))
		.beginSequence()
		.push(Tween.to(this, GroupAccessor.ROTATION, 0).target(0))
		.push(Tween.to(this, GroupAccessor.ROTATION, durationMove).target(rotate))
		.end()
		.end()
		.addCallback(EventType.COMPLETE, this)
		.start();

TimelineAction timelineAction = TimelineAction.$(timeline);

action(timelineAction);

Something of interest to note here. I want the rotation to last the length of the screen traversal and I want it to start from zero. I could set the “rotation” value of the sprite explicitly at the start of each run or I can do a non-Tween which will initially rotate to zero. This is what I have done here. There doesn’t seem to be a way to set the initial value in the Timeline/Tween setup itself any other way although I am open to suggestions.

The pulse is a straight Tween. Again, note the COMPLETE event callback to send an event which will trigger the removal of the element from the view.

Tween tween = Tween.to(this, ActorAccessor.POSITION_XY, duration)
                    .target(x, height)
                    .ease(Quad.OUT)
                    .addCallback(EventType.COMPLETE, this)
                    .start();
tweenAction = TweenAction.$(tween);

action(tweenAction);

I have extended the Actor and Group classes to manage the clean up of actions which are prematurely removed from the view. What I mean by this is – imagine a pulse is moving up the screen, animated by TweenAction. In normal operation the animation would complete, the “done” flag would get set and the “act” method would call the “finish” method on the associated actions which will put the respective actions back in the pool. If the pulse hits an Asteroid on the way up we should probably set the “done” flag on the PulseSprite and let it clean up itself but because of the way I am pooling these items I am having to override the “remove” method and force the “finish” method on all associated actions myself. This is a work in progress and I am still having a think about the best approach here. It works for now and you should have no leaking objects.

Demo code here.

Comments

Leave a Reply