I'm using LibGDX engine in my game and I started to use Tween for animation:
I have in my game a small 'custom' progress bar. One image is an empty progress bar and the second is full one, they booths have the same width and height.What I do is I update it by setting the position of the full as the same as the empty, and when I want to display progress I use:
private Image fullProgress; //its a libGDX image type
//I have already set the process
float originalHeight = progressEmpty.getHeight();
howMuchProgress = originalHeight * process;
fullProgress.setHeight(howMuchProgress);
Any ideas how can I make it change the height with delay animation, so I can see it for a few seconds? better will be to use the Tween Engine?
Given your code example, delay should already be calculated in the process.
If you need to visualize the delay just for the debugging purposes (for example, for faking the AssetManager's loading progress), I suggest you do something like this:
private static final float LOADING_MIN_TIME= 2.0f; // delay amount in seconds
private float loadingTimer;
#Override
public void render(float delta) {
// ...
loadingTimer += delta;
float loadingTimerScaled = loadingTimer / LOADING_MIN_TIME;
if (loadingTimerScaled >= 1.0f) {
loadingTimerScaled = 1.0f;
}
process = Math.min(process, loadingTimerScaled); // use the slow(fake) value.
}
Related
I have a circle at the center of the screen inside which there's an ImageView + TextView. I have another two ImageView+TextView, one at the top and another at bottom of the screen.
My requirement is :
I want a copy of the top ImageView+TextView and a copy of the bottom ImageView+TextView to move in animation into the center of the circle, thereby changing the value of the textView inside the circle.
For example:
Say top textView has value 200 and bottom textview has value 300. I want a portion of those values (say 100 or 150) to animate and move into the circle, but the original values 200 and 300 should remain on the same position.
I've tried using TranslateAnimation. However I face issues finding the x and y coordinates of the center circle. It is not exactly going to the center of the circle. Also original view's position is not retained.
TranslateAnimation animation = new
TranslateAnimation(startLayout.getX(),endLayout.getX(),
startLayout.getY(),endLayout.getY);
animation.setDuration(1000);
animation.setFillAfter(false);
startView.startAnimation(animation);
startLayout is the linearlayout in which ImageView and TextView reside.
Please help! Thanks!
I had the same issue and I fixed by using the next code (sorry is in Kotlin, but works the same in Java).Let's say viewFirst wants to reach viewTwo position:
(DON'T USE):
viewFirst.animate()
.translationX(viewSecond.x)
.translationY(viewSecond.y)
.setDuration(1000)
.withEndAction {
//to make sure that it arrives,
//but not needed actually these two lines
viewFirst.x = viewSecond.x
viewFirst.y = viewSecond.y
}
.start()
(USE THIS SOLUTION):
viewFirst.animate()
.x(viewSecond.x)
.y(viewSecond.y)
.setDuration(1000)
.withEndAction {
//to make sure that it arrives,
//but not needed actually these two lines
viewFirst.x = viewSecond.x
viewFirst.y = viewSecond.y
}
.start()
Using the getX() and getY() methods define the position of the view in pixels, but the constructor you use defines Float type values that must be values from 0.0f to 1.0f
TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
This is another option using the view`s position in pixels:
viewFirst.animate()
.x(viewSecond.getX())
.y(viewSecond.getY())
.setDuration(1000).withEndAction(new Runnable() {
#Override
public void run() {
viewFirst.setX(tv2.getX());
viewFirst.setY(tv2.getY());
}
}).start();
Try this for accurate coordinates
private fun moveView(viewToBeMoved: View, targetView: View) {
val targetX: Float =
targetView.x + targetView.width / 2 - viewToBeMoved.width / 2
val targetY: Float =
targetView.y + targetView.height / 2 - viewToBeMoved.height / 2
viewToBeMoved.animate()
.x(targetX)
.y(targetY)
.setDuration(2000)
.withEndAction {
targetView.visibility = View.GONE
}
.start()
}
I am reading learning andengine book https://play.google.com/store/books/details/Martin_Varga_Learning_AndEngine?id=ww9_BAAAQBAJ.
Try to manually execute setPosition on Player inside onManagedUpdate scene class
Here is my code. For example
player.getY()=100
platform.getFirst().getY()=200
player.getHeight=50
#Override
protected void onManagedUpdate(float pSecondsElapsed) {
// in this position player.getY always 100 evertime onManagedUpdate is called even I already set up player.setPosition like below
if (player.getY() < platforms.getFirst().getY()) {
float x = platforms.getFirst().getX();
float y= platforms.getFirst().getY() + player.getHeight() / 2;
player.setPosition(x, y);
}
}
Unfortunately the player.getY() is back to 100 everytime onManagedUpdate is called even I already set player.setPosition(x,y) inside the managedUpdate
What's wrong ?
In your above assigments, you are using player.getY() = 100; if you want to asign a value to Y coordinate use player.setY(100f); to set a property use set() methods and to get some property value use get() methods.
Change you assigments to this:
player.setY(100f);
platform.getFirst().setY(200f);
player.setHeight(50f);
Use float values.
I am using the universal tween library in LibGdx to move the Sprite from top to Middle of the screen as mentioned in the below code. I could see there is remarkable delay before the animation starts. I even set the delay as 0f still I am not able to resolve it. Please assist.
sprite1 = new Sprite(LEVEL_SELECTION_BOARD);
// Top to bottom
sprite1.setPosition((SCREEN_WIDTH/2) - LEVEL_SELECTION_BOARD .getWidth()/2 ,SCREEN_HEIGHT);
Tween.to(sprite1,SpriteTween.POSITION_Y,20f)
.delay(0f)
.target( SCREEN_HEIGHT /2 - LEVEL_SELECTION_BOARD.getHeight()/2)
.ease(TweenEquations.easeOutBack)
.setCallback(callbackEnd)
.setCallbackTriggers(TweenCallback.END)
.start(tweenManagerLevelBoard);
#Override
public void render(SpriteBatch sb) {
//System.out.println("StartScreen - Render ");
batch=sb;
sb.setProjectionMatrix(camera.combined);
sb.begin();
sb.draw(BACKGROUND,0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
delta = (TimeUtils.millis()-startTime)/1000; // **get time delta **//
tweenManagerLevelBoard.update(delta); //** update sprite1 **//
sprite1.draw(batch);
sb.end();
}
Try that:
tweenManagerLevelBoard.update(Gdx.graphics.getDeltaTime());
Say I'm animating a button from x=0 to x=200, using:
ObjectAnimator animator = ObjectAnimator.ofFloat(button, "x", 0f, 200f);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
Now, when the button is at x=100, I want to cancel the animation. When I call animator.cancel(), the button stops abruptly. What I'm trying to achieve is that the x value gradually slows down (somewhat like a DecelerateInterpolator), and neatly comes to a stop at, say, x=120.
Ofcourse, I could just cancel() the animation and start a new decelerating animator. However, this does not take the current velocity of the button in account, leading to weird behavior.
How would I do this?
As correctly pointed out by #Gil, You have to deal with your custom Interpolator implementation. The good news is that you don't actually need to implement everything yourself. You can just combine 2 different interpolation formulas: accelerate/decelerate for the main animation and decelerate interpolator for cancellation.
Essentially that's what you are looking for:
Normal accelerate/decelerate animation:
Your animation when you cancel it somewhere in the middle:
Here is my quick interpolator implementation:
static class MyInterpolator extends AccelerateDecelerateInterpolator {
private float phaseShift = 0f;
private boolean isCancelled = false;
private float lastInput = 0f;
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* #param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* #return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
#Override
public float getInterpolation(float input) {
lastInput = input;
if(!isCancelled)
{
return super.getInterpolation(input);
}
else
{
return getCancellationInterpolation(input) - phaseShift;
}
}
public void cancel()
{
isCancelled = true;
this.phaseShift = getCancellationInterpolation(lastInput) - super.getInterpolation(lastInput);
}
private float getCancellationInterpolation(float input)
{
return (1.0f - (1.0f - input) * (1.0f - input));
}
}
As you can see, I use default interpolation for normal animation and switch do deceleration one when animation is canceled. Obviously this code is not perfect (it doesn't reset phaseShift value and isCancelled flag which causes miscalculation if you use repeat mode), but that's something you hopefully can figure out yourself :)
I created sample project on GitHub, so you can see how it looks like
FOLLOW UP
I played a bit more with formulas and taken the second part of DecelerateInterpolator implementation. I introduced factor parameter which helps you to control how fast your cancellation should happen (some sort of traction). Setting factor to 1.5 gives me this:
As you can see, when I hit cancel at ~0.5 point, animation gets cancelled more quickly (so it doesn't go all the way to the 0.7 of the distance as in previous example). It gives a bit better feeling of real object. Higher factor - faster your animation will stop.
Here is an updated interpolator:
static class MyInterpolator extends AccelerateDecelerateInterpolator {
......
private float factor = 1.5f;
.......
private float getCancellationInterpolation(float input)
{
if(factor == 1)
{
return (1.0f - (1.0f - input) * (1.0f - input));
}
else
{
return (float)(1.0f - Math.pow((1.0f - input), 2 * factor));
}
}
}
You run your animation all the way through and you implement your TimeInterpolator that "slows down" after accelerating.
You need to implement the method getInterpolation(float): this represents a mathematical function that maps time instants to your position values x normalised between 0 and 1. For instance, if you wanna run from x = 0 to x = 120, the value x = 100 corresponds to the normalised value
100/|120 - 0| = 0.83
Finding the right function requires some mathematical sophistication and some guessing, but this tutorial should get you started.
I think probably I have the wrong idea here - can someone set me straight?
I have a label which when clicked causes a panel to appear - i did this with panel.visible = true - in fact it is pretty clunky though and I would love to have it slide open.
So I used a for-next loop to change the height of the panel dynamically, which I tried to slow up with a timer. But I'm doing something wrong:
Sub button_click
For i = 1 to 500
counter = i
timer1.initialize("timer1", 50)
timer1.enabled = true
next
End sub
sub timer1_click
panel.height = counter
timer1.enabled=false
end sub
This has the effect of a long delay and then the panel appears. Not quite what I was after. Does simply stating panel.height = xx cause the panel to be redrawn or do I have to use animation?
thanks....
You are initializing the timer 500 times by having it in the loop. This may cause the timer to excute immediately for 500 times instead of for the interval set. This, in turn, would not allow time for the panel to be redrawn. Even if it the code were right, an interval of 50 is 5/100ths of a second repeated 500 times is 25 seconds. That's a long time to sit and watch a panel go up.
However, even if you reduced the interval to 1, it could take about the same amount of time just to redraw the panel 500 times, depending on the device and the number of views on the panel. This means that you have to move more than 1 pixel at a time. To get the moving time down to a reasonable number of seconds, you could use an interval of 1 and move 5 pixels at a time, which works, but the movement is not very smooth. Also, the speed of movement can vary quite a bit on different devices, say from a 4" phone to a 10" tablet.
Sub Activity_Create
timer1.initialize("timer1", 1)
timer1.enabled = false
end sub
Sub button_click
counter1 = 0 ' counter1 should be DIMed in Sub Globals
timer1.enabled = true
end sub
Sub timer1_tick ' Note: not "time1_click"
counter1 = counter1 + 5
panel.Height = counter1
if counter1 = 500 then
timer1.enabled = false
end if
End Sub
You should really use an animation. You have a good example here :
Android Left to Right slide animation
You need to use Animation.
Below is the code which is used for sliding up/down animation
btn.setOnClickListener(new OnClickListener()
{
public void onClick(View arg0) {
isOpen=!isOpen;
if(isOpen)
{ //
lin1.getLayoutParams().height=actualHeight;
btn.setBackgroundResource(R.drawable.header_uparrow);
}
else
btn.setBackgroundResource(R.drawable.header_downarrow);
ani a=new ani();
a.setDuration(2000);
lin1.startAnimation(a);
}
//}
});
class ani extends Animation
{
public ani()
{
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newHeight;
if(isOpen)
newHeight = (int)(initialHeight * interpolatedTime);
else
newHeight = (int)(initialHeight * (1-interpolatedTime));
lin1.getLayoutParams().height = newHeight;
lin1.requestLayout();
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
initialHeight = actualHeight;
}
#Override
public boolean willChangeBounds() {
return true;
}
};