6–Sprite Animation with Android

來源:互聯網
上載者:User

If you followed the series so far we are pretty knowledgable in handling touches, displaying images and moving them around.

目前為止,我們已經知道了處理觸摸時間,畫圖,移動他們

But a moving image it’s a pretty dull sight as it looks really fake and amateurish. To give the characters some life we will need to do more than that. That is what animation is all about. A rock is an inanimate object and even if it is thrown, it doesn’t
change its shape. A human on the other hand, is very animated. Try throwing one and you’ll see twitching limbs and even screams in the air.

但是移動的圖片看起來太傻了,為了給角色新的生命力,我們需要做的更多。那就是動畫的功用。一塊石頭是沒有動作的物體,即使它被扔出去,也不會改變形狀,相對來說,人是會動的,扔出去一個人,他胳膊會動,甚至會叫喊。

Let’s just resort to examining walking which is pretty complex in its own. Imagine a human crossing your way (just in 2D). You’ll notice different displays of the body. Left foot in front, right hand in front while the oposite limbs are behind. This slowly
changes so the left foot remains behind while the right progresses along with the body. But at one point the cycle repeats. If you don’t close your eyes you see the person progressing smoothly. If you close your eyes and keep them closed for a bit and open
them again the person already went on and is in a different position. Try blinking quite rapidly and you will see something like the old black and white comedies. That is low frame rate. More on FPS
here.

 

Actually we do want a low frame rate walking for this tutorial, just like this.



上面的角色看起來有點狡詐,它是從monkey island裡面找出來的,她是elaine marley
這個被叫做精靈,就是一個簡單的二維圖形或者動畫

為了做上面的動作,我們需要走路的每一幀

這是一個150像素寬的圖形,每一幀是30像素

看,它解釋的更好一些

為了顯示上面的動畫,我們需要把每一幀載入成獨立的圖形,然後以有規律的間隔來顯示他們。或者我們可以把整個圖片載入進來,然後用android提供的切割方法,只是顯示對應的幀
這樣做有點麻煩,我們知道我們有5幀,每一幀是30個像素,我們定義一個矩形,他的寬度是一幀,高度是圖形的高度。

顯示了我是如何切割前兩幀的,其餘您自己完成吧

下面開始我們的工程吧,我們需要前面幾章的知識,主要是game loop和圖形顯示的的內容。

我們需要一個對象來做動作,我們用elaine,所以我建立了ElaineAnimated.java的類

01 public class ElaineAnimated {  02    03     private static final String TAG = ElaineAnimated.class.getSimpleName();  04    05     private Bitmap bitmap;      // the animation sequence  06     private Rect sourceRect;    // the rectangle to be drawn from the animation bitmap  07     private int frameNr;        // number of frames in animation  08     private int currentFrame;   // the current frame  09     private long frameTicker;   // the time of the last frame update  10     private int framePeriod;    // milliseconds between each frame (1000/fps)  11    12     private int spriteWidth;    // the width of the sprite to calculate the cut out rectangle  13     private int spriteHeight;   // the height of the sprite  14    15     private int x;              // the X coordinate of the object (top left of the image)  16     private int y;              // the Y coordinate of the object (top left of the image)  17    18 } 

提倡私人的屬性,按時有些需要注意的

  • bitmap是包含所有幀的圖形,本文中的第二個圖片
  • sourceRect是所選的矩形,是下面圖片中的藍色小視窗,矩形一次移動一幀
  • frameticker 是行走序列中最近的幀變換的java時間戳記,注意這個不是遊戲的FPS,是行走的FPS,如果我們要elaine在一秒走完,需要5幀,因為我們的圖形有5幀,為了得到一個平滑的動畫,我們需要30幀,但是這不是重點
  • frameperiod代表了一幀需要顯示的時間,如果迴圈在一秒完成,那麼這個值是0.2秒,每一幀顯示了0.2秒

建構函式如下

01 public ElaineAnimated(Bitmap bitmap, int x, int y, int width, int height, int fps, int frameCount) {  02         this.bitmap = bitmap;  03         this.x = x;  04         this.y = y;  05         currentFrame = 0;  06         frameNr = frameCount;  07         spriteWidth = bitmap.getWidth() / frameCount;  08         spriteHeight = bitmap.getHeight();  09         sourceRect = new Rect(0, 0, spriteWidth, spriteHeight);  10         framePeriod = 1000 / fps;  11         frameTicker = 0l;  12     } 

 

I’m assuming that the frames are the same width so I calculate the width of the rectangle by dividing the width of the image with the number of frames. I also pass in the
fps which is again, the frames per second of the walk cycle not the game FPS.

 

Elaine will have an update method of her own as she is an animated object and she needs to look good and she is in charge of dragging her feet. Because the period of the game update cycle and Elaine’s one might be (in this case is) different
we pass the actual game time as a variable so we know when we need to display the next frame.
For example the game runs very fast and the update is called every 20 milliseconds and we need to update the frame every 200ms, then the progression of the frame will happen at every 10th game update.

Here’s the code:

01 public void update(long gameTime) {  02     if (gameTime > frameTicker + framePeriod) {  03         frameTicker = gameTime;  04         // increment the frame  05         currentFrame++;  06         if (currentFrame >= frameNr) {  07             currentFrame = 0;  08         }  09     }  10     // define the rectangle to cut out sprite  11     this.sourceRect.left = currentFrame * spriteWidth;  12     this.sourceRect.right = this.sourceRect.left + spriteWidth;  13 } 

The update is called from the main game panel (check previous entries how that works). This is the update method of the
MainGamePanel class.

1 public void update() {  2     elaine.update(System.currentTimeMillis());  3 } 

 

The update method is simple (Elaine’s). It increments the frame if the passed in time (which is the system time when the update method was called) is greater than the last time (frameTicker) the frame was updated plus the period
of the next update.
If the next frame is beyond the last, we reset the cycle.

After all that area from which the image will be cut out is defined as the
sourceRect
.
That’s it. Now let’s go on to display it.

1 public void draw(Canvas canvas) {  2         // where to draw the sprite  3         Rect destRect = new Rect(getX(), getY(), getX() + spriteWidth, getY() + spriteHeight);  4         canvas.drawBitmap(bitmap, sourceRect, destRect, null);  5     } 

at is all. We set the destination rectangle as to where to draw the cut out image. It is at Elaine’s position (X and Y set in the constructor).

 
1 canvas.drawBitmap(bitmap, sourceRect, destRect, null); 

tells android to cut out the image defined by sourceRect from the image contained in
bitmap and draw it into the rectangle on the canvas defined by
destRect
.

The draw is called from the game panel’s render method triggered by the game loop (check previous entries).

The MainGamePanel.java differs slightly from the one from previous chapters. I got rid of all the droidz and added just Elaine.

01 private ElaineAnimated elaine;  02    03     public MainGamePanel(Context context) {  04         //* ... removed ... */  05    06         // create Elaine and load bitmap  07         elaine = new ElaineAnimated(  08                 BitmapFactory.decodeResource(getResources(), R.drawable.walk_elaine)  09                 , 10, 50    // initial position  10                 , 30, 47    // width and height of sprite  11                 , 5, 5);    // FPS and number of frames in the animation  12    13         // create the game loop thread  14         thread = new MainThread(getHolder(), this);  15    16         //* ... removed ... */  17     } 

 

Elaine is instantiated in the panel’s constructor and is given an initial positon of (X=10, Y=50). I pass in the width and the height of the sprite too but that is ignored anyway, but you can modify the code.
The FPS is very important and the number of frames too. FPS says how many frames are to be shown in one second. The last parameter is the number of frames in the cycle.

The thread and activity classes haven’t changed at all. You can find them in the download as they are quite long to be pasted. The image is named
walk_elaine.png and it was copied to /res/drawable-mdpi/ so android can pick it up automatically.

If you run the application you should be seeing Elaine performing walking cycles in one place. We should have used jumping as that can be performed in one place but you get the idea.

 

Elaine Walking

 

Enhancement

To make some neat additions modify Elaine’s draw method so it displays the original image containing the sprites from which the frames are extracted.

1 public void draw(Canvas canvas) {  2     // where to draw the sprite  3     Rect destRect = new Rect(getX(), getY(), getX() + spriteWidth, getY() + spriteHeight);  4     canvas.drawBitmap(bitmap, sourceRect, destRect, null);  5     canvas.drawBitmap(bitmap, 20, 150, null);  6     Paint paint = new Paint();  7     paint.setARGB(50, 0, 255, 0);  8     canvas.drawRect(20 + (currentFrame * destRect.width()), 150, 20 + (currentFrame * destRect.width()) + destRect.width(), 150 + destRect.height(),  paint);  9 } 

 

This just displays the image at (20, 150) and creates a new paint object so we can paint over the current frame on the original image.
The method setARGB creates a semi-transparent green paint. The first value is
50 which means it’s 75% transparent. 0 is completely transparentwhile
255 is fully opaque.
After everything was drawn we paint a rectangle of the size of a frame onto the original image so you see which frame is being displayed in the motion.

 

Walking with Current Frame Painted

 

That’s it. Run it and you have your first sprite animation.

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.