Sprite Rotation in Android using Canvas.DrawBitmap

2019-02-17 01:25发布

问题:

I have this sprite rotating algorithm (its poorly named and just used for testing). It is so close, sprites drawn with it do rotate. Everyframe I can add +5 degrees to it and see my nice little sprite rotate around. The problem is, the other stuff drawn to the canvas now flickers. If I don't do the rotation the regular drawn sprites work great. I think I am close but I just don't know what piece I am missing. Below is my two "Draw_Sprite" methods, one just draws the previously resource loaded bitmap to the canvas passed in. The other one, does some rotation the best I know how to rotate the sprite by so x many degrees..and then draw it. If I have a nice game loop that draws several objects, one type is the rotated kind. Then the non-rotated sprites flicker and yet the rotated sprite never does. Though if I draw the non-rotated sprites first, all is well, but then the Z-Ordering could be messed up (sprites on top of UI elements etc)... The method definitions:

    /*************************************************
 * rotated sprite, ignore the whatever, its for ease of use and testing to have this argument list
 * @param c canvas to draw on.
 * @param whatever ignore 
 * @param rot degrees to rotate
 * @return
 */
public int Draw_Sprite(Canvas c, int whatever, int rot) {   
    //rotating sprite
    Rect src = new Rect(0, 0, width, height);
    Rect dst = new Rect(x, y, x + width, y + height);
    Matrix orig = c.getMatrix();        
    mMatrix = orig;
    orig.setTranslate(0, 0);
    orig.postRotate(rot, x+width/2, y+height/2); 
    c.setMatrix(orig);
    c.drawBitmap(images[curr_frame], src, dst, null);
    c.setMatrix(mMatrix); //set it back so all things afterwards are displayed correctly.

    isScaled=false;
    return 1;

}

/********************************************************
 * draw a regular sprite to canvas c
 * @param c
 * @return
 */
public int Draw_Sprite(Canvas c) {  

    Rect src = new Rect(0, 0, width, height);
    Rect dst = new Rect(x, y, x + width, y + height);       
    c.drawBitmap(images[curr_frame], src, dst, null);

    isScaled=false;
    return 1;
}

And now the usage:

void onDraw(Canvas c)
{
    canvas.drawRect( bgRect, bgPaint); //draw the background

    //draw all game objects
      // draw the normal items
    for (GameEntity graphic : _graphics) { 
        graphic.toScreenCoords((int)player_x, (int)player_y);
        if(graphic.getType().equals("planet")) //draw planets
            graphic.Draw_Sprite(canvas); //before the rotation call draws fine
        else
        {
            //rotate all space ships every frame so i see them spinning        
            //test rotation
            mRot +=5;
            if(mRot>=360)
                mRot=0;
            graphic.Draw_Sprite(canvas, 0, mRot); //yes function name will be better in future.  this rotates spins draws fine


        }                           
       }

       thePlayer.Draw_Sprite(canvas); //FLICKERS
       drawUI(canvas);//all things here flickr

}  

So it does do it, things after a call to a rotational draw are drawn correctly. But the problem is it flickrs. Now One could say I should just do all my non rotational stuff and save that last, but the zordering would be off.... suggestions as to how to tackle this issue of zordering or the flickering?

回答1:

Try using canvas.save() before the rotation and canvas.restore() after manipulation is complete.

When performing manipulations on the canvas in order to change the way an object is drawn you have to remember the manipulations set how the canvas handles origins etc... So if you translate or rotate the canvas, that will be set for the lifetime of that canvas. In order to avoid this you first call save, which saves a snapshot of the canvas matrix before you manipulate it, then you run all your changes, then call restore which will restore the canvas back to the last saved point. Otherwise all your changes build up and you get unintended results.



回答2:

Just for the next guy who may read this. You can do this with only a few lines of code:

canvas.save();
canvas.rotate(rotation_angle, x + (widthofimage / 2), y + (heightofimage / 2));
canvas.drawBitmap(bitmap, x, y, null);
canvas.restore();