How would I animate a bitmap in android so that it moves across the screen in a parabolic arch or any other curved path? Currently, the method I'm using is to use the onDraw() method to draw a bitmap to the canvas with an x/y coordinate and then increasing that x/y coordinate by one after the bitmap has been drawn, at which point the method calls invalidate() to redraw the bitmap with the new position.
Update:
Maybe this will give a bit more context to what i'm trying to do. Below is the implementation I have right now for animating my bitmap:
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(gBall, x, y, null);
x += changeX;
y += changeY;
if(x >= (canvas.getWidth()-gBall.getWidth()) || x <= 0)
changeX = -changeX;
if(y >= (canvas.getHeight()-gBall.getHeight()) || y <= 0)
changeY = -changeY;
invalidate();
Is there a way while still using this implementation to make the bitmap gBall curves as it approaches the edge of the screen?
Use a Handler to controle the speed :
public void draw(Canvas canvas, ...){
if (System.currentTimeMillis() - lastCall < PERIOD-50){
mHandler.postDelayed(mReDrawRunnable,PERIOD);
return;
}
//to call back
mHandler.removeCallbacks(mReDrawRunnable);
mHandler.postDelayed(mReDrawRunnable,PERIOD);
lastCall = System.currentTimeMillis();
//your code here
...
}
private long lastCall = 0;
private static final PERIOD = 250; //millis
private Handler mHandler = new Handler();
private Runnable mReDrawRunnable==new Runnable() {
public void run() {YourClass.this.invalidate();}
};
This is a quick way to do it, it should work. You should create an other thread to control the drawing.
Implement a custom Animator. To implement a custom animator, all you have to do is overide the applyTransformation method of the Animator class. You can then call View.startAnimation with an instance of your custom class. Given the lengths that google developers have gone to to implement smooth animations, this is likely to be the best solution -- much better than performing actions off a handler, which is likely to cause glitches due to garbage collects. A properly implmement Animation, that performs no memory allocations in its applyTransform method can run without incurring any garbage collects at all.
If you look at platform sources, it quickly becomes apparent that glitch-free animations were a primary development goal in Android 4.x. Google engineers have put a lot of work in to making sure that Animations run without glitches. Your draw-and-invalidate strategy may actually work plausibly well. The Handler approach not so much. But if it were me, I'd take the extra time to leverage the effort that has been put into Animations.
Related
I'm developing a simple game which uses normal android views, not openGL or other apis, simply uses views and moves them on the scren. I have a game loop which calls to AsteroidManager.updateAsteroidsPositions() which iterates in all the screen asteroids calculating it's possitions.
After that, the thread, calls to a AsteroidManager.invalidateAsteroids() method using runOnUiThread() method, because in Android you need to manipulate views on the main thread. AsteroidManager.invalidateAsteroids() method simply iterates all the asteroids and set's x,y positions to the view and calls invalidate().
The problem is that I disscovered that it gives a much more smooth and faster behaviour if you put the logic of calculatePositions inside the onDraw method of the view. Doing that, the logic of calculating possitions is not being done in the game loop thread... its being done in the main UI thread!!
How is that possible? It is breaking all the game development logic... about doing the position calculations on Game Loop thread instead of other places like main thread or onDraws...
This the slower original code:
AsteroidManager class:
public void updateAsteroidsPositions(){
for (int i = 0; i<onScreenAsteroids.size(); i++){
onScreenAsteroids.get(i).updatePosition();
}
}
public void invalidateAsteroids() {
for (int i = 0; i<onScreenAsteroids.size(); i++){
onScreenAsteroids.get(i).invalidate();
}
}
Asteroid Class:
public void updatePosition(){
currentScale = (Float) scaleX.getAnimatedValue();
factor = currentScale/MAX_SCALE;
//adding a minimum of factor, because with too low factor the movement is not realistic
if (factor < 0.250f)
factor = 0.250f;
x = x-((float)GameState.getInstance().getJoyX()*factor);
y = y-((float)GameState.getInstance().getJoyY()*factor);
}
public void invalidate(){
view.setX(x);
view.setY(y);
view.invalidate();
}
this is the trick done in Asteroid class which does the behaviour of the game smooth and faster:
Asteroid Class:
public Asteroid(Bitmap bitmap, Context context) {
view = new ImageView(context){
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentScale = (Float) scaleX.getAnimatedValue();
factor = currentScale/MAX_SCALE;
//adding a minimum of factor, because with too low factor the movement is not realistic
if (factor < 0.250f)
factor = 0.250f;
x = x-((float)GameState.getInstance().getJoyX()*factor);
y = y-((float)GameState.getInstance().getJoyY()*factor);
view.setX(x);
view.setY(y);
}
};
view.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
view.setImageBitmap(bitmap);
}
public void updatePosition(){
}
public void invalidate(){
view.invalidate();
}
If you have too many items in onScreenAsteroids list it takes some time to execute updatePosition() for each of them. Try to use single method for them:
public void updateAndInvalidateAsteroidsPositions(){
for (int i = 0; i<onScreenAsteroids.size(); i++){
onScreenAsteroids.get(i).updatePosition();
onScreenAsteroids.get(i).invalidate();
}
}
Not all games need game loop. Thread switching has its own cost.
Game Loop separates game state from rendering. Ideally the game loop has the responsibility to processes all the onscreen objects in the game and objects have the responsibility to draw itself in its place. This way we have central place to react to events(mouse click, user touch etc) and update view positions and views have the responsibility to draw themselves at updated position.
For eg consider that we have 10 moving asteroids on screen and we are updating them in onDraw(), now two of them collide, but asteroid1 does not know position of asteroid2, so how will they detect collision? By game logic the game loop knows position of all 10 asteroids, it can detect collision. If don't care about messy code, then collision can be detected in onDraw also. But consider following...
If two are colliding , then we need to check if some other asteroid is near by collision region, if so then how much impact? Mess increases linearly...
After collision we decide to show collision graphic effects. Mess increases exponentially....
Asteroids collided, game state = 'EXPLOSIONS IN THE SKY', user gets a call, game goes to background, game state is to be saved, but our asteroids are master of their own destiny, now we need to provide every asteroid's state to our activity and save it in onPause(). Its all mess now...
User returns after few hours, we can't welcome them directly with 'EXPLOSIONS IN THE SKY', need to rewind to the state where asteroids are about to collide and then show BANG-BANG... Mess goes ALL HELL BREAK LOOSE.....
Views are slaves and they should not be empowered.
Where to display view, its dimens? -> comes from outside.
What to draw in view? -> comes from outside/ can have little say here.
How to animate view? -> comes from outside.
Coming to your particular case, you are using both versions differently, in onDraw() case you are directly invalidating asteroid's (first one is drawn instantly) whereas in game loop case you are first computing all asteroid's position and then invalidating, I don't know how many asteroids you have but if they are significant number, then this coupled with thread switching costs, may trick you to believe onDraw() is faster.
So I'm trying to understand how I can properly use hardware acceleration (when available) in a custom View that is persistently animating. This is the basic premise of my onDraw():
canvas.drawColor(mBackgroundColor);
for (Layer layer : mLayers) {
canvas.save();
canvas.translate(layer.x, layer.y);
//Draw that number of images in a grid, offset by -1
for (int i = -1; i < layer.xCount - 1; i++) {
for (int j = -1; j < layer.yCount - 1; j++) {
canvas.drawBitmap(layer.bitmap, layer.w * i, layer.h * j, null);
}
}
//If the layer's x has moved past its width, reset back to a seamless position
layer.x += ((difference * layer.xSpeed) / 1000f);
float xOverlap = layer.x % layer.w;
if (xOverlap > 0) {
layer.x = xOverlap;
}
//If the layer's y has moved past its height, reset back to a seamless position
layer.y += ((difference * layer.ySpeed) / 1000f);
float yOverlap = layer.y % layer.h;
if (yOverlap > 0) {
layer.y = yOverlap;
}
canvas.restore();
}
//Redraw the view
ViewCompat.postInvalidateOnAnimation(this);
I'm enabling hardware layers in onAttachedToWindow() and disabling them in onDetachedFromWindow(), but I'm trying to understand whether or not I'm actually using it. Essentially, the i/j loop that calls drawBitmap() never changes; the only thing that changes is the Canvas translation. Is the Bitmap automatically saved to the GPU as a texture behind the scenes, or is there something I need to do manually to do so?
On what view(s) are you setting View.LAYER_TYPE_HARDWARE exactly? If you are setting a hardware layer on the view that contains the drawing code shown above, you are causing the system to do a lot more work than necessary. Since you are only drawing bitmaps you don't need to do anything here. If you call Canvas.drawBitmap() the framework will cache the resulting OpenGL texture on your behalf.
You could however optimize your code a little more. Instead of calling drawBitmap(), you could use child views. If you move these children using the offset*() methods (or setX()/setY()) the framework will apply further optimizations to avoid calling the draw() methods again.
In general, hardware layers should be set on views that are expensive to draw and whose content won't change often (so pretty much the opposite of what you're doing :)
You can use Android's Tracer for OpenGL ES to see if your view issue OpenGL commands.
From developer.android.com
Tracer is a tool for analyzing OpenGL for Embedded Systems (ES) code in your Android application. The tool allows you to capture OpenGL ES commands and frame by frame images to help you understand how your graphics commands are being executed.
There is also a tutorial about Android Performance Study by Romain Guy which describes its use almost step by step.
I'm a completely newbie to android programming, having done some java for my computing levels but nothing too complex!
I'm working on a game where an object falls down the screen and has to be sorted into the relevant 'box' when it reaches the bottom.
I've got a surface view running with a thread etc, using canvas draw methods, however, i can't for the life of me see how i will be able to make the falling object reach a speed where it'll present a challenge to the user.
Running the thread with a change of 1 in the y direction causes the object to crawl down the screen. Greater changes in Y lead to jumpy graphics.
Would OpenGL make any difference or are there other canvas methods i can implement?
Hope that makes sense!
Thanks in advance
----Thread------
public void run()
{
Canvas canvas;
while(running)
{
canvas = null;
try{
canvas = this.surfaceholder.lockCanvas();
synchronized(surfaceholder)
{
gamepanel.Check();
this.gamepanel.onDraw(canvas);
}
}finally
{
if(canvas != null)
{
surfaceholder.unlockCanvasAndPost(canvas);
}
}
}
}
----SurfaceView-------
protected void onDraw(Canvas canvas){
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.gamebackground), 0, 0, null);
SortItOut.sortitout.Letter.draw(canvas);
}
-----Letter----- (Each object is a different letter)
public static void draw(Canvas canvas)
{
y += 1;
canvas.drawBitmap(LetterObject, x, y, null);
}
Those are the methods i would believe are relevant (The Check method is simply to check whether the object has reached the bottom of the screen).
You must load all your bitmaps in the constructor for the SurfaceView, never in onDraw()
Aside from the bitmap loading problem, you can make it fall faster my increasing the rate of the y change. If you do it too much, the box will appear to jump, but I bet you could get away with up to 10 pixel changes before that would happen (experiment).
You would only need to do OpenGL in this case if performance was slowing you down. I don't think that's the case. Although, I would stop loading the bitmap in the onDraw method and put it in the onCreate or some constructor. onDraw gets called hundreds of times and that's killing your app.
Ok guys, this may sound dumb, but I have been banging my head against the keyboard for some time now trying to figure out why this will not refresh. the basics: I have a little sample app that i am testing to see if i can rotate an image around a point a X amount of degrees, and show it one degree at a time to make a smooth animation. So I have a great sample i found that works great with a slider bar, basically setting the images rotation to a point on the slider bar, great! but.... when i try and create a for loop with a random number and use my for variable updating the image along the way every degree... it does nothing... and all i get is the updated image at the end... but when i drag my finger on he slider bar the graphic is updated instant as me spinning it... I cant figure out what i am doing wrong here... here is the code with the slider... i don't have my piece that creates the random number and draws it but essentially i did it behind a button click
essentially if you look at this piece i did the same behind a button again but it doesnt do it "real time". i called view.invalidate() and view.postinvalidate() to try to force it but no go...
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
curRotate = (float)progress;
drawMatrix();
}
private void drawMatrix(){
Matrix matrix = new Matrix();
matrix.postScale(curScale, curScale);
matrix.postRotate(curRotate);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bmpWidth, bmpHeight, matrix, true);
myImageView.setImageBitmap(resizedBitmap);
}
I think what you did was something like:
for (int degrees = 0 ; degrees < maxRotation ; i++) {
// perform the rotation by matrix
myImageView.invalidate();
}
This wouldn't work because invalidate() only schedules a redraw in the main thread event queue. This means that the redraw will be performed only when the current code has all been executed (in this case, the for cycle).
For a simple rotation a Tween Animation would be better suited. For more advanced stuff (like game animations) you might need to create a custom view or use SurfaceView.
Sounds like you're blocking the UI thread with your code to rotate the image.
I don't have any code to show you right now (reply back and when I'm home tonight I can post something that should help), but yuo will probably get better results placing your rotate code in an AsyncTask, see the Painless Threading area of the dev site for more info.
I was having the same problem, i used:
runOnUiThread(new Runnable() {
public void run() {
myImageView.setImageBitmap(image);
imageView.invalidate();
}
});
I am trying to perform basic task of rotating a canvas 20 times a
second using timer but it doesn't seem to be working properly and its
lagging. for example, if I rotate rectangle 0.3 degrees per 50 ms it
should rotate 6 degree in on second, but that is not the case. It
really slow in rotation. Here is my sample code:
//Code for update task
class UpdateTimeTask extends TimerTask {
public void run() {
hndView.post(new Runnable() {
public void run() {
hndView.invalidate(); //this code invalidates custom view that calls onDraw to draw rotated hand
}
});
}
}
//Code for onDraw method of custom view that needs to be update
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//ang is angle to rotate and inc is float value of 0.3 degree to be incremented
ang = ang + inc;
if (ang >= 360) ang = ang - 360;
canvas.rotate(ang, canvas.getWidth()/2, canvas.getHeight()/2);
canvas.drawRect((canvas.getWidth()/2 - 2), (canvas.getHeight()/2 - 125), (canvas.getWidth()/2 + 2), (canvas.getHeight()/2 + 10), mTextPaint);
canvas.restore();
}
//code to schedule task
Timer timer = new Timer();
UpdateTimeTask tt = new UpdateTimeTask();
timer.schedule(tt, 0, 50);
Can anyone please tell me what am I doing wrong here? Should I used
different approach to perform this task? Because its hard to believe
that you cannot have simple smooth rotation of rectangle 20 times in
one second.
Timer/TimerTask are not supposed to be precise and are not supposed to be used for this purpose. Follow the recipes for 2D game development, such as the LunarLander sample that is included in your SDK. Or, you could just search StackOverflow and find all sorts of useful posts on the subject.
I believe you are not using a SurfaceView.
Drawing like that to a canvas was meant for controls and not fast rendering(read >10fps)
If you want performance you need either to use a SurfaceView where you'll average a 25-30 fps or opengl
Please read : http://developer.android.com/guide/topics/graphics/index.html
The number of calls to invalidate need not equal the number of calls to onDraw. If your timer ends up running twice successively before the UI thread gets a chance to run, then the double invalidate will end up only causing a single rotation. Consider putting in debug code to validate the number of calls to those two methods.